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