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 "dummy.h"
17
18#include "../openbsd-compat/openbsd-compat.h"
19
20#define MAXRPID	64
21
22struct param {
23	char pin1[MAXSTR];
24	char pin2[MAXSTR];
25	struct blob reset_wire_data;
26	struct blob info_wire_data;
27	struct blob set_pin_wire_data;
28	struct blob change_pin_wire_data;
29	struct blob retry_wire_data;
30	struct blob config_wire_data;
31	int seed;
32};
33
34static const uint8_t dummy_reset_wire_data[] = {
35	WIREDATA_CTAP_INIT,
36	WIREDATA_CTAP_CBOR_INFO,
37	WIREDATA_CTAP_KEEPALIVE,
38	WIREDATA_CTAP_KEEPALIVE,
39	WIREDATA_CTAP_KEEPALIVE,
40	WIREDATA_CTAP_CBOR_STATUS,
41};
42
43static const uint8_t dummy_info_wire_data[] = {
44	WIREDATA_CTAP_INIT,
45	WIREDATA_CTAP_CBOR_INFO,
46	WIREDATA_CTAP_CBOR_INFO,
47};
48
49static const uint8_t dummy_set_pin_wire_data[] = {
50	WIREDATA_CTAP_INIT,
51	WIREDATA_CTAP_CBOR_INFO,
52	WIREDATA_CTAP_CBOR_AUTHKEY,
53	WIREDATA_CTAP_CBOR_STATUS,
54};
55
56static const uint8_t dummy_change_pin_wire_data[] = {
57	WIREDATA_CTAP_INIT,
58	WIREDATA_CTAP_CBOR_INFO,
59	WIREDATA_CTAP_CBOR_AUTHKEY,
60	WIREDATA_CTAP_CBOR_STATUS,
61};
62
63static const uint8_t dummy_retry_wire_data[] = {
64	WIREDATA_CTAP_INIT,
65	WIREDATA_CTAP_CBOR_INFO,
66	WIREDATA_CTAP_CBOR_RETRIES,
67};
68
69static const uint8_t dummy_config_wire_data[] = {
70	WIREDATA_CTAP_INIT,
71	WIREDATA_CTAP_CBOR_INFO,
72	WIREDATA_CTAP_CBOR_STATUS,
73};
74
75struct param *
76unpack(const uint8_t *ptr, size_t len)
77{
78	cbor_item_t *item = NULL, **v;
79	struct cbor_load_result cbor;
80	struct param *p;
81	int ok = -1;
82
83	if ((p = calloc(1, sizeof(*p))) == NULL ||
84	    (item = cbor_load(ptr, len, &cbor)) == NULL ||
85	    cbor.read != len ||
86	    cbor_isa_array(item) == false ||
87	    cbor_array_is_definite(item) == false ||
88	    cbor_array_size(item) != 9 ||
89	    (v = cbor_array_handle(item)) == NULL)
90		goto fail;
91
92	if (unpack_int(v[0], &p->seed) < 0 ||
93	    unpack_string(v[1], p->pin1) < 0 ||
94	    unpack_string(v[2], p->pin2) < 0 ||
95	    unpack_blob(v[3], &p->reset_wire_data) < 0 ||
96	    unpack_blob(v[4], &p->info_wire_data) < 0 ||
97	    unpack_blob(v[5], &p->set_pin_wire_data) < 0 ||
98	    unpack_blob(v[6], &p->change_pin_wire_data) < 0 ||
99	    unpack_blob(v[7], &p->retry_wire_data) < 0 ||
100	    unpack_blob(v[8], &p->config_wire_data) < 0)
101		goto fail;
102
103	ok = 0;
104fail:
105	if (ok < 0) {
106		free(p);
107		p = NULL;
108	}
109
110	if (item)
111		cbor_decref(&item);
112
113	return p;
114}
115
116size_t
117pack(uint8_t *ptr, size_t len, const struct param *p)
118{
119	cbor_item_t *argv[9], *array = NULL;
120	size_t cbor_alloc_len, cbor_len = 0;
121	unsigned char *cbor = NULL;
122
123	memset(argv, 0, sizeof(argv));
124
125	if ((array = cbor_new_definite_array(9)) == NULL ||
126	    (argv[0] = pack_int(p->seed)) == NULL ||
127	    (argv[1] = pack_string(p->pin1)) == NULL ||
128	    (argv[2] = pack_string(p->pin2)) == NULL ||
129	    (argv[3] = pack_blob(&p->reset_wire_data)) == NULL ||
130	    (argv[4] = pack_blob(&p->info_wire_data)) == NULL ||
131	    (argv[5] = pack_blob(&p->set_pin_wire_data)) == NULL ||
132	    (argv[6] = pack_blob(&p->change_pin_wire_data)) == NULL ||
133	    (argv[7] = pack_blob(&p->retry_wire_data)) == NULL ||
134	    (argv[8] = pack_blob(&p->config_wire_data)) == NULL)
135		goto fail;
136
137	for (size_t i = 0; i < 9; i++)
138		if (cbor_array_push(array, argv[i]) == false)
139			goto fail;
140
141	if ((cbor_len = cbor_serialize_alloc(array, &cbor,
142	    &cbor_alloc_len)) == 0 || cbor_len > len) {
143		cbor_len = 0;
144		goto fail;
145	}
146
147	memcpy(ptr, cbor, cbor_len);
148fail:
149	for (size_t i = 0; i < 9; i++)
150		if (argv[i])
151			cbor_decref(&argv[i]);
152
153	if (array)
154		cbor_decref(&array);
155
156	free(cbor);
157
158	return cbor_len;
159}
160
161size_t
162pack_dummy(uint8_t *ptr, size_t len)
163{
164	struct param dummy;
165	uint8_t blob[MAXCORPUS];
166	size_t blob_len;
167
168	memset(&dummy, 0, sizeof(dummy));
169
170	strlcpy(dummy.pin1, dummy_pin1, sizeof(dummy.pin1));
171	strlcpy(dummy.pin2, dummy_pin2, sizeof(dummy.pin2));
172
173	dummy.reset_wire_data.len = sizeof(dummy_reset_wire_data);
174	dummy.info_wire_data.len = sizeof(dummy_info_wire_data);
175	dummy.set_pin_wire_data.len = sizeof(dummy_set_pin_wire_data);
176	dummy.change_pin_wire_data.len = sizeof(dummy_change_pin_wire_data);
177	dummy.retry_wire_data.len = sizeof(dummy_retry_wire_data);
178	dummy.config_wire_data.len = sizeof(dummy_config_wire_data);
179
180	memcpy(&dummy.reset_wire_data.body, &dummy_reset_wire_data,
181	    dummy.reset_wire_data.len);
182	memcpy(&dummy.info_wire_data.body, &dummy_info_wire_data,
183	    dummy.info_wire_data.len);
184	memcpy(&dummy.set_pin_wire_data.body, &dummy_set_pin_wire_data,
185	    dummy.set_pin_wire_data.len);
186	memcpy(&dummy.change_pin_wire_data.body, &dummy_change_pin_wire_data,
187	    dummy.change_pin_wire_data.len);
188	memcpy(&dummy.retry_wire_data.body, &dummy_retry_wire_data,
189	    dummy.retry_wire_data.len);
190	memcpy(&dummy.config_wire_data.body, &dummy_config_wire_data,
191	    dummy.config_wire_data.len);
192
193	assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
194
195	if (blob_len > len) {
196		memcpy(ptr, blob, len);
197		return len;
198	}
199
200	memcpy(ptr, blob, blob_len);
201
202	return blob_len;
203}
204
205static void
206dev_reset(const struct param *p)
207{
208	fido_dev_t *dev;
209
210	set_wire_data(p->reset_wire_data.body, p->reset_wire_data.len);
211
212	if ((dev = open_dev(0)) == NULL)
213		return;
214
215	fido_dev_reset(dev);
216	fido_dev_close(dev);
217	fido_dev_free(&dev);
218}
219
220static void
221dev_get_cbor_info(const struct param *p)
222{
223	fido_dev_t *dev;
224	fido_cbor_info_t *ci;
225	uint64_t n;
226	uint8_t proto, major, minor, build, flags;
227	bool v;
228
229	set_wire_data(p->info_wire_data.body, p->info_wire_data.len);
230
231	if ((dev = open_dev(0)) == NULL)
232		return;
233
234	proto = fido_dev_protocol(dev);
235	major = fido_dev_major(dev);
236	minor = fido_dev_minor(dev);
237	build = fido_dev_build(dev);
238	flags = fido_dev_flags(dev);
239
240	consume(&proto, sizeof(proto));
241	consume(&major, sizeof(major));
242	consume(&minor, sizeof(minor));
243	consume(&build, sizeof(build));
244	consume(&flags, sizeof(flags));
245
246	if ((ci = fido_cbor_info_new()) == NULL)
247		goto out;
248
249	fido_dev_get_cbor_info(dev, ci);
250
251	for (size_t i = 0; i < fido_cbor_info_versions_len(ci); i++) {
252		char * const *sa = fido_cbor_info_versions_ptr(ci);
253		consume(sa[i], strlen(sa[i]));
254	}
255
256	for (size_t i = 0; i < fido_cbor_info_extensions_len(ci); i++) {
257		char * const *sa = fido_cbor_info_extensions_ptr(ci);
258		consume(sa[i], strlen(sa[i]));
259	}
260
261	for (size_t i = 0; i < fido_cbor_info_transports_len(ci); i++) {
262		char * const *sa = fido_cbor_info_transports_ptr(ci);
263		consume(sa[i], strlen(sa[i]));
264	}
265
266	for (size_t i = 0; i < fido_cbor_info_options_len(ci); i++) {
267		char * const *sa = fido_cbor_info_options_name_ptr(ci);
268		const bool *va = fido_cbor_info_options_value_ptr(ci);
269		consume(sa[i], strlen(sa[i]));
270		consume(&va[i], sizeof(va[i]));
271	}
272
273	/* +1 on purpose */
274	for (size_t i = 0; i <= fido_cbor_info_algorithm_count(ci); i++) {
275		const char *type = fido_cbor_info_algorithm_type(ci, i);
276		int cose = fido_cbor_info_algorithm_cose(ci, i);
277		consume_str(type);
278		consume(&cose, sizeof(cose));
279	}
280
281	for (size_t i = 0; i < fido_cbor_info_certs_len(ci); i++) {
282		char * const *na = fido_cbor_info_certs_name_ptr(ci);
283		const uint64_t *va = fido_cbor_info_certs_value_ptr(ci);
284		consume(na[i], strlen(na[i]));
285		consume(&va[i], sizeof(va[i]));
286	}
287
288	n = fido_cbor_info_maxmsgsiz(ci);
289	consume(&n, sizeof(n));
290	n = fido_cbor_info_maxcredbloblen(ci);
291	consume(&n, sizeof(n));
292	n = fido_cbor_info_maxcredcntlst(ci);
293	consume(&n, sizeof(n));
294	n = fido_cbor_info_maxcredidlen(ci);
295	consume(&n, sizeof(n));
296	n = fido_cbor_info_maxlargeblob(ci);
297	consume(&n, sizeof(n));
298	n = fido_cbor_info_fwversion(ci);
299	consume(&n, sizeof(n));
300	n = fido_cbor_info_minpinlen(ci);
301	consume(&n, sizeof(n));
302	n = fido_cbor_info_maxrpid_minpinlen(ci);
303	consume(&n, sizeof(n));
304	n = fido_cbor_info_uv_attempts(ci);
305	consume(&n, sizeof(n));
306	n = fido_cbor_info_uv_modality(ci);
307	consume(&n, sizeof(n));
308	n = (uint64_t)fido_cbor_info_rk_remaining(ci);
309	consume(&n, sizeof(n));
310
311	consume(fido_cbor_info_aaguid_ptr(ci), fido_cbor_info_aaguid_len(ci));
312	consume(fido_cbor_info_protocols_ptr(ci),
313	    fido_cbor_info_protocols_len(ci));
314
315	v = fido_cbor_info_new_pin_required(ci);
316	consume(&v, sizeof(v));
317
318out:
319	fido_dev_close(dev);
320	fido_dev_free(&dev);
321
322	fido_cbor_info_free(&ci);
323}
324
325static void
326dev_set_pin(const struct param *p)
327{
328	fido_dev_t *dev;
329
330	set_wire_data(p->set_pin_wire_data.body, p->set_pin_wire_data.len);
331
332	if ((dev = open_dev(0)) == NULL)
333		return;
334
335	fido_dev_set_pin(dev, p->pin1, NULL);
336	fido_dev_close(dev);
337	fido_dev_free(&dev);
338}
339
340static void
341dev_change_pin(const struct param *p)
342{
343	fido_dev_t *dev;
344
345	set_wire_data(p->change_pin_wire_data.body, p->change_pin_wire_data.len);
346
347	if ((dev = open_dev(0)) == NULL)
348		return;
349
350	fido_dev_set_pin(dev, p->pin2, p->pin1);
351	fido_dev_close(dev);
352	fido_dev_free(&dev);
353}
354
355static void
356dev_get_retry_count(const struct param *p)
357{
358	fido_dev_t *dev;
359	int n = 0;
360
361	set_wire_data(p->retry_wire_data.body, p->retry_wire_data.len);
362
363	if ((dev = open_dev(0)) == NULL)
364		return;
365
366	fido_dev_get_retry_count(dev, &n);
367	consume(&n, sizeof(n));
368	fido_dev_close(dev);
369	fido_dev_free(&dev);
370}
371
372static void
373dev_get_uv_retry_count(const struct param *p)
374{
375	fido_dev_t *dev;
376	int n = 0;
377
378	set_wire_data(p->retry_wire_data.body, p->retry_wire_data.len);
379
380	if ((dev = open_dev(0)) == NULL)
381		return;
382
383	fido_dev_get_uv_retry_count(dev, &n);
384	consume(&n, sizeof(n));
385	fido_dev_close(dev);
386	fido_dev_free(&dev);
387}
388
389static void
390dev_enable_entattest(const struct param *p)
391{
392	fido_dev_t *dev;
393	const char *pin;
394	int r;
395
396	set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
397	if ((dev = open_dev(0)) == NULL)
398		return;
399	pin = p->pin1;
400	if (strlen(pin) == 0)
401		pin = NULL;
402	r = fido_dev_enable_entattest(dev, pin);
403	consume_str(fido_strerr(r));
404	fido_dev_close(dev);
405	fido_dev_free(&dev);
406}
407
408static void
409dev_toggle_always_uv(const struct param *p)
410{
411	fido_dev_t *dev;
412	const char *pin;
413	int r;
414
415	set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
416	if ((dev = open_dev(0)) == NULL)
417		return;
418	pin = p->pin1;
419	if (strlen(pin) == 0)
420		pin = NULL;
421	r = fido_dev_toggle_always_uv(dev, pin);
422	consume_str(fido_strerr(r));
423	fido_dev_close(dev);
424	fido_dev_free(&dev);
425}
426
427static void
428dev_force_pin_change(const struct param *p)
429{
430	fido_dev_t *dev;
431	const char *pin;
432	int r;
433
434	set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
435	if ((dev = open_dev(0)) == NULL)
436		return;
437	pin = p->pin1;
438	if (strlen(pin) == 0)
439		pin = NULL;
440	r = fido_dev_force_pin_change(dev, pin);
441	consume_str(fido_strerr(r));
442	fido_dev_close(dev);
443	fido_dev_free(&dev);
444}
445
446static void
447dev_set_pin_minlen(const struct param *p)
448{
449	fido_dev_t *dev;
450	const char *pin;
451	int r;
452
453	set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
454	if ((dev = open_dev(0)) == NULL)
455		return;
456	pin = p->pin1;
457	if (strlen(pin) == 0)
458		pin = NULL;
459	r = fido_dev_set_pin_minlen(dev, strlen(p->pin2), pin);
460	consume_str(fido_strerr(r));
461	fido_dev_close(dev);
462	fido_dev_free(&dev);
463}
464
465static void
466dev_set_pin_minlen_rpid(const struct param *p)
467{
468	fido_dev_t *dev;
469	const char *rpid[MAXRPID];
470	const char *pin;
471	size_t n;
472	int r;
473
474	set_wire_data(p->config_wire_data.body, p->config_wire_data.len);
475	if ((dev = open_dev(0)) == NULL)
476		return;
477	n = uniform_random(MAXRPID);
478	for (size_t i = 0; i < n; i++)
479		rpid[i] = dummy_rp_id;
480	pin = p->pin1;
481	if (strlen(pin) == 0)
482		pin = NULL;
483	r = fido_dev_set_pin_minlen_rpid(dev, rpid, n, pin);
484	consume_str(fido_strerr(r));
485	fido_dev_close(dev);
486	fido_dev_free(&dev);
487}
488
489void
490test(const struct param *p)
491{
492	prng_init((unsigned int)p->seed);
493	fuzz_clock_reset();
494	fido_init(FIDO_DEBUG);
495	fido_set_log_handler(consume_str);
496
497	dev_reset(p);
498	dev_get_cbor_info(p);
499	dev_set_pin(p);
500	dev_change_pin(p);
501	dev_get_retry_count(p);
502	dev_get_uv_retry_count(p);
503	dev_enable_entattest(p);
504	dev_toggle_always_uv(p);
505	dev_force_pin_change(p);
506	dev_set_pin_minlen(p);
507	dev_set_pin_minlen_rpid(p);
508}
509
510void
511mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
512{
513	if (flags & MUTATE_SEED)
514		p->seed = (int)seed;
515
516	if (flags & MUTATE_PARAM) {
517		mutate_string(p->pin1);
518		mutate_string(p->pin2);
519	}
520
521	if (flags & MUTATE_WIREDATA) {
522		mutate_blob(&p->reset_wire_data);
523		mutate_blob(&p->info_wire_data);
524		mutate_blob(&p->set_pin_wire_data);
525		mutate_blob(&p->change_pin_wire_data);
526		mutate_blob(&p->retry_wire_data);
527	}
528}
529