1 /* 	$OpenBSD: test_fuzz.c,v 1.13 2021/12/14 21:25:27 deraadt 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
43 onerror(void *fuzz)
44 {
45 	fprintf(stderr, "Failed during fuzz:\n");
46 	fuzz_dump((struct fuzz *)fuzz);
47 }
48 
49 static void
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
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
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 	TEST_START("fuzz DSA private");
164 	buf = load_file("dsa_1");
165 	fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
166 	    sshbuf_len(buf));
167 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
168 	sshkey_free(k1);
169 	sshbuf_free(buf);
170 	ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
171 	TEST_ONERROR(onerror, fuzz);
172 	for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
173 		r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
174 		ASSERT_INT_EQ(r, 0);
175 		if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
176 			sshkey_free(k1);
177 		sshbuf_reset(fuzzed);
178 		if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
179 			break;
180 	}
181 	sshbuf_free(fuzzed);
182 	fuzz_cleanup(fuzz);
183 	TEST_DONE();
184 
185 	TEST_START("fuzz DSA new-format private");
186 	buf = load_file("dsa_n");
187 	fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
188 	    sshbuf_len(buf));
189 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
190 	sshkey_free(k1);
191 	sshbuf_free(buf);
192 	ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
193 	TEST_ONERROR(onerror, fuzz);
194 	for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
195 		r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
196 		ASSERT_INT_EQ(r, 0);
197 		if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
198 			sshkey_free(k1);
199 		sshbuf_reset(fuzzed);
200 		if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
201 			break;
202 	}
203 	sshbuf_free(fuzzed);
204 	fuzz_cleanup(fuzz);
205 	TEST_DONE();
206 
207 #ifdef OPENSSL_HAS_ECC
208 	TEST_START("fuzz ECDSA private");
209 	buf = load_file("ecdsa_1");
210 	fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
211 	    sshbuf_len(buf));
212 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
213 	sshkey_free(k1);
214 	sshbuf_free(buf);
215 	ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
216 	TEST_ONERROR(onerror, fuzz);
217 	for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
218 		r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
219 		ASSERT_INT_EQ(r, 0);
220 		if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
221 			sshkey_free(k1);
222 		sshbuf_reset(fuzzed);
223 		if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
224 			break;
225 	}
226 	sshbuf_free(fuzzed);
227 	fuzz_cleanup(fuzz);
228 	TEST_DONE();
229 
230 	TEST_START("fuzz ECDSA new-format private");
231 	buf = load_file("ecdsa_n");
232 	fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
233 	    sshbuf_len(buf));
234 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
235 	sshkey_free(k1);
236 	sshbuf_free(buf);
237 	ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
238 	TEST_ONERROR(onerror, fuzz);
239 	for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
240 		r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
241 		ASSERT_INT_EQ(r, 0);
242 		if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
243 			sshkey_free(k1);
244 		sshbuf_reset(fuzzed);
245 		if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
246 			break;
247 	}
248 	sshbuf_free(fuzzed);
249 	fuzz_cleanup(fuzz);
250 	TEST_DONE();
251 #endif /* OPENSSL_HAS_ECC */
252 #endif /* WITH_OPENSSL */
253 
254 	TEST_START("fuzz Ed25519 private");
255 	buf = load_file("ed25519_1");
256 	fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
257 	    sshbuf_len(buf));
258 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
259 	sshkey_free(k1);
260 	sshbuf_free(buf);
261 	ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
262 	TEST_ONERROR(onerror, fuzz);
263 	for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
264 		r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
265 		ASSERT_INT_EQ(r, 0);
266 		if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
267 			sshkey_free(k1);
268 		sshbuf_reset(fuzzed);
269 		if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
270 			break;
271 	}
272 	sshbuf_free(fuzzed);
273 	fuzz_cleanup(fuzz);
274 	TEST_DONE();
275 
276 #ifdef WITH_OPENSSL
277 	TEST_START("fuzz RSA public");
278 	buf = load_file("rsa_1");
279 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
280 	sshbuf_free(buf);
281 	public_fuzz(k1);
282 	sshkey_free(k1);
283 	TEST_DONE();
284 
285 	TEST_START("fuzz RSA cert");
286 	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k1), 0);
287 	public_fuzz(k1);
288 	sshkey_free(k1);
289 	TEST_DONE();
290 
291 	TEST_START("fuzz DSA public");
292 	buf = load_file("dsa_1");
293 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
294 	sshbuf_free(buf);
295 	public_fuzz(k1);
296 	sshkey_free(k1);
297 	TEST_DONE();
298 
299 	TEST_START("fuzz DSA cert");
300 	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("dsa_1"), &k1), 0);
301 	public_fuzz(k1);
302 	sshkey_free(k1);
303 	TEST_DONE();
304 
305 #ifdef OPENSSL_HAS_ECC
306 	TEST_START("fuzz ECDSA public");
307 	buf = load_file("ecdsa_1");
308 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
309 	sshbuf_free(buf);
310 	public_fuzz(k1);
311 	sshkey_free(k1);
312 	TEST_DONE();
313 
314 	TEST_START("fuzz ECDSA cert");
315 	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ecdsa_1"), &k1), 0);
316 	public_fuzz(k1);
317 	sshkey_free(k1);
318 	TEST_DONE();
319 #endif /* OPENSSL_HAS_ECC */
320 #endif /* WITH_OPENSSL */
321 
322 	TEST_START("fuzz Ed25519 public");
323 	buf = load_file("ed25519_1");
324 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
325 	sshbuf_free(buf);
326 	public_fuzz(k1);
327 	sshkey_free(k1);
328 	TEST_DONE();
329 
330 	TEST_START("fuzz Ed25519 cert");
331 	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ed25519_1"), &k1), 0);
332 	public_fuzz(k1);
333 	sshkey_free(k1);
334 	TEST_DONE();
335 
336 #ifdef WITH_OPENSSL
337 	TEST_START("fuzz RSA sig");
338 	buf = load_file("rsa_1");
339 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
340 	sshbuf_free(buf);
341 	sig_fuzz(k1, "ssh-rsa");
342 	sshkey_free(k1);
343 	TEST_DONE();
344 
345 	TEST_START("fuzz RSA SHA256 sig");
346 	buf = load_file("rsa_1");
347 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
348 	sshbuf_free(buf);
349 	sig_fuzz(k1, "rsa-sha2-256");
350 	sshkey_free(k1);
351 	TEST_DONE();
352 
353 	TEST_START("fuzz RSA SHA512 sig");
354 	buf = load_file("rsa_1");
355 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
356 	sshbuf_free(buf);
357 	sig_fuzz(k1, "rsa-sha2-512");
358 	sshkey_free(k1);
359 	TEST_DONE();
360 
361 	TEST_START("fuzz DSA sig");
362 	buf = load_file("dsa_1");
363 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
364 	sshbuf_free(buf);
365 	sig_fuzz(k1, NULL);
366 	sshkey_free(k1);
367 	TEST_DONE();
368 
369 #ifdef OPENSSL_HAS_ECC
370 	TEST_START("fuzz ECDSA sig");
371 	buf = load_file("ecdsa_1");
372 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
373 	sshbuf_free(buf);
374 	sig_fuzz(k1, NULL);
375 	sshkey_free(k1);
376 	TEST_DONE();
377 #endif /* OPENSSL_HAS_ECC */
378 #endif /* WITH_OPENSSL */
379 
380 	TEST_START("fuzz Ed25519 sig");
381 	buf = load_file("ed25519_1");
382 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
383 	sshbuf_free(buf);
384 	sig_fuzz(k1, NULL);
385 	sshkey_free(k1);
386 	TEST_DONE();
387 
388 /* XXX fuzz decoded new-format blobs too */
389 /* XXX fuzz XMSS too */
390 
391 }
392