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