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