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