1 /* $OpenBSD: test_fuzz.c,v 1.14 2024/01/11 01:45:58 djm Exp $ */
2 /*
3 * Fuzz tests for key parsing
4 *
5 * Placed in the public domain
6 */
7
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <stdint.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16
17 #include <openssl/bn.h>
18 #include <openssl/ec.h>
19 #include <openssl/rsa.h>
20 #include <openssl/dsa.h>
21 #include <openssl/objects.h>
22
23 #include "test_helper.h"
24
25 #include "ssherr.h"
26 #include "authfile.h"
27 #include "sshkey.h"
28 #include "sshbuf.h"
29
30 #include "common.h"
31
32 void sshkey_fuzz_tests(void);
33
34 static void
onerror(void * fuzz)35 onerror(void *fuzz)
36 {
37 fprintf(stderr, "Failed during fuzz:\n");
38 fuzz_dump((struct fuzz *)fuzz);
39 }
40
41 static void
public_fuzz(struct sshkey * k)42 public_fuzz(struct sshkey *k)
43 {
44 struct sshkey *k1;
45 struct sshbuf *buf;
46 struct fuzz *fuzz;
47 u_int fuzzers = FUZZ_1_BIT_FLIP | FUZZ_1_BYTE_FLIP |
48 FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END;
49
50 if (test_is_fast())
51 fuzzers &= ~FUZZ_1_BIT_FLIP;
52 if (test_is_slow())
53 fuzzers |= FUZZ_2_BIT_FLIP | FUZZ_2_BYTE_FLIP;
54 ASSERT_PTR_NE(buf = sshbuf_new(), NULL);
55 ASSERT_INT_EQ(sshkey_putb(k, buf), 0);
56 fuzz = fuzz_begin(fuzzers, sshbuf_mutable_ptr(buf), sshbuf_len(buf));
57 ASSERT_INT_EQ(sshkey_from_blob(sshbuf_ptr(buf), sshbuf_len(buf),
58 &k1), 0);
59 sshkey_free(k1);
60 sshbuf_free(buf);
61 TEST_ONERROR(onerror, fuzz);
62 for(; !fuzz_done(fuzz); fuzz_next(fuzz)) {
63 if (sshkey_from_blob(fuzz_ptr(fuzz), fuzz_len(fuzz), &k1) == 0)
64 sshkey_free(k1);
65 }
66 fuzz_cleanup(fuzz);
67 }
68
69 static void
sig_fuzz(struct sshkey * k,const char * sig_alg)70 sig_fuzz(struct sshkey *k, const char *sig_alg)
71 {
72 struct fuzz *fuzz;
73 u_char *sig, c[] = "some junk to be signed";
74 size_t l;
75 u_int fuzzers = FUZZ_1_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP |
76 FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END;
77
78 if (test_is_fast())
79 fuzzers &= ~FUZZ_2_BYTE_FLIP;
80 if (test_is_slow())
81 fuzzers |= FUZZ_2_BIT_FLIP;
82
83 ASSERT_INT_EQ(sshkey_sign(k, &sig, &l, c, sizeof(c),
84 sig_alg, NULL, NULL, 0), 0);
85 ASSERT_SIZE_T_GT(l, 0);
86 fuzz = fuzz_begin(fuzzers, sig, l);
87 ASSERT_INT_EQ(sshkey_verify(k, sig, l, c, sizeof(c), NULL, 0, NULL), 0);
88 free(sig);
89 TEST_ONERROR(onerror, fuzz);
90 for(; !fuzz_done(fuzz); fuzz_next(fuzz)) {
91 /* Ensure 1-bit difference at least */
92 if (fuzz_matches_original(fuzz))
93 continue;
94 ASSERT_INT_NE(sshkey_verify(k, fuzz_ptr(fuzz), fuzz_len(fuzz),
95 c, sizeof(c), NULL, 0, NULL), 0);
96 }
97 fuzz_cleanup(fuzz);
98 }
99
100 #define NUM_FAST_BASE64_TESTS 1024
101
102 void
sshkey_fuzz_tests(void)103 sshkey_fuzz_tests(void)
104 {
105 struct sshkey *k1;
106 struct sshbuf *buf, *fuzzed;
107 struct fuzz *fuzz;
108 int r, i;
109
110
111 TEST_START("fuzz RSA private");
112 buf = load_file("rsa_1");
113 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
114 sshbuf_len(buf));
115 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
116 sshkey_free(k1);
117 sshbuf_free(buf);
118 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
119 TEST_ONERROR(onerror, fuzz);
120 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
121 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
122 ASSERT_INT_EQ(r, 0);
123 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
124 sshkey_free(k1);
125 sshbuf_reset(fuzzed);
126 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
127 break;
128 }
129 sshbuf_free(fuzzed);
130 fuzz_cleanup(fuzz);
131 TEST_DONE();
132
133 TEST_START("fuzz RSA new-format private");
134 buf = load_file("rsa_n");
135 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
136 sshbuf_len(buf));
137 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
138 sshkey_free(k1);
139 sshbuf_free(buf);
140 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
141 TEST_ONERROR(onerror, fuzz);
142 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
143 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
144 ASSERT_INT_EQ(r, 0);
145 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
146 sshkey_free(k1);
147 sshbuf_reset(fuzzed);
148 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
149 break;
150 }
151 sshbuf_free(fuzzed);
152 fuzz_cleanup(fuzz);
153 TEST_DONE();
154
155 #ifdef WITH_DSA
156 TEST_START("fuzz DSA private");
157 buf = load_file("dsa_1");
158 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
159 sshbuf_len(buf));
160 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
161 sshkey_free(k1);
162 sshbuf_free(buf);
163 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
164 TEST_ONERROR(onerror, fuzz);
165 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
166 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
167 ASSERT_INT_EQ(r, 0);
168 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
169 sshkey_free(k1);
170 sshbuf_reset(fuzzed);
171 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
172 break;
173 }
174 sshbuf_free(fuzzed);
175 fuzz_cleanup(fuzz);
176 TEST_DONE();
177
178 TEST_START("fuzz DSA new-format private");
179 buf = load_file("dsa_n");
180 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
181 sshbuf_len(buf));
182 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
183 sshkey_free(k1);
184 sshbuf_free(buf);
185 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
186 TEST_ONERROR(onerror, fuzz);
187 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
188 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
189 ASSERT_INT_EQ(r, 0);
190 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
191 sshkey_free(k1);
192 sshbuf_reset(fuzzed);
193 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
194 break;
195 }
196 sshbuf_free(fuzzed);
197 fuzz_cleanup(fuzz);
198 TEST_DONE();
199 #endif
200
201 TEST_START("fuzz ECDSA private");
202 buf = load_file("ecdsa_1");
203 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
204 sshbuf_len(buf));
205 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
206 sshkey_free(k1);
207 sshbuf_free(buf);
208 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
209 TEST_ONERROR(onerror, fuzz);
210 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
211 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
212 ASSERT_INT_EQ(r, 0);
213 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
214 sshkey_free(k1);
215 sshbuf_reset(fuzzed);
216 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
217 break;
218 }
219 sshbuf_free(fuzzed);
220 fuzz_cleanup(fuzz);
221 TEST_DONE();
222
223 TEST_START("fuzz ECDSA new-format private");
224 buf = load_file("ecdsa_n");
225 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
226 sshbuf_len(buf));
227 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
228 sshkey_free(k1);
229 sshbuf_free(buf);
230 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
231 TEST_ONERROR(onerror, fuzz);
232 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
233 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
234 ASSERT_INT_EQ(r, 0);
235 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
236 sshkey_free(k1);
237 sshbuf_reset(fuzzed);
238 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
239 break;
240 }
241 sshbuf_free(fuzzed);
242 fuzz_cleanup(fuzz);
243 TEST_DONE();
244
245 TEST_START("fuzz Ed25519 private");
246 buf = load_file("ed25519_1");
247 fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
248 sshbuf_len(buf));
249 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
250 sshkey_free(k1);
251 sshbuf_free(buf);
252 ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
253 TEST_ONERROR(onerror, fuzz);
254 for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
255 r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
256 ASSERT_INT_EQ(r, 0);
257 if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
258 sshkey_free(k1);
259 sshbuf_reset(fuzzed);
260 if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
261 break;
262 }
263 sshbuf_free(fuzzed);
264 fuzz_cleanup(fuzz);
265 TEST_DONE();
266
267 TEST_START("fuzz RSA public");
268 buf = load_file("rsa_1");
269 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
270 sshbuf_free(buf);
271 public_fuzz(k1);
272 sshkey_free(k1);
273 TEST_DONE();
274
275 TEST_START("fuzz RSA cert");
276 ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k1), 0);
277 public_fuzz(k1);
278 sshkey_free(k1);
279 TEST_DONE();
280
281 #ifdef WITH_DSA
282 TEST_START("fuzz DSA public");
283 buf = load_file("dsa_1");
284 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
285 sshbuf_free(buf);
286 public_fuzz(k1);
287 sshkey_free(k1);
288 TEST_DONE();
289
290 TEST_START("fuzz DSA cert");
291 ASSERT_INT_EQ(sshkey_load_cert(test_data_file("dsa_1"), &k1), 0);
292 public_fuzz(k1);
293 sshkey_free(k1);
294 TEST_DONE();
295 #endif
296
297 TEST_START("fuzz ECDSA public");
298 buf = load_file("ecdsa_1");
299 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
300 sshbuf_free(buf);
301 public_fuzz(k1);
302 sshkey_free(k1);
303 TEST_DONE();
304
305 TEST_START("fuzz ECDSA cert");
306 ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ecdsa_1"), &k1), 0);
307 public_fuzz(k1);
308 sshkey_free(k1);
309 TEST_DONE();
310
311 TEST_START("fuzz Ed25519 public");
312 buf = load_file("ed25519_1");
313 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
314 sshbuf_free(buf);
315 public_fuzz(k1);
316 sshkey_free(k1);
317 TEST_DONE();
318
319 TEST_START("fuzz Ed25519 cert");
320 ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ed25519_1"), &k1), 0);
321 public_fuzz(k1);
322 sshkey_free(k1);
323 TEST_DONE();
324
325 TEST_START("fuzz RSA sig");
326 buf = load_file("rsa_1");
327 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
328 sshbuf_free(buf);
329 sig_fuzz(k1, "ssh-rsa");
330 sshkey_free(k1);
331 TEST_DONE();
332
333 TEST_START("fuzz RSA SHA256 sig");
334 buf = load_file("rsa_1");
335 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
336 sshbuf_free(buf);
337 sig_fuzz(k1, "rsa-sha2-256");
338 sshkey_free(k1);
339 TEST_DONE();
340
341 TEST_START("fuzz RSA SHA512 sig");
342 buf = load_file("rsa_1");
343 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
344 sshbuf_free(buf);
345 sig_fuzz(k1, "rsa-sha2-512");
346 sshkey_free(k1);
347 TEST_DONE();
348
349 #ifdef WITH_DSA
350 TEST_START("fuzz DSA sig");
351 buf = load_file("dsa_1");
352 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
353 sshbuf_free(buf);
354 sig_fuzz(k1, NULL);
355 sshkey_free(k1);
356 TEST_DONE();
357 #endif
358
359 TEST_START("fuzz ECDSA sig");
360 buf = load_file("ecdsa_1");
361 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
362 sshbuf_free(buf);
363 sig_fuzz(k1, NULL);
364 sshkey_free(k1);
365 TEST_DONE();
366
367 TEST_START("fuzz Ed25519 sig");
368 buf = load_file("ed25519_1");
369 ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
370 sshbuf_free(buf);
371 sig_fuzz(k1, NULL);
372 sshkey_free(k1);
373 TEST_DONE();
374
375 /* XXX fuzz decoded new-format blobs too */
376
377 }
378