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