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