xref: /freebsd/contrib/libfido2/fuzz/fuzz_bio.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1 /*
2  * Copyright (c) 2019 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 /* Parameter set defining a FIDO2 credential management operation. */
21 struct param {
22 	char pin[MAXSTR];
23 	char name[MAXSTR];
24 	int seed;
25 	struct blob id;
26 	struct blob info_wire_data;
27 	struct blob enroll_wire_data;
28 	struct blob list_wire_data;
29 	struct blob set_name_wire_data;
30 	struct blob remove_wire_data;
31 };
32 
33 /*
34  * Collection of HID reports from an authenticator issued with a FIDO2
35  * 'getFingerprintSensorInfo' bio enrollment command.
36  */
37 static const uint8_t dummy_info_wire_data[] = {
38 	WIREDATA_CTAP_INIT,
39 	WIREDATA_CTAP_CBOR_INFO,
40 	WIREDATA_CTAP_CBOR_BIO_INFO,
41 };
42 
43 /*
44  * Collection of HID reports from an authenticator issued with FIDO2
45  * 'enrollBegin' + 'enrollCaptureNextSample' bio enrollment commands.
46  */
47 static const uint8_t dummy_enroll_wire_data[] = {
48 	WIREDATA_CTAP_INIT,
49 	WIREDATA_CTAP_CBOR_INFO,
50 	WIREDATA_CTAP_CBOR_AUTHKEY,
51 	WIREDATA_CTAP_CBOR_PINTOKEN,
52 	WIREDATA_CTAP_CBOR_BIO_ENROLL,
53 };
54 
55 /*
56  * Collection of HID reports from an authenticator issued with a FIDO2
57  * 'enumerateEnrollments' bio enrollment command.
58  */
59 static const uint8_t dummy_list_wire_data[] = {
60 	WIREDATA_CTAP_INIT,
61 	WIREDATA_CTAP_CBOR_INFO,
62 	WIREDATA_CTAP_CBOR_AUTHKEY,
63 	WIREDATA_CTAP_CBOR_PINTOKEN,
64 	WIREDATA_CTAP_CBOR_BIO_ENUM,
65 };
66 
67 /*
68  * Collection of HID reports from an authenticator issued with a FIDO2
69  * 'setFriendlyName' bio enrollment command.
70  */
71 static const uint8_t dummy_set_name_wire_data[] = {
72 	WIREDATA_CTAP_INIT,
73 	WIREDATA_CTAP_CBOR_INFO,
74 	WIREDATA_CTAP_CBOR_AUTHKEY,
75 	WIREDATA_CTAP_CBOR_PINTOKEN,
76 	WIREDATA_CTAP_CBOR_STATUS,
77 };
78 
79 /*
80  * Collection of HID reports from an authenticator issued with a FIDO2
81  * 'removeEnrollment' bio enrollment command.
82  */
83 static const uint8_t dummy_remove_wire_data[] = {
84 	WIREDATA_CTAP_INIT,
85 	WIREDATA_CTAP_CBOR_INFO,
86 	WIREDATA_CTAP_CBOR_AUTHKEY,
87 	WIREDATA_CTAP_CBOR_PINTOKEN,
88 	WIREDATA_CTAP_CBOR_STATUS,
89 };
90 
91 struct param *
92 unpack(const uint8_t *ptr, size_t len)
93 {
94 	cbor_item_t *item = NULL, **v;
95 	struct cbor_load_result cbor;
96 	struct param *p;
97 	int ok = -1;
98 
99 	if ((p = calloc(1, sizeof(*p))) == NULL ||
100 	    (item = cbor_load(ptr, len, &cbor)) == NULL ||
101 	    cbor.read != len ||
102 	    cbor_isa_array(item) == false ||
103 	    cbor_array_is_definite(item) == false ||
104 	    cbor_array_size(item) != 9 ||
105 	    (v = cbor_array_handle(item)) == NULL)
106 		goto fail;
107 
108 	if (unpack_int(v[0], &p->seed) < 0 ||
109 	    unpack_string(v[1], p->pin) < 0 ||
110 	    unpack_string(v[2], p->name) < 0 ||
111 	    unpack_blob(v[3], &p->id) < 0 ||
112 	    unpack_blob(v[4], &p->info_wire_data) < 0 ||
113 	    unpack_blob(v[5], &p->enroll_wire_data) < 0 ||
114 	    unpack_blob(v[6], &p->list_wire_data) < 0 ||
115 	    unpack_blob(v[7], &p->set_name_wire_data) < 0 ||
116 	    unpack_blob(v[8], &p->remove_wire_data) < 0)
117 		goto fail;
118 
119 	ok = 0;
120 fail:
121 	if (ok < 0) {
122 		free(p);
123 		p = NULL;
124 	}
125 
126 	if (item)
127 		cbor_decref(&item);
128 
129 	return p;
130 }
131 
132 size_t
133 pack(uint8_t *ptr, size_t len, const struct param *p)
134 {
135 	cbor_item_t *argv[9], *array = NULL;
136 	size_t cbor_alloc_len, cbor_len = 0;
137 	unsigned char *cbor = NULL;
138 
139 	memset(argv, 0, sizeof(argv));
140 
141 	if ((array = cbor_new_definite_array(9)) == NULL ||
142 	    (argv[0] = pack_int(p->seed)) == NULL ||
143 	    (argv[1] = pack_string(p->pin)) == NULL ||
144 	    (argv[2] = pack_string(p->name)) == NULL ||
145 	    (argv[3] = pack_blob(&p->id)) == NULL ||
146 	    (argv[4] = pack_blob(&p->info_wire_data)) == NULL ||
147 	    (argv[5] = pack_blob(&p->enroll_wire_data)) == NULL ||
148 	    (argv[6] = pack_blob(&p->list_wire_data)) == NULL ||
149 	    (argv[7] = pack_blob(&p->set_name_wire_data)) == NULL ||
150 	    (argv[8] = pack_blob(&p->remove_wire_data)) == NULL)
151 		goto fail;
152 
153 	for (size_t i = 0; i < 9; i++)
154 		if (cbor_array_push(array, argv[i]) == false)
155 			goto fail;
156 
157 	if ((cbor_len = cbor_serialize_alloc(array, &cbor,
158 	    &cbor_alloc_len)) == 0 || cbor_len > len) {
159 		cbor_len = 0;
160 		goto fail;
161 	}
162 
163 	memcpy(ptr, cbor, cbor_len);
164 fail:
165 	for (size_t i = 0; i < 9; i++)
166 		if (argv[i])
167 			cbor_decref(&argv[i]);
168 
169 	if (array)
170 		cbor_decref(&array);
171 
172 	free(cbor);
173 
174 	return cbor_len;
175 }
176 
177 size_t
178 pack_dummy(uint8_t *ptr, size_t len)
179 {
180 	struct param dummy;
181 	uint8_t	blob[MAXCORPUS];
182 	size_t blob_len;
183 
184 	memset(&dummy, 0, sizeof(dummy));
185 
186 	strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
187 	strlcpy(dummy.name, dummy_name, sizeof(dummy.name));
188 
189 	dummy.info_wire_data.len = sizeof(dummy_info_wire_data);
190 	dummy.enroll_wire_data.len = sizeof(dummy_enroll_wire_data);
191 	dummy.list_wire_data.len = sizeof(dummy_list_wire_data);
192 	dummy.set_name_wire_data.len = sizeof(dummy_set_name_wire_data);
193 	dummy.remove_wire_data.len = sizeof(dummy_remove_wire_data);
194 	dummy.id.len = sizeof(dummy_id);
195 
196 	memcpy(&dummy.info_wire_data.body, &dummy_info_wire_data,
197 	    dummy.info_wire_data.len);
198 	memcpy(&dummy.enroll_wire_data.body, &dummy_enroll_wire_data,
199 	    dummy.enroll_wire_data.len);
200 	memcpy(&dummy.list_wire_data.body, &dummy_list_wire_data,
201 	    dummy.list_wire_data.len);
202 	memcpy(&dummy.set_name_wire_data.body, &dummy_set_name_wire_data,
203 	    dummy.set_name_wire_data.len);
204 	memcpy(&dummy.remove_wire_data.body, &dummy_remove_wire_data,
205 	    dummy.remove_wire_data.len);
206 	memcpy(&dummy.id.body, &dummy_id, dummy.id.len);
207 
208 	assert((blob_len = pack(blob, sizeof(blob), &dummy)) != 0);
209 
210 	if (blob_len > len) {
211 		memcpy(ptr, blob, len);
212 		return len;
213 	}
214 
215 	memcpy(ptr, blob, blob_len);
216 
217 	return blob_len;
218 }
219 
220 static fido_dev_t *
221 prepare_dev(void)
222 {
223 	fido_dev_t *dev;
224 	bool x;
225 
226 	if ((dev = open_dev(0)) == NULL)
227 		return NULL;
228 
229 	x = fido_dev_is_fido2(dev);
230 	consume(&x, sizeof(x));
231 	x = fido_dev_supports_pin(dev);
232 	consume(&x, sizeof(x));
233 	x = fido_dev_has_pin(dev);
234 	consume(&x, sizeof(x));
235 	x = fido_dev_supports_uv(dev);
236 	consume(&x, sizeof(x));
237 	x = fido_dev_has_uv(dev);
238 	consume(&x, sizeof(x));
239 
240 	return dev;
241 }
242 
243 static void
244 get_info(const struct param *p)
245 {
246 	fido_dev_t *dev = NULL;
247 	fido_bio_info_t *i = NULL;
248 	uint8_t type;
249 	uint8_t max_samples;
250 	int r;
251 
252 	set_wire_data(p->info_wire_data.body, p->info_wire_data.len);
253 
254 	if ((dev = prepare_dev()) == NULL || (i = fido_bio_info_new()) == NULL)
255 		goto done;
256 
257 	r = fido_bio_dev_get_info(dev, i);
258 	consume_str(fido_strerr(r));
259 
260 	type = fido_bio_info_type(i);
261 	max_samples = fido_bio_info_max_samples(i);
262 	consume(&type, sizeof(type));
263 	consume(&max_samples, sizeof(max_samples));
264 
265 done:
266 	if (dev)
267 		fido_dev_close(dev);
268 
269 	fido_dev_free(&dev);
270 	fido_bio_info_free(&i);
271 }
272 
273 static void
274 consume_template(const fido_bio_template_t *t)
275 {
276 	consume_str(fido_bio_template_name(t));
277 	consume(fido_bio_template_id_ptr(t), fido_bio_template_id_len(t));
278 }
279 
280 static void
281 consume_enroll(fido_bio_enroll_t *e)
282 {
283 	uint8_t last_status;
284 	uint8_t remaining_samples;
285 
286 	last_status = fido_bio_enroll_last_status(e);
287 	remaining_samples = fido_bio_enroll_remaining_samples(e);
288 	consume(&last_status, sizeof(last_status));
289 	consume(&remaining_samples, sizeof(remaining_samples));
290 }
291 
292 static void
293 enroll(const struct param *p)
294 {
295 	fido_dev_t *dev = NULL;
296 	fido_bio_template_t *t = NULL;
297 	fido_bio_enroll_t *e = NULL;
298 	size_t cnt = 0;
299 
300 	set_wire_data(p->enroll_wire_data.body, p->enroll_wire_data.len);
301 
302 	if ((dev = prepare_dev()) == NULL ||
303 	    (t = fido_bio_template_new()) == NULL ||
304 	    (e = fido_bio_enroll_new()) == NULL)
305 		goto done;
306 
307 	fido_bio_dev_enroll_begin(dev, t, e, (uint32_t)p->seed, p->pin);
308 
309 	consume_template(t);
310 	consume_enroll(e);
311 
312 	while (fido_bio_enroll_remaining_samples(e) > 0 && cnt++ < 5) {
313 		fido_bio_dev_enroll_continue(dev, t, e, p->seed);
314 		consume_template(t);
315 		consume_enroll(e);
316 	}
317 
318 done:
319 	if (dev)
320 		fido_dev_close(dev);
321 
322 	fido_dev_free(&dev);
323 	fido_bio_template_free(&t);
324 	fido_bio_enroll_free(&e);
325 }
326 
327 static void
328 list(const struct param *p)
329 {
330 	fido_dev_t *dev = NULL;
331 	fido_bio_template_array_t *ta = NULL;
332 	const fido_bio_template_t *t = NULL;
333 
334 	set_wire_data(p->list_wire_data.body, p->list_wire_data.len);
335 
336 	if ((dev = prepare_dev()) == NULL ||
337 	    (ta = fido_bio_template_array_new()) == NULL)
338 		goto done;
339 
340 	fido_bio_dev_get_template_array(dev, ta, p->pin);
341 
342 	/* +1 on purpose */
343 	for (size_t i = 0; i < fido_bio_template_array_count(ta) + 1; i++)
344 		if ((t = fido_bio_template(ta, i)) != NULL)
345 			consume_template(t);
346 
347 done:
348 	if (dev)
349 		fido_dev_close(dev);
350 
351 	fido_dev_free(&dev);
352 	fido_bio_template_array_free(&ta);
353 }
354 
355 static void
356 set_name(const struct param *p)
357 {
358 	fido_dev_t *dev = NULL;
359 	fido_bio_template_t *t = NULL;
360 
361 	set_wire_data(p->set_name_wire_data.body, p->set_name_wire_data.len);
362 
363 	if ((dev = prepare_dev()) == NULL ||
364 	    (t = fido_bio_template_new()) == NULL)
365 		goto done;
366 
367 	fido_bio_template_set_name(t, p->name);
368 	fido_bio_template_set_id(t, p->id.body, p->id.len);
369 	consume_template(t);
370 
371 	fido_bio_dev_set_template_name(dev, t, p->pin);
372 
373 done:
374 	if (dev)
375 		fido_dev_close(dev);
376 
377 	fido_dev_free(&dev);
378 	fido_bio_template_free(&t);
379 }
380 
381 static void
382 del(const struct param *p)
383 {
384 	fido_dev_t *dev = NULL;
385 	fido_bio_template_t *t = NULL;
386 	int r;
387 
388 	set_wire_data(p->remove_wire_data.body, p->remove_wire_data.len);
389 
390 	if ((dev = prepare_dev()) == NULL ||
391 	    (t = fido_bio_template_new()) == NULL)
392 		goto done;
393 
394 	r = fido_bio_template_set_id(t, p->id.body, p->id.len);
395 	consume_template(t);
396 	consume_str(fido_strerr(r));
397 
398 	fido_bio_dev_enroll_remove(dev, t, p->pin);
399 
400 done:
401 	if (dev)
402 		fido_dev_close(dev);
403 
404 	fido_dev_free(&dev);
405 	fido_bio_template_free(&t);
406 }
407 
408 void
409 test(const struct param *p)
410 {
411 	prng_init((unsigned int)p->seed);
412 	fuzz_clock_reset();
413 	fido_init(FIDO_DEBUG);
414 	fido_set_log_handler(consume_str);
415 
416 	get_info(p);
417 	enroll(p);
418 	list(p);
419 	set_name(p);
420 	del(p);
421 }
422 
423 void
424 mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
425 {
426 	if (flags & MUTATE_SEED)
427 		p->seed = (int)seed;
428 
429 	if (flags & MUTATE_PARAM) {
430 		mutate_blob(&p->id);
431 		mutate_string(p->pin);
432 		mutate_string(p->name);
433 	}
434 
435 	if (flags & MUTATE_WIREDATA) {
436 		mutate_blob(&p->info_wire_data);
437 		mutate_blob(&p->enroll_wire_data);
438 		mutate_blob(&p->list_wire_data);
439 		mutate_blob(&p->set_name_wire_data);
440 		mutate_blob(&p->remove_wire_data);
441 	}
442 }
443