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