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 *
unpack(const uint8_t * ptr,size_t len)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
pack(uint8_t * ptr,size_t len,const struct param * p)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
pack_dummy(uint8_t * ptr,size_t len)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 *
prepare_dev(void)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
get_info(const struct param * p)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
consume_template(const fido_bio_template_t * t)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
consume_enroll(fido_bio_enroll_t * e)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
enroll(const struct param * p)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
list(const struct param * p)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
set_name(const struct param * p)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
del(const struct param * p)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
test(const struct param * p)408 test(const struct param *p)
409 {
410 prng_init((unsigned int)p->seed);
411 fido_init(FIDO_DEBUG);
412 fido_set_log_handler(consume_str);
413
414 get_info(p);
415 enroll(p);
416 list(p);
417 set_name(p);
418 del(p);
419 }
420
421 void
mutate(struct param * p,unsigned int seed,unsigned int flags)422 mutate(struct param *p, unsigned int seed, unsigned int flags) NO_MSAN
423 {
424 if (flags & MUTATE_SEED)
425 p->seed = (int)seed;
426
427 if (flags & MUTATE_PARAM) {
428 mutate_blob(&p->id);
429 mutate_string(p->pin);
430 mutate_string(p->name);
431 }
432
433 if (flags & MUTATE_WIREDATA) {
434 mutate_blob(&p->info_wire_data);
435 mutate_blob(&p->enroll_wire_data);
436 mutate_blob(&p->list_wire_data);
437 mutate_blob(&p->set_name_wire_data);
438 mutate_blob(&p->remove_wire_data);
439 }
440 }
441