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