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 *
unpack(const uint8_t * ptr,size_t len)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
pack(uint8_t * ptr,size_t len,const struct param * p)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
pack_dummy(uint8_t * ptr,size_t len)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 *
prepare_dev(void)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
get_info(const struct param * p)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
consume_template(const fido_bio_template_t * t)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
consume_enroll(fido_bio_enroll_t * e)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
enroll(const struct param * p)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
list(const struct param * p)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
set_name(const struct param * p)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
del(const struct param * p)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
test(const struct param * p)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
mutate(struct param * p,unsigned int seed,unsigned int flags)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