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