1 /* 2 * Copyright (c) 2019-2022 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 "fido.h" 9 #include "fido/bio.h" 10 #include "fido/es256.h" 11 12 #define CMD_ENROLL_BEGIN 0x01 13 #define CMD_ENROLL_NEXT 0x02 14 #define CMD_ENROLL_CANCEL 0x03 15 #define CMD_ENUM 0x04 16 #define CMD_SET_NAME 0x05 17 #define CMD_ENROLL_REMOVE 0x06 18 #define CMD_GET_INFO 0x07 19 20 static int 21 bio_prepare_hmac(uint8_t cmd, cbor_item_t **argv, size_t argc, 22 cbor_item_t **param, fido_blob_t *hmac_data) 23 { 24 const uint8_t prefix[2] = { 0x01 /* modality */, cmd }; 25 int ok = -1; 26 size_t cbor_alloc_len; 27 size_t cbor_len; 28 unsigned char *cbor = NULL; 29 30 if (argv == NULL || param == NULL) 31 return (fido_blob_set(hmac_data, prefix, sizeof(prefix))); 32 33 if ((*param = cbor_flatten_vector(argv, argc)) == NULL) { 34 fido_log_debug("%s: cbor_flatten_vector", __func__); 35 goto fail; 36 } 37 38 if ((cbor_len = cbor_serialize_alloc(*param, &cbor, 39 &cbor_alloc_len)) == 0 || cbor_len > SIZE_MAX - sizeof(prefix)) { 40 fido_log_debug("%s: cbor_serialize_alloc", __func__); 41 goto fail; 42 } 43 44 if ((hmac_data->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) { 45 fido_log_debug("%s: malloc", __func__); 46 goto fail; 47 } 48 49 memcpy(hmac_data->ptr, prefix, sizeof(prefix)); 50 memcpy(hmac_data->ptr + sizeof(prefix), cbor, cbor_len); 51 hmac_data->len = cbor_len + sizeof(prefix); 52 53 ok = 0; 54 fail: 55 free(cbor); 56 57 return (ok); 58 } 59 60 static int 61 bio_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **sub_argv, size_t sub_argc, 62 const char *pin, const fido_blob_t *token, int *ms) 63 { 64 cbor_item_t *argv[5]; 65 es256_pk_t *pk = NULL; 66 fido_blob_t *ecdh = NULL; 67 fido_blob_t f; 68 fido_blob_t hmac; 69 const uint8_t cmd = CTAP_CBOR_BIO_ENROLL_PRE; 70 int r = FIDO_ERR_INTERNAL; 71 72 memset(&f, 0, sizeof(f)); 73 memset(&hmac, 0, sizeof(hmac)); 74 memset(&argv, 0, sizeof(argv)); 75 76 /* modality, subCommand */ 77 if ((argv[0] = cbor_build_uint8(1)) == NULL || 78 (argv[1] = cbor_build_uint8(subcmd)) == NULL) { 79 fido_log_debug("%s: cbor encode", __func__); 80 goto fail; 81 } 82 83 /* subParams */ 84 if (pin || token) { 85 if (bio_prepare_hmac(subcmd, sub_argv, sub_argc, &argv[2], 86 &hmac) < 0) { 87 fido_log_debug("%s: bio_prepare_hmac", __func__); 88 goto fail; 89 } 90 } 91 92 /* pinProtocol, pinAuth */ 93 if (pin) { 94 if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) { 95 fido_log_debug("%s: fido_do_ecdh", __func__); 96 goto fail; 97 } 98 if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin, 99 NULL, &argv[4], &argv[3], ms)) != FIDO_OK) { 100 fido_log_debug("%s: cbor_add_uv_params", __func__); 101 goto fail; 102 } 103 } else if (token) { 104 if ((argv[3] = cbor_encode_pin_opt(dev)) == NULL || 105 (argv[4] = cbor_encode_pin_auth(dev, token, &hmac)) == NULL) { 106 fido_log_debug("%s: encode pin", __func__); 107 goto fail; 108 } 109 } 110 111 /* framing and transmission */ 112 if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || 113 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) { 114 fido_log_debug("%s: fido_tx", __func__); 115 r = FIDO_ERR_TX; 116 goto fail; 117 } 118 119 r = FIDO_OK; 120 fail: 121 cbor_vector_free(argv, nitems(argv)); 122 es256_pk_free(&pk); 123 fido_blob_free(&ecdh); 124 free(f.ptr); 125 free(hmac.ptr); 126 127 return (r); 128 } 129 130 static void 131 bio_reset_template(fido_bio_template_t *t) 132 { 133 free(t->name); 134 t->name = NULL; 135 fido_blob_reset(&t->id); 136 } 137 138 static void 139 bio_reset_template_array(fido_bio_template_array_t *ta) 140 { 141 for (size_t i = 0; i < ta->n_alloc; i++) 142 bio_reset_template(&ta->ptr[i]); 143 144 free(ta->ptr); 145 ta->ptr = NULL; 146 memset(ta, 0, sizeof(*ta)); 147 } 148 149 static int 150 decode_template(const cbor_item_t *key, const cbor_item_t *val, void *arg) 151 { 152 fido_bio_template_t *t = arg; 153 154 if (cbor_isa_uint(key) == false || 155 cbor_int_get_width(key) != CBOR_INT_8) { 156 fido_log_debug("%s: cbor type", __func__); 157 return (0); /* ignore */ 158 } 159 160 switch (cbor_get_uint8(key)) { 161 case 1: /* id */ 162 return (fido_blob_decode(val, &t->id)); 163 case 2: /* name */ 164 return (cbor_string_copy(val, &t->name)); 165 } 166 167 return (0); /* ignore */ 168 } 169 170 static int 171 decode_template_array(const cbor_item_t *item, void *arg) 172 { 173 fido_bio_template_array_t *ta = arg; 174 175 if (cbor_isa_map(item) == false || 176 cbor_map_is_definite(item) == false) { 177 fido_log_debug("%s: cbor type", __func__); 178 return (-1); 179 } 180 181 if (ta->n_rx >= ta->n_alloc) { 182 fido_log_debug("%s: n_rx >= n_alloc", __func__); 183 return (-1); 184 } 185 186 if (cbor_map_iter(item, &ta->ptr[ta->n_rx], decode_template) < 0) { 187 fido_log_debug("%s: decode_template", __func__); 188 return (-1); 189 } 190 191 ta->n_rx++; 192 193 return (0); 194 } 195 196 static int 197 bio_parse_template_array(const cbor_item_t *key, const cbor_item_t *val, 198 void *arg) 199 { 200 fido_bio_template_array_t *ta = arg; 201 202 if (cbor_isa_uint(key) == false || 203 cbor_int_get_width(key) != CBOR_INT_8 || 204 cbor_get_uint8(key) != 7) { 205 fido_log_debug("%s: cbor type", __func__); 206 return (0); /* ignore */ 207 } 208 209 if (cbor_isa_array(val) == false || 210 cbor_array_is_definite(val) == false) { 211 fido_log_debug("%s: cbor type", __func__); 212 return (-1); 213 } 214 215 if (ta->ptr != NULL || ta->n_alloc != 0 || ta->n_rx != 0) { 216 fido_log_debug("%s: ptr != NULL || n_alloc != 0 || n_rx != 0", 217 __func__); 218 return (-1); 219 } 220 221 if ((ta->ptr = calloc(cbor_array_size(val), sizeof(*ta->ptr))) == NULL) 222 return (-1); 223 224 ta->n_alloc = cbor_array_size(val); 225 226 if (cbor_array_iter(val, ta, decode_template_array) < 0) { 227 fido_log_debug("%s: decode_template_array", __func__); 228 return (-1); 229 } 230 231 return (0); 232 } 233 234 static int 235 bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int *ms) 236 { 237 unsigned char *msg; 238 int msglen; 239 int r; 240 241 bio_reset_template_array(ta); 242 243 if ((msg = malloc(FIDO_MAXMSG)) == NULL) { 244 r = FIDO_ERR_INTERNAL; 245 goto out; 246 } 247 248 if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { 249 fido_log_debug("%s: fido_rx", __func__); 250 r = FIDO_ERR_RX; 251 goto out; 252 } 253 254 if ((r = cbor_parse_reply(msg, (size_t)msglen, ta, 255 bio_parse_template_array)) != FIDO_OK) { 256 fido_log_debug("%s: bio_parse_template_array" , __func__); 257 goto out; 258 } 259 260 r = FIDO_OK; 261 out: 262 freezero(msg, FIDO_MAXMSG); 263 264 return (r); 265 } 266 267 static int 268 bio_get_template_array_wait(fido_dev_t *dev, fido_bio_template_array_t *ta, 269 const char *pin, int *ms) 270 { 271 int r; 272 273 if ((r = bio_tx(dev, CMD_ENUM, NULL, 0, pin, NULL, ms)) != FIDO_OK || 274 (r = bio_rx_template_array(dev, ta, ms)) != FIDO_OK) 275 return (r); 276 277 return (FIDO_OK); 278 } 279 280 int 281 fido_bio_dev_get_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, 282 const char *pin) 283 { 284 int ms = dev->timeout_ms; 285 286 if (pin == NULL) 287 return (FIDO_ERR_INVALID_ARGUMENT); 288 289 return (bio_get_template_array_wait(dev, ta, pin, &ms)); 290 } 291 292 static int 293 bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t, 294 const char *pin, int *ms) 295 { 296 cbor_item_t *argv[2]; 297 int r = FIDO_ERR_INTERNAL; 298 299 memset(&argv, 0, sizeof(argv)); 300 301 if ((argv[0] = fido_blob_encode(&t->id)) == NULL || 302 (argv[1] = cbor_build_string(t->name)) == NULL) { 303 fido_log_debug("%s: cbor encode", __func__); 304 goto fail; 305 } 306 307 if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL, 308 ms)) != FIDO_OK || 309 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { 310 fido_log_debug("%s: tx/rx", __func__); 311 goto fail; 312 } 313 314 r = FIDO_OK; 315 fail: 316 cbor_vector_free(argv, nitems(argv)); 317 318 return (r); 319 } 320 321 int 322 fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t, 323 const char *pin) 324 { 325 int ms = dev->timeout_ms; 326 327 if (pin == NULL || t->name == NULL) 328 return (FIDO_ERR_INVALID_ARGUMENT); 329 330 return (bio_set_template_name_wait(dev, t, pin, &ms)); 331 } 332 333 static void 334 bio_reset_enroll(fido_bio_enroll_t *e) 335 { 336 e->remaining_samples = 0; 337 e->last_status = 0; 338 339 if (e->token) 340 fido_blob_free(&e->token); 341 } 342 343 static int 344 bio_parse_enroll_status(const cbor_item_t *key, const cbor_item_t *val, 345 void *arg) 346 { 347 fido_bio_enroll_t *e = arg; 348 uint64_t x; 349 350 if (cbor_isa_uint(key) == false || 351 cbor_int_get_width(key) != CBOR_INT_8) { 352 fido_log_debug("%s: cbor type", __func__); 353 return (0); /* ignore */ 354 } 355 356 switch (cbor_get_uint8(key)) { 357 case 5: 358 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { 359 fido_log_debug("%s: cbor_decode_uint64", __func__); 360 return (-1); 361 } 362 e->last_status = (uint8_t)x; 363 break; 364 case 6: 365 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { 366 fido_log_debug("%s: cbor_decode_uint64", __func__); 367 return (-1); 368 } 369 e->remaining_samples = (uint8_t)x; 370 break; 371 default: 372 return (0); /* ignore */ 373 } 374 375 return (0); 376 } 377 378 static int 379 bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val, 380 void *arg) 381 { 382 fido_blob_t *id = arg; 383 384 if (cbor_isa_uint(key) == false || 385 cbor_int_get_width(key) != CBOR_INT_8 || 386 cbor_get_uint8(key) != 4) { 387 fido_log_debug("%s: cbor type", __func__); 388 return (0); /* ignore */ 389 } 390 391 return (fido_blob_decode(val, id)); 392 } 393 394 static int 395 bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t, 396 fido_bio_enroll_t *e, int *ms) 397 { 398 unsigned char *msg; 399 int msglen; 400 int r; 401 402 bio_reset_template(t); 403 404 e->remaining_samples = 0; 405 e->last_status = 0; 406 407 if ((msg = malloc(FIDO_MAXMSG)) == NULL) { 408 r = FIDO_ERR_INTERNAL; 409 goto out; 410 } 411 412 if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { 413 fido_log_debug("%s: fido_rx", __func__); 414 r = FIDO_ERR_RX; 415 goto out; 416 } 417 418 if ((r = cbor_parse_reply(msg, (size_t)msglen, e, 419 bio_parse_enroll_status)) != FIDO_OK) { 420 fido_log_debug("%s: bio_parse_enroll_status", __func__); 421 goto out; 422 } 423 424 if ((r = cbor_parse_reply(msg, (size_t)msglen, &t->id, 425 bio_parse_template_id)) != FIDO_OK) { 426 fido_log_debug("%s: bio_parse_template_id", __func__); 427 goto out; 428 } 429 430 r = FIDO_OK; 431 out: 432 freezero(msg, FIDO_MAXMSG); 433 434 return (r); 435 } 436 437 static int 438 bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t, 439 fido_bio_enroll_t *e, uint32_t timo_ms, int *ms) 440 { 441 cbor_item_t *argv[3]; 442 const uint8_t cmd = CMD_ENROLL_BEGIN; 443 int r = FIDO_ERR_INTERNAL; 444 445 memset(&argv, 0, sizeof(argv)); 446 447 if ((argv[2] = cbor_build_uint(timo_ms)) == NULL) { 448 fido_log_debug("%s: cbor encode", __func__); 449 goto fail; 450 } 451 452 if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != FIDO_OK || 453 (r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) { 454 fido_log_debug("%s: tx/rx", __func__); 455 goto fail; 456 } 457 458 r = FIDO_OK; 459 fail: 460 cbor_vector_free(argv, nitems(argv)); 461 462 return (r); 463 } 464 465 int 466 fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t, 467 fido_bio_enroll_t *e, uint32_t timo_ms, const char *pin) 468 { 469 es256_pk_t *pk = NULL; 470 fido_blob_t *ecdh = NULL; 471 fido_blob_t *token = NULL; 472 int ms = dev->timeout_ms; 473 int r; 474 475 if (pin == NULL || e->token != NULL) 476 return (FIDO_ERR_INVALID_ARGUMENT); 477 478 if ((token = fido_blob_new()) == NULL) { 479 r = FIDO_ERR_INTERNAL; 480 goto fail; 481 } 482 483 if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) { 484 fido_log_debug("%s: fido_do_ecdh", __func__); 485 goto fail; 486 } 487 488 if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_BIO_ENROLL_PRE, pin, ecdh, 489 pk, NULL, token, &ms)) != FIDO_OK) { 490 fido_log_debug("%s: fido_dev_get_uv_token", __func__); 491 goto fail; 492 } 493 494 e->token = token; 495 token = NULL; 496 fail: 497 es256_pk_free(&pk); 498 fido_blob_free(&ecdh); 499 fido_blob_free(&token); 500 501 if (r != FIDO_OK) 502 return (r); 503 504 return (bio_enroll_begin_wait(dev, t, e, timo_ms, &ms)); 505 } 506 507 static int 508 bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int *ms) 509 { 510 unsigned char *msg; 511 int msglen; 512 int r; 513 514 e->remaining_samples = 0; 515 e->last_status = 0; 516 517 if ((msg = malloc(FIDO_MAXMSG)) == NULL) { 518 r = FIDO_ERR_INTERNAL; 519 goto out; 520 } 521 522 if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { 523 fido_log_debug("%s: fido_rx", __func__); 524 r = FIDO_ERR_RX; 525 goto out; 526 } 527 528 if ((r = cbor_parse_reply(msg, (size_t)msglen, e, 529 bio_parse_enroll_status)) != FIDO_OK) { 530 fido_log_debug("%s: bio_parse_enroll_status", __func__); 531 goto out; 532 } 533 534 r = FIDO_OK; 535 out: 536 freezero(msg, FIDO_MAXMSG); 537 538 return (r); 539 } 540 541 static int 542 bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t, 543 fido_bio_enroll_t *e, uint32_t timo_ms, int *ms) 544 { 545 cbor_item_t *argv[3]; 546 const uint8_t cmd = CMD_ENROLL_NEXT; 547 int r = FIDO_ERR_INTERNAL; 548 549 memset(&argv, 0, sizeof(argv)); 550 551 if ((argv[0] = fido_blob_encode(&t->id)) == NULL || 552 (argv[2] = cbor_build_uint(timo_ms)) == NULL) { 553 fido_log_debug("%s: cbor encode", __func__); 554 goto fail; 555 } 556 557 if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != FIDO_OK || 558 (r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) { 559 fido_log_debug("%s: tx/rx", __func__); 560 goto fail; 561 } 562 563 r = FIDO_OK; 564 fail: 565 cbor_vector_free(argv, nitems(argv)); 566 567 return (r); 568 } 569 570 int 571 fido_bio_dev_enroll_continue(fido_dev_t *dev, const fido_bio_template_t *t, 572 fido_bio_enroll_t *e, uint32_t timo_ms) 573 { 574 int ms = dev->timeout_ms; 575 576 if (e->token == NULL) 577 return (FIDO_ERR_INVALID_ARGUMENT); 578 579 return (bio_enroll_continue_wait(dev, t, e, timo_ms, &ms)); 580 } 581 582 static int 583 bio_enroll_cancel_wait(fido_dev_t *dev, int *ms) 584 { 585 const uint8_t cmd = CMD_ENROLL_CANCEL; 586 int r; 587 588 if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL, ms)) != FIDO_OK || 589 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { 590 fido_log_debug("%s: tx/rx", __func__); 591 return (r); 592 } 593 594 return (FIDO_OK); 595 } 596 597 int 598 fido_bio_dev_enroll_cancel(fido_dev_t *dev) 599 { 600 int ms = dev->timeout_ms; 601 602 return (bio_enroll_cancel_wait(dev, &ms)); 603 } 604 605 static int 606 bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t, 607 const char *pin, int *ms) 608 { 609 cbor_item_t *argv[1]; 610 const uint8_t cmd = CMD_ENROLL_REMOVE; 611 int r = FIDO_ERR_INTERNAL; 612 613 memset(&argv, 0, sizeof(argv)); 614 615 if ((argv[0] = fido_blob_encode(&t->id)) == NULL) { 616 fido_log_debug("%s: cbor encode", __func__); 617 goto fail; 618 } 619 620 if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL, ms)) != FIDO_OK || 621 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { 622 fido_log_debug("%s: tx/rx", __func__); 623 goto fail; 624 } 625 626 r = FIDO_OK; 627 fail: 628 cbor_vector_free(argv, nitems(argv)); 629 630 return (r); 631 } 632 633 int 634 fido_bio_dev_enroll_remove(fido_dev_t *dev, const fido_bio_template_t *t, 635 const char *pin) 636 { 637 int ms = dev->timeout_ms; 638 639 return (bio_enroll_remove_wait(dev, t, pin, &ms)); 640 } 641 642 static void 643 bio_reset_info(fido_bio_info_t *i) 644 { 645 i->type = 0; 646 i->max_samples = 0; 647 } 648 649 static int 650 bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg) 651 { 652 fido_bio_info_t *i = arg; 653 uint64_t x; 654 655 if (cbor_isa_uint(key) == false || 656 cbor_int_get_width(key) != CBOR_INT_8) { 657 fido_log_debug("%s: cbor type", __func__); 658 return (0); /* ignore */ 659 } 660 661 switch (cbor_get_uint8(key)) { 662 case 2: 663 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { 664 fido_log_debug("%s: cbor_decode_uint64", __func__); 665 return (-1); 666 } 667 i->type = (uint8_t)x; 668 break; 669 case 3: 670 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { 671 fido_log_debug("%s: cbor_decode_uint64", __func__); 672 return (-1); 673 } 674 i->max_samples = (uint8_t)x; 675 break; 676 default: 677 return (0); /* ignore */ 678 } 679 680 return (0); 681 } 682 683 static int 684 bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int *ms) 685 { 686 unsigned char *msg; 687 int msglen; 688 int r; 689 690 bio_reset_info(i); 691 692 if ((msg = malloc(FIDO_MAXMSG)) == NULL) { 693 r = FIDO_ERR_INTERNAL; 694 goto out; 695 } 696 697 if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { 698 fido_log_debug("%s: fido_rx", __func__); 699 r = FIDO_ERR_RX; 700 goto out; 701 } 702 703 if ((r = cbor_parse_reply(msg, (size_t)msglen, i, 704 bio_parse_info)) != FIDO_OK) { 705 fido_log_debug("%s: bio_parse_info" , __func__); 706 goto out; 707 } 708 709 r = FIDO_OK; 710 out: 711 freezero(msg, FIDO_MAXMSG); 712 713 return (r); 714 } 715 716 static int 717 bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int *ms) 718 { 719 int r; 720 721 if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL, 722 ms)) != FIDO_OK || 723 (r = bio_rx_info(dev, i, ms)) != FIDO_OK) { 724 fido_log_debug("%s: tx/rx", __func__); 725 return (r); 726 } 727 728 return (FIDO_OK); 729 } 730 731 int 732 fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i) 733 { 734 int ms = dev->timeout_ms; 735 736 return (bio_get_info_wait(dev, i, &ms)); 737 } 738 739 const char * 740 fido_bio_template_name(const fido_bio_template_t *t) 741 { 742 return (t->name); 743 } 744 745 const unsigned char * 746 fido_bio_template_id_ptr(const fido_bio_template_t *t) 747 { 748 return (t->id.ptr); 749 } 750 751 size_t 752 fido_bio_template_id_len(const fido_bio_template_t *t) 753 { 754 return (t->id.len); 755 } 756 757 size_t 758 fido_bio_template_array_count(const fido_bio_template_array_t *ta) 759 { 760 return (ta->n_rx); 761 } 762 763 fido_bio_template_array_t * 764 fido_bio_template_array_new(void) 765 { 766 return (calloc(1, sizeof(fido_bio_template_array_t))); 767 } 768 769 fido_bio_template_t * 770 fido_bio_template_new(void) 771 { 772 return (calloc(1, sizeof(fido_bio_template_t))); 773 } 774 775 void 776 fido_bio_template_array_free(fido_bio_template_array_t **tap) 777 { 778 fido_bio_template_array_t *ta; 779 780 if (tap == NULL || (ta = *tap) == NULL) 781 return; 782 783 bio_reset_template_array(ta); 784 free(ta); 785 *tap = NULL; 786 } 787 788 void 789 fido_bio_template_free(fido_bio_template_t **tp) 790 { 791 fido_bio_template_t *t; 792 793 if (tp == NULL || (t = *tp) == NULL) 794 return; 795 796 bio_reset_template(t); 797 free(t); 798 *tp = NULL; 799 } 800 801 int 802 fido_bio_template_set_name(fido_bio_template_t *t, const char *name) 803 { 804 free(t->name); 805 t->name = NULL; 806 807 if (name && (t->name = strdup(name)) == NULL) 808 return (FIDO_ERR_INTERNAL); 809 810 return (FIDO_OK); 811 } 812 813 int 814 fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr, 815 size_t len) 816 { 817 fido_blob_reset(&t->id); 818 819 if (ptr && fido_blob_set(&t->id, ptr, len) < 0) 820 return (FIDO_ERR_INTERNAL); 821 822 return (FIDO_OK); 823 } 824 825 const fido_bio_template_t * 826 fido_bio_template(const fido_bio_template_array_t *ta, size_t idx) 827 { 828 if (idx >= ta->n_alloc) 829 return (NULL); 830 831 return (&ta->ptr[idx]); 832 } 833 834 fido_bio_enroll_t * 835 fido_bio_enroll_new(void) 836 { 837 return (calloc(1, sizeof(fido_bio_enroll_t))); 838 } 839 840 fido_bio_info_t * 841 fido_bio_info_new(void) 842 { 843 return (calloc(1, sizeof(fido_bio_info_t))); 844 } 845 846 uint8_t 847 fido_bio_info_type(const fido_bio_info_t *i) 848 { 849 return (i->type); 850 } 851 852 uint8_t 853 fido_bio_info_max_samples(const fido_bio_info_t *i) 854 { 855 return (i->max_samples); 856 } 857 858 void 859 fido_bio_enroll_free(fido_bio_enroll_t **ep) 860 { 861 fido_bio_enroll_t *e; 862 863 if (ep == NULL || (e = *ep) == NULL) 864 return; 865 866 bio_reset_enroll(e); 867 868 free(e); 869 *ep = NULL; 870 } 871 872 void 873 fido_bio_info_free(fido_bio_info_t **ip) 874 { 875 fido_bio_info_t *i; 876 877 if (ip == NULL || (i = *ip) == NULL) 878 return; 879 880 free(i); 881 *ip = NULL; 882 } 883 884 uint8_t 885 fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e) 886 { 887 return (e->remaining_samples); 888 } 889 890 uint8_t 891 fido_bio_enroll_last_status(const fido_bio_enroll_t *e) 892 { 893 return (e->last_status); 894 } 895