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
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 #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