xref: /freebsd/contrib/libfido2/fuzz/fuzz_cred.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
1 /*
2  * Copyright (c) 2019-2021 Yubico AB. All rights reserved.
3  * Use of this source code is governed by a BSD-style
4  * license that can be found in the LICENSE file.
5  */
6 
7 #include <assert.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include "mutator_aux.h"
14 #include "wiredata_fido2.h"
15 #include "wiredata_u2f.h"
16 #include "dummy.h"
17 
18 #include "../openbsd-compat/openbsd-compat.h"
19 
20 /* Parameter set defining a FIDO2 make credential operation. */
21 struct param {
22 	char pin[MAXSTR];
23 	char rp_id[MAXSTR];
24 	char rp_name[MAXSTR];
25 	char user_icon[MAXSTR];
26 	char user_name[MAXSTR];
27 	char user_nick[MAXSTR];
28 	int ext;
29 	int seed;
30 	struct blob cdh;
31 	struct blob excl_cred;
32 	struct blob user_id;
33 	struct blob wire_data;
34 	uint8_t excl_count;
35 	uint8_t rk;
36 	uint8_t type;
37 	uint8_t opt;
38 	uint8_t uv;
39 };
40 
41 /*
42  * Collection of HID reports from an authenticator issued with a FIDO2
43  * make credential using the example parameters above.
44  */
45 static const uint8_t dummy_wire_data_fido[] = {
46 	WIREDATA_CTAP_INIT,
47 	WIREDATA_CTAP_CBOR_INFO,
48 	WIREDATA_CTAP_CBOR_AUTHKEY,
49 	WIREDATA_CTAP_CBOR_PINTOKEN,
50 	WIREDATA_CTAP_KEEPALIVE,
51 	WIREDATA_CTAP_KEEPALIVE,
52 	WIREDATA_CTAP_KEEPALIVE,
53 	WIREDATA_CTAP_CBOR_CRED,
54 };
55 
56 /*
57  * Collection of HID reports from an authenticator issued with a U2F
58  * registration using the example parameters above.
59  */
60 static const uint8_t dummy_wire_data_u2f[] = {
61 	WIREDATA_CTAP_INIT,
62 	WIREDATA_CTAP_U2F_6985,
63 	WIREDATA_CTAP_U2F_6985,
64 	WIREDATA_CTAP_U2F_6985,
65 	WIREDATA_CTAP_U2F_6985,
66 	WIREDATA_CTAP_U2F_6985,
67 	WIREDATA_CTAP_U2F_REGISTER,
68 };
69 
70 struct param *
71 unpack(const uint8_t *ptr, size_t len)
72 {
73 	cbor_item_t *item = NULL, **v;
74 	struct cbor_load_result cbor;
75 	struct param *p;
76 	int ok = -1;
77 
78 	if ((p = calloc(1, sizeof(*p))) == NULL ||
79 	    (item = cbor_load(ptr, len, &cbor)) == NULL ||
80 	    cbor.read != len ||
81 	    cbor_isa_array(item) == false ||
82 	    cbor_array_is_definite(item) == false ||
83 	    cbor_array_size(item) != 17 ||
84 	    (v = cbor_array_handle(item)) == NULL)
85 		goto fail;
86 
87 	if (unpack_byte(v[0], &p->rk) < 0 ||
88 	    unpack_byte(v[1], &p->type) < 0 ||
89 	    unpack_byte(v[2], &p->opt) < 0 ||
90 	    unpack_byte(v[3], &p->uv) < 0 ||
91 	    unpack_byte(v[4], &p->excl_count) < 0 ||
92 	    unpack_int(v[5], &p->ext) < 0 ||
93 	    unpack_int(v[6], &p->seed) < 0 ||
94 	    unpack_string(v[7], p->pin) < 0 ||
95 	    unpack_string(v[8], p->rp_id) < 0 ||
96 	    unpack_string(v[9], p->rp_name) < 0 ||
97 	    unpack_string(v[10], p->user_icon) < 0 ||
98 	    unpack_string(v[11], p->user_name) < 0 ||
99 	    unpack_string(v[12], p->user_nick) < 0 ||
100 	    unpack_blob(v[13], &p->cdh) < 0 ||
101 	    unpack_blob(v[14], &p->user_id) < 0 ||
102 	    unpack_blob(v[15], &p->wire_data) < 0 ||
103 	    unpack_blob(v[16], &p->excl_cred) < 0)
104 		goto fail;
105 
106 	ok = 0;
107 fail:
108 	if (ok < 0) {
109 		free(p);
110 		p = NULL;
111 	}
112 
113 	if (item)
114 		cbor_decref(&item);
115 
116 	return p;
117 }
118 
119 size_t
120 pack(uint8_t *ptr, size_t len, const struct param *p)
121 {
122 	cbor_item_t *argv[17], *array = NULL;
123 	size_t cbor_alloc_len, cbor_len = 0;
124 	unsigned char *cbor = NULL;
125 
126 	memset(argv, 0, sizeof(argv));
127 
128 	if ((array = cbor_new_definite_array(17)) == NULL ||
129 	    (argv[0] = pack_byte(p->rk)) == NULL ||
130 	    (argv[1] = pack_byte(p->type)) == NULL ||
131 	    (argv[2] = pack_byte(p->opt)) == NULL ||
132 	    (argv[3] = pack_byte(p->uv)) == NULL ||
133 	    (argv[4] = pack_byte(p->excl_count)) == NULL ||
134 	    (argv[5] = pack_int(p->ext)) == NULL ||
135 	    (argv[6] = pack_int(p->seed)) == NULL ||
136 	    (argv[7] = pack_string(p->pin)) == NULL ||
137 	    (argv[8] = pack_string(p->rp_id)) == NULL ||
138 	    (argv[9] = pack_string(p->rp_name)) == NULL ||
139 	    (argv[10] = pack_string(p->user_icon)) == NULL ||
140 	    (argv[11] = pack_string(p->user_name)) == NULL ||
141 	    (argv[12] = pack_string(p->user_nick)) == NULL ||
142 	    (argv[13] = pack_blob(&p->cdh)) == NULL ||
143 	    (argv[14] = pack_blob(&p->user_id)) == NULL ||
144 	    (argv[15] = pack_blob(&p->wire_data)) == NULL ||
145 	    (argv[16] = pack_blob(&p->excl_cred)) == NULL)
146 		goto fail;
147 
148 	for (size_t i = 0; i < 17; i++)
149 		if (cbor_array_push(array, argv[i]) == false)
150 			goto fail;
151 
152 	if ((cbor_len = cbor_serialize_alloc(array, &cbor,
153 	    &cbor_alloc_len)) > len) {
154 		cbor_len = 0;
155 		goto fail;
156 	}
157 
158 	memcpy(ptr, cbor, cbor_len);
159 fail:
160 	for (size_t i = 0; i < 17; i++)
161 		if (argv[i])
162 			cbor_decref(&argv[i]);
163 
164 	if (array)
165 		cbor_decref(&array);
166 
167 	free(cbor);
168 
169 	return cbor_len;
170 }
171 
172 size_t
173 pack_dummy(uint8_t *ptr, size_t len)
174 {
175 	struct param dummy;
176 	uint8_t blob[4096];
177 	size_t blob_len;
178 
179 	memset(&dummy, 0, sizeof(dummy));
180 
181 	dummy.type = 1;
182 	dummy.ext = FIDO_EXT_HMAC_SECRET;
183 
184 	strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
185 	strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id));
186 	strlcpy(dummy.rp_name, dummy_rp_name, sizeof(dummy.rp_name));
187 	strlcpy(dummy.user_icon, dummy_user_icon, sizeof(dummy.user_icon));
188 	strlcpy(dummy.user_name, dummy_user_name, sizeof(dummy.user_name));
189 	strlcpy(dummy.user_nick, dummy_user_nick, sizeof(dummy.user_nick));
190 
191 	dummy.cdh.len = sizeof(dummy_cdh);
192 	dummy.user_id.len = sizeof(dummy_user_id);
193 	dummy.wire_data.len = sizeof(dummy_wire_data_fido);
194 
195 	memcpy(&dummy.cdh.body, &dummy_cdh, dummy.cdh.len);
196 	memcpy(&dummy.user_id.body, &dummy_user_id, dummy.user_id.len);
197 	memcpy(&dummy.wire_data.body, &dummy_wire_data_fido,
198 	    dummy.wire_data.len);
199 
200 	assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
201 
202 	if (blob_len > len) {
203 		memcpy(ptr, blob, len);
204 		return len;
205 	}
206 
207 	memcpy(ptr, blob, blob_len);
208 
209 	return blob_len;
210 }
211 
212 static void
213 make_cred(fido_cred_t *cred, uint8_t opt, int type, const struct blob *cdh,
214     const char *rp_id, const char *rp_name, const struct blob *user_id,
215     const char *user_name, const char *user_nick, const char *user_icon,
216     int ext, uint8_t rk, uint8_t uv, const char *pin, uint8_t excl_count,
217     const struct blob *excl_cred)
218 {
219 	fido_dev_t *dev;
220 
221 	if ((dev = open_dev(opt & 2)) == NULL)
222 		return;
223 	if (opt & 1)
224 		fido_dev_force_u2f(dev);
225 
226 	for (uint8_t i = 0; i < excl_count; i++)
227 		fido_cred_exclude(cred, excl_cred->body, excl_cred->len);
228 
229 	fido_cred_set_type(cred, type);
230 	fido_cred_set_clientdata_hash(cred, cdh->body, cdh->len);
231 	fido_cred_set_rp(cred, rp_id, rp_name);
232 	fido_cred_set_user(cred, user_id->body, user_id->len, user_name,
233 	    user_nick, user_icon);
234 
235 	if (ext & FIDO_EXT_HMAC_SECRET)
236 		fido_cred_set_extensions(cred, FIDO_EXT_HMAC_SECRET);
237 	if (ext & FIDO_EXT_CRED_BLOB)
238 		fido_cred_set_blob(cred, user_id->body, user_id->len);
239 	if (ext & FIDO_EXT_LARGEBLOB_KEY)
240 		fido_cred_set_extensions(cred, FIDO_EXT_LARGEBLOB_KEY);
241 	if (ext & FIDO_EXT_MINPINLEN)
242 		fido_cred_set_pin_minlen(cred, strlen(pin));
243 
244 	if (rk & 1)
245 		fido_cred_set_rk(cred, FIDO_OPT_TRUE);
246 	if (uv & 1)
247 		fido_cred_set_uv(cred, FIDO_OPT_TRUE);
248 	if (user_id->len)
249 		fido_cred_set_prot(cred, user_id->body[0] & 0x03);
250 
251 	/* repeat memory operations to trigger reallocation paths */
252 	fido_cred_set_type(cred, type);
253 	fido_cred_set_clientdata_hash(cred, cdh->body, cdh->len);
254 	fido_cred_set_rp(cred, rp_id, rp_name);
255 	fido_cred_set_user(cred, user_id->body, user_id->len, user_name,
256 	    user_nick, user_icon);
257 
258 	if (strlen(pin) == 0)
259 		pin = NULL;
260 
261 	fido_dev_make_cred(dev, cred, (opt & 1) ? NULL : pin);
262 
263 	fido_dev_cancel(dev);
264 	fido_dev_close(dev);
265 	fido_dev_free(&dev);
266 }
267 
268 static void
269 verify_cred(int type, const unsigned char *cdh_ptr, size_t cdh_len,
270     const char *rp_id, const char *rp_name, const unsigned char *authdata_ptr,
271     size_t authdata_len, const unsigned char *authdata_raw_ptr,
272     size_t authdata_raw_len, int ext, uint8_t rk, uint8_t uv,
273     const unsigned char *x5c_ptr, size_t x5c_len, const unsigned char *sig_ptr,
274     size_t sig_len, const unsigned char *attstmt_ptr, size_t attstmt_len,
275     const char *fmt, int prot, size_t minpinlen)
276 {
277 	fido_cred_t *cred;
278 	uint8_t flags;
279 	uint32_t sigcount;
280 	int r;
281 
282 	if ((cred = fido_cred_new()) == NULL)
283 		return;
284 
285 	fido_cred_set_type(cred, type);
286 	fido_cred_set_clientdata_hash(cred, cdh_ptr, cdh_len);
287 	fido_cred_set_rp(cred, rp_id, rp_name);
288 	consume(authdata_ptr, authdata_len);
289 	consume(authdata_raw_ptr, authdata_raw_len);
290 	consume(x5c_ptr, x5c_len);
291 	consume(sig_ptr, sig_len);
292 	consume(attstmt_ptr, attstmt_len);
293 	if (fido_cred_set_authdata(cred, authdata_ptr, authdata_len) != FIDO_OK)
294 		fido_cred_set_authdata_raw(cred, authdata_raw_ptr,
295 		    authdata_raw_len);
296 	fido_cred_set_extensions(cred, ext);
297 	if (fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len) != FIDO_OK) {
298 		fido_cred_set_x509(cred, x5c_ptr, x5c_len);
299 		fido_cred_set_sig(cred, sig_ptr, sig_len);
300 	}
301 	fido_cred_set_prot(cred, prot);
302 	fido_cred_set_pin_minlen(cred, minpinlen);
303 
304 	if (rk & 1)
305 		fido_cred_set_rk(cred, FIDO_OPT_TRUE);
306 	if (uv & 1)
307 		fido_cred_set_uv(cred, FIDO_OPT_TRUE);
308 	if (fmt)
309 		fido_cred_set_fmt(cred, fmt);
310 
311 	/* repeat memory operations to trigger reallocation paths */
312 	if (fido_cred_set_authdata(cred, authdata_ptr, authdata_len) != FIDO_OK)
313 		fido_cred_set_authdata_raw(cred, authdata_raw_ptr,
314 		    authdata_raw_len);
315 	if (fido_cred_set_attstmt(cred, attstmt_ptr, attstmt_len) != FIDO_OK) {
316 		fido_cred_set_x509(cred, x5c_ptr, x5c_len);
317 		fido_cred_set_sig(cred, sig_ptr, sig_len);
318 	}
319 	fido_cred_set_x509(cred, x5c_ptr, x5c_len);
320 	fido_cred_set_sig(cred, sig_ptr, sig_len);
321 
322 	r = fido_cred_verify(cred);
323 	consume(&r, sizeof(r));
324 	r = fido_cred_verify_self(cred);
325 	consume(&r, sizeof(r));
326 
327 	consume(fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred));
328 	consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred));
329 	consume(fido_cred_aaguid_ptr(cred), fido_cred_aaguid_len(cred));
330 	consume(fido_cred_user_id_ptr(cred), fido_cred_user_id_len(cred));
331 	consume_str(fido_cred_user_name(cred));
332 	consume_str(fido_cred_display_name(cred));
333 	consume(fido_cred_largeblob_key_ptr(cred),
334 	    fido_cred_largeblob_key_len(cred));
335 
336 	flags = fido_cred_flags(cred);
337 	consume(&flags, sizeof(flags));
338 	sigcount = fido_cred_sigcount(cred);
339 	consume(&sigcount, sizeof(sigcount));
340 	type = fido_cred_type(cred);
341 	consume(&type, sizeof(type));
342 	minpinlen = fido_cred_pin_minlen(cred);
343 	consume(&minpinlen, sizeof(minpinlen));
344 
345 	fido_cred_free(&cred);
346 }
347 
348 static void
349 test_cred(const struct param *p)
350 {
351 	fido_cred_t *cred = NULL;
352 	int cose_alg = 0;
353 
354 	if ((cred = fido_cred_new()) == NULL)
355 		return;
356 
357 	switch (p->type & 3) {
358 	case 0:
359 		cose_alg = COSE_ES256;
360 		break;
361 	case 1:
362 		cose_alg = COSE_RS256;
363 		break;
364 	default:
365 		cose_alg = COSE_EDDSA;
366 		break;
367 	}
368 
369 	set_wire_data(p->wire_data.body, p->wire_data.len);
370 
371 	make_cred(cred, p->opt, cose_alg, &p->cdh, p->rp_id, p->rp_name,
372 	    &p->user_id, p->user_name, p->user_nick, p->user_icon, p->ext,
373 	    p->rk, p->uv, p->pin, p->excl_count, &p->excl_cred);
374 
375 	verify_cred(cose_alg,
376 	    fido_cred_clientdata_hash_ptr(cred),
377 	    fido_cred_clientdata_hash_len(cred), fido_cred_rp_id(cred),
378 	    fido_cred_rp_name(cred), fido_cred_authdata_ptr(cred),
379 	    fido_cred_authdata_len(cred), fido_cred_authdata_raw_ptr(cred),
380 	    fido_cred_authdata_raw_len(cred), p->ext, p->rk, p->uv,
381 	    fido_cred_x5c_ptr(cred), fido_cred_x5c_len(cred),
382 	    fido_cred_sig_ptr(cred), fido_cred_sig_len(cred),
383 	    fido_cred_attstmt_ptr(cred), fido_cred_attstmt_len(cred),
384 	    fido_cred_fmt(cred), fido_cred_prot(cred),
385 	    fido_cred_pin_minlen(cred));
386 
387 	fido_cred_free(&cred);
388 }
389 
390 static void
391 test_touch(const struct param *p)
392 {
393 	fido_dev_t *dev;
394 	int r;
395 	int touched;
396 
397 	set_wire_data(p->wire_data.body, p->wire_data.len);
398 
399 	if ((dev = open_dev(p->opt & 2)) == NULL)
400 		return;
401 	if (p->opt & 1)
402 		fido_dev_force_u2f(dev);
403 
404 	r = fido_dev_get_touch_begin(dev);
405 	consume_str(fido_strerr(r));
406 	r = fido_dev_get_touch_status(dev, &touched, -1);
407 	consume_str(fido_strerr(r));
408 	consume(&touched, sizeof(touched));
409 
410 	fido_dev_cancel(dev);
411 	fido_dev_close(dev);
412 	fido_dev_free(&dev);
413 }
414 
415 static void
416 test_misc(const struct param *p)
417 {
418 	fido_cred_t *cred = NULL;
419 
420 	if ((cred = fido_cred_new()) == NULL)
421 		return;
422 
423 	/* reuse user id as credential id */
424 	fido_cred_set_id(cred, p->user_id.body, p->user_id.len);
425 	consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred));
426 	fido_cred_free(&cred);
427 }
428 
429 void
430 test(const struct param *p)
431 {
432 	prng_init((unsigned int)p->seed);
433 	fuzz_clock_reset();
434 	fido_init(FIDO_DEBUG);
435 	fido_set_log_handler(consume_str);
436 
437 	test_cred(p);
438 	test_touch(p);
439 	test_misc(p);
440 }
441 
442 void
443 mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
444 {
445 	if (flags & MUTATE_SEED)
446 		p->seed = (int)seed;
447 
448 	if (flags & MUTATE_PARAM) {
449 		mutate_byte(&p->rk);
450 		mutate_byte(&p->type);
451 		mutate_byte(&p->opt);
452 		mutate_byte(&p->uv);
453 		mutate_byte(&p->excl_count);
454 		mutate_int(&p->ext);
455 		mutate_blob(&p->cdh);
456 		mutate_blob(&p->user_id);
457 		mutate_blob(&p->excl_cred);
458 		mutate_string(p->pin);
459 		mutate_string(p->user_icon);
460 		mutate_string(p->user_name);
461 		mutate_string(p->user_nick);
462 		mutate_string(p->rp_id);
463 		mutate_string(p->rp_name);
464 	}
465 
466 	if (flags & MUTATE_WIREDATA) {
467 		if (p->opt & 1) {
468 			p->wire_data.len = sizeof(dummy_wire_data_u2f);
469 			memcpy(&p->wire_data.body, &dummy_wire_data_u2f,
470 			    p->wire_data.len);
471 		} else {
472 			p->wire_data.len = sizeof(dummy_wire_data_fido);
473 			memcpy(&p->wire_data.body, &dummy_wire_data_fido,
474 			    p->wire_data.len);
475 		}
476 		mutate_blob(&p->wire_data);
477 	}
478 }
479