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 <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
35 onerror(void *fuzz)
36 {
37 	fprintf(stderr, "Failed during fuzz:\n");
38 	fuzz_dump((struct fuzz *)fuzz);
39 }
40 
41 static void
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
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
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 	TEST_START("fuzz DSA private");
156 	buf = load_file("dsa_1");
157 	fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
158 	    sshbuf_len(buf));
159 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
160 	sshkey_free(k1);
161 	sshbuf_free(buf);
162 	ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
163 	TEST_ONERROR(onerror, fuzz);
164 	for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
165 		r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
166 		ASSERT_INT_EQ(r, 0);
167 		if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
168 			sshkey_free(k1);
169 		sshbuf_reset(fuzzed);
170 		if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
171 			break;
172 	}
173 	sshbuf_free(fuzzed);
174 	fuzz_cleanup(fuzz);
175 	TEST_DONE();
176 
177 	TEST_START("fuzz DSA new-format private");
178 	buf = load_file("dsa_n");
179 	fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
180 	    sshbuf_len(buf));
181 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
182 	sshkey_free(k1);
183 	sshbuf_free(buf);
184 	ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
185 	TEST_ONERROR(onerror, fuzz);
186 	for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
187 		r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
188 		ASSERT_INT_EQ(r, 0);
189 		if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
190 			sshkey_free(k1);
191 		sshbuf_reset(fuzzed);
192 		if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
193 			break;
194 	}
195 	sshbuf_free(fuzzed);
196 	fuzz_cleanup(fuzz);
197 	TEST_DONE();
198 
199 	TEST_START("fuzz ECDSA private");
200 	buf = load_file("ecdsa_1");
201 	fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
202 	    sshbuf_len(buf));
203 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
204 	sshkey_free(k1);
205 	sshbuf_free(buf);
206 	ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
207 	TEST_ONERROR(onerror, fuzz);
208 	for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
209 		r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
210 		ASSERT_INT_EQ(r, 0);
211 		if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
212 			sshkey_free(k1);
213 		sshbuf_reset(fuzzed);
214 		if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
215 			break;
216 	}
217 	sshbuf_free(fuzzed);
218 	fuzz_cleanup(fuzz);
219 	TEST_DONE();
220 
221 	TEST_START("fuzz ECDSA new-format private");
222 	buf = load_file("ecdsa_n");
223 	fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
224 	    sshbuf_len(buf));
225 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
226 	sshkey_free(k1);
227 	sshbuf_free(buf);
228 	ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
229 	TEST_ONERROR(onerror, fuzz);
230 	for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
231 		r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
232 		ASSERT_INT_EQ(r, 0);
233 		if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
234 			sshkey_free(k1);
235 		sshbuf_reset(fuzzed);
236 		if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
237 			break;
238 	}
239 	sshbuf_free(fuzzed);
240 	fuzz_cleanup(fuzz);
241 	TEST_DONE();
242 
243 	TEST_START("fuzz Ed25519 private");
244 	buf = load_file("ed25519_1");
245 	fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
246 	    sshbuf_len(buf));
247 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
248 	sshkey_free(k1);
249 	sshbuf_free(buf);
250 	ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
251 	TEST_ONERROR(onerror, fuzz);
252 	for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) {
253 		r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
254 		ASSERT_INT_EQ(r, 0);
255 		if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
256 			sshkey_free(k1);
257 		sshbuf_reset(fuzzed);
258 		if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS)
259 			break;
260 	}
261 	sshbuf_free(fuzzed);
262 	fuzz_cleanup(fuzz);
263 	TEST_DONE();
264 
265 	TEST_START("fuzz RSA public");
266 	buf = load_file("rsa_1");
267 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
268 	sshbuf_free(buf);
269 	public_fuzz(k1);
270 	sshkey_free(k1);
271 	TEST_DONE();
272 
273 	TEST_START("fuzz RSA cert");
274 	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k1), 0);
275 	public_fuzz(k1);
276 	sshkey_free(k1);
277 	TEST_DONE();
278 
279 	TEST_START("fuzz DSA public");
280 	buf = load_file("dsa_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 DSA cert");
288 	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("dsa_1"), &k1), 0);
289 	public_fuzz(k1);
290 	sshkey_free(k1);
291 	TEST_DONE();
292 
293 	TEST_START("fuzz ECDSA public");
294 	buf = load_file("ecdsa_1");
295 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
296 	sshbuf_free(buf);
297 	public_fuzz(k1);
298 	sshkey_free(k1);
299 	TEST_DONE();
300 
301 	TEST_START("fuzz ECDSA cert");
302 	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ecdsa_1"), &k1), 0);
303 	public_fuzz(k1);
304 	sshkey_free(k1);
305 	TEST_DONE();
306 
307 	TEST_START("fuzz Ed25519 public");
308 	buf = load_file("ed25519_1");
309 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
310 	sshbuf_free(buf);
311 	public_fuzz(k1);
312 	sshkey_free(k1);
313 	TEST_DONE();
314 
315 	TEST_START("fuzz Ed25519 cert");
316 	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ed25519_1"), &k1), 0);
317 	public_fuzz(k1);
318 	sshkey_free(k1);
319 	TEST_DONE();
320 
321 	TEST_START("fuzz RSA sig");
322 	buf = load_file("rsa_1");
323 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
324 	sshbuf_free(buf);
325 	sig_fuzz(k1, "ssh-rsa");
326 	sshkey_free(k1);
327 	TEST_DONE();
328 
329 	TEST_START("fuzz RSA SHA256 sig");
330 	buf = load_file("rsa_1");
331 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
332 	sshbuf_free(buf);
333 	sig_fuzz(k1, "rsa-sha2-256");
334 	sshkey_free(k1);
335 	TEST_DONE();
336 
337 	TEST_START("fuzz RSA SHA512 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, "rsa-sha2-512");
342 	sshkey_free(k1);
343 	TEST_DONE();
344 
345 	TEST_START("fuzz DSA sig");
346 	buf = load_file("dsa_1");
347 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
348 	sshbuf_free(buf);
349 	sig_fuzz(k1, NULL);
350 	sshkey_free(k1);
351 	TEST_DONE();
352 
353 	TEST_START("fuzz ECDSA sig");
354 	buf = load_file("ecdsa_1");
355 	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
356 	sshbuf_free(buf);
357 	sig_fuzz(k1, NULL);
358 	sshkey_free(k1);
359 	TEST_DONE();
360 
361 	TEST_START("fuzz Ed25519 sig");
362 	buf = load_file("ed25519_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 /* XXX fuzz decoded new-format blobs too */
370 
371 }
372