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, int *ms) 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, ms)) != 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], ms)) != 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, ms) < 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, ms)) != 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 int ms = dev->timeout_ms; 275 276 if (pin == NULL) 277 return (FIDO_ERR_INVALID_ARGUMENT); 278 279 return (bio_get_template_array_wait(dev, ta, pin, &ms)); 280 } 281 282 static int 283 bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t, 284 const char *pin, int *ms) 285 { 286 cbor_item_t *argv[2]; 287 int r = FIDO_ERR_INTERNAL; 288 289 memset(&argv, 0, sizeof(argv)); 290 291 if ((argv[0] = fido_blob_encode(&t->id)) == NULL || 292 (argv[1] = cbor_build_string(t->name)) == NULL) { 293 fido_log_debug("%s: cbor encode", __func__); 294 goto fail; 295 } 296 297 if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL, 298 ms)) != FIDO_OK || 299 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { 300 fido_log_debug("%s: tx/rx", __func__); 301 goto fail; 302 } 303 304 r = FIDO_OK; 305 fail: 306 cbor_vector_free(argv, nitems(argv)); 307 308 return (r); 309 } 310 311 int 312 fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t, 313 const char *pin) 314 { 315 int ms = dev->timeout_ms; 316 317 if (pin == NULL || t->name == NULL) 318 return (FIDO_ERR_INVALID_ARGUMENT); 319 320 return (bio_set_template_name_wait(dev, t, pin, &ms)); 321 } 322 323 static void 324 bio_reset_enroll(fido_bio_enroll_t *e) 325 { 326 e->remaining_samples = 0; 327 e->last_status = 0; 328 329 if (e->token) 330 fido_blob_free(&e->token); 331 } 332 333 static int 334 bio_parse_enroll_status(const cbor_item_t *key, const cbor_item_t *val, 335 void *arg) 336 { 337 fido_bio_enroll_t *e = arg; 338 uint64_t x; 339 340 if (cbor_isa_uint(key) == false || 341 cbor_int_get_width(key) != CBOR_INT_8) { 342 fido_log_debug("%s: cbor type", __func__); 343 return (0); /* ignore */ 344 } 345 346 switch (cbor_get_uint8(key)) { 347 case 5: 348 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { 349 fido_log_debug("%s: cbor_decode_uint64", __func__); 350 return (-1); 351 } 352 e->last_status = (uint8_t)x; 353 break; 354 case 6: 355 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { 356 fido_log_debug("%s: cbor_decode_uint64", __func__); 357 return (-1); 358 } 359 e->remaining_samples = (uint8_t)x; 360 break; 361 default: 362 return (0); /* ignore */ 363 } 364 365 return (0); 366 } 367 368 static int 369 bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val, 370 void *arg) 371 { 372 fido_blob_t *id = arg; 373 374 if (cbor_isa_uint(key) == false || 375 cbor_int_get_width(key) != CBOR_INT_8 || 376 cbor_get_uint8(key) != 4) { 377 fido_log_debug("%s: cbor type", __func__); 378 return (0); /* ignore */ 379 } 380 381 return (fido_blob_decode(val, id)); 382 } 383 384 static int 385 bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t, 386 fido_bio_enroll_t *e, int *ms) 387 { 388 unsigned char reply[FIDO_MAXMSG]; 389 int reply_len; 390 int r; 391 392 bio_reset_template(t); 393 394 e->remaining_samples = 0; 395 e->last_status = 0; 396 397 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 398 ms)) < 0) { 399 fido_log_debug("%s: fido_rx", __func__); 400 return (FIDO_ERR_RX); 401 } 402 403 if ((r = cbor_parse_reply(reply, (size_t)reply_len, e, 404 bio_parse_enroll_status)) != FIDO_OK) { 405 fido_log_debug("%s: bio_parse_enroll_status", __func__); 406 return (r); 407 } 408 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &t->id, 409 bio_parse_template_id)) != FIDO_OK) { 410 fido_log_debug("%s: bio_parse_template_id", __func__); 411 return (r); 412 } 413 414 return (FIDO_OK); 415 } 416 417 static int 418 bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t, 419 fido_bio_enroll_t *e, uint32_t timo_ms, int *ms) 420 { 421 cbor_item_t *argv[3]; 422 const uint8_t cmd = CMD_ENROLL_BEGIN; 423 int r = FIDO_ERR_INTERNAL; 424 425 memset(&argv, 0, sizeof(argv)); 426 427 if ((argv[2] = cbor_build_uint(timo_ms)) == NULL) { 428 fido_log_debug("%s: cbor encode", __func__); 429 goto fail; 430 } 431 432 if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != FIDO_OK || 433 (r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) { 434 fido_log_debug("%s: tx/rx", __func__); 435 goto fail; 436 } 437 438 r = FIDO_OK; 439 fail: 440 cbor_vector_free(argv, nitems(argv)); 441 442 return (r); 443 } 444 445 int 446 fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t, 447 fido_bio_enroll_t *e, uint32_t timo_ms, const char *pin) 448 { 449 es256_pk_t *pk = NULL; 450 fido_blob_t *ecdh = NULL; 451 fido_blob_t *token = NULL; 452 int ms = dev->timeout_ms; 453 int r; 454 455 if (pin == NULL || e->token != NULL) 456 return (FIDO_ERR_INVALID_ARGUMENT); 457 458 if ((token = fido_blob_new()) == NULL) { 459 r = FIDO_ERR_INTERNAL; 460 goto fail; 461 } 462 463 if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) { 464 fido_log_debug("%s: fido_do_ecdh", __func__); 465 goto fail; 466 } 467 468 if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_BIO_ENROLL_PRE, pin, ecdh, 469 pk, NULL, token, &ms)) != FIDO_OK) { 470 fido_log_debug("%s: fido_dev_get_uv_token", __func__); 471 goto fail; 472 } 473 474 e->token = token; 475 token = NULL; 476 fail: 477 es256_pk_free(&pk); 478 fido_blob_free(&ecdh); 479 fido_blob_free(&token); 480 481 if (r != FIDO_OK) 482 return (r); 483 484 return (bio_enroll_begin_wait(dev, t, e, timo_ms, &ms)); 485 } 486 487 static int 488 bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int *ms) 489 { 490 unsigned char reply[FIDO_MAXMSG]; 491 int reply_len; 492 int r; 493 494 e->remaining_samples = 0; 495 e->last_status = 0; 496 497 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 498 ms)) < 0) { 499 fido_log_debug("%s: fido_rx", __func__); 500 return (FIDO_ERR_RX); 501 } 502 503 if ((r = cbor_parse_reply(reply, (size_t)reply_len, e, 504 bio_parse_enroll_status)) != FIDO_OK) { 505 fido_log_debug("%s: bio_parse_enroll_status", __func__); 506 return (r); 507 } 508 509 return (FIDO_OK); 510 } 511 512 static int 513 bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t, 514 fido_bio_enroll_t *e, uint32_t timo_ms, int *ms) 515 { 516 cbor_item_t *argv[3]; 517 const uint8_t cmd = CMD_ENROLL_NEXT; 518 int r = FIDO_ERR_INTERNAL; 519 520 memset(&argv, 0, sizeof(argv)); 521 522 if ((argv[0] = fido_blob_encode(&t->id)) == NULL || 523 (argv[2] = cbor_build_uint(timo_ms)) == NULL) { 524 fido_log_debug("%s: cbor encode", __func__); 525 goto fail; 526 } 527 528 if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != FIDO_OK || 529 (r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) { 530 fido_log_debug("%s: tx/rx", __func__); 531 goto fail; 532 } 533 534 r = FIDO_OK; 535 fail: 536 cbor_vector_free(argv, nitems(argv)); 537 538 return (r); 539 } 540 541 int 542 fido_bio_dev_enroll_continue(fido_dev_t *dev, const fido_bio_template_t *t, 543 fido_bio_enroll_t *e, uint32_t timo_ms) 544 { 545 int ms = dev->timeout_ms; 546 547 if (e->token == NULL) 548 return (FIDO_ERR_INVALID_ARGUMENT); 549 550 return (bio_enroll_continue_wait(dev, t, e, timo_ms, &ms)); 551 } 552 553 static int 554 bio_enroll_cancel_wait(fido_dev_t *dev, int *ms) 555 { 556 const uint8_t cmd = CMD_ENROLL_CANCEL; 557 int r; 558 559 if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL, ms)) != FIDO_OK || 560 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { 561 fido_log_debug("%s: tx/rx", __func__); 562 return (r); 563 } 564 565 return (FIDO_OK); 566 } 567 568 int 569 fido_bio_dev_enroll_cancel(fido_dev_t *dev) 570 { 571 int ms = dev->timeout_ms; 572 573 return (bio_enroll_cancel_wait(dev, &ms)); 574 } 575 576 static int 577 bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t, 578 const char *pin, int *ms) 579 { 580 cbor_item_t *argv[1]; 581 const uint8_t cmd = CMD_ENROLL_REMOVE; 582 int r = FIDO_ERR_INTERNAL; 583 584 memset(&argv, 0, sizeof(argv)); 585 586 if ((argv[0] = fido_blob_encode(&t->id)) == NULL) { 587 fido_log_debug("%s: cbor encode", __func__); 588 goto fail; 589 } 590 591 if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL, ms)) != FIDO_OK || 592 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { 593 fido_log_debug("%s: tx/rx", __func__); 594 goto fail; 595 } 596 597 r = FIDO_OK; 598 fail: 599 cbor_vector_free(argv, nitems(argv)); 600 601 return (r); 602 } 603 604 int 605 fido_bio_dev_enroll_remove(fido_dev_t *dev, const fido_bio_template_t *t, 606 const char *pin) 607 { 608 int ms = dev->timeout_ms; 609 610 return (bio_enroll_remove_wait(dev, t, pin, &ms)); 611 } 612 613 static void 614 bio_reset_info(fido_bio_info_t *i) 615 { 616 i->type = 0; 617 i->max_samples = 0; 618 } 619 620 static int 621 bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg) 622 { 623 fido_bio_info_t *i = arg; 624 uint64_t x; 625 626 if (cbor_isa_uint(key) == false || 627 cbor_int_get_width(key) != CBOR_INT_8) { 628 fido_log_debug("%s: cbor type", __func__); 629 return (0); /* ignore */ 630 } 631 632 switch (cbor_get_uint8(key)) { 633 case 2: 634 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { 635 fido_log_debug("%s: cbor_decode_uint64", __func__); 636 return (-1); 637 } 638 i->type = (uint8_t)x; 639 break; 640 case 3: 641 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) { 642 fido_log_debug("%s: cbor_decode_uint64", __func__); 643 return (-1); 644 } 645 i->max_samples = (uint8_t)x; 646 break; 647 default: 648 return (0); /* ignore */ 649 } 650 651 return (0); 652 } 653 654 static int 655 bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int *ms) 656 { 657 unsigned char reply[FIDO_MAXMSG]; 658 int reply_len; 659 int r; 660 661 bio_reset_info(i); 662 663 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 664 ms)) < 0) { 665 fido_log_debug("%s: fido_rx", __func__); 666 return (FIDO_ERR_RX); 667 } 668 669 if ((r = cbor_parse_reply(reply, (size_t)reply_len, i, 670 bio_parse_info)) != FIDO_OK) { 671 fido_log_debug("%s: bio_parse_info" , __func__); 672 return (r); 673 } 674 675 return (FIDO_OK); 676 } 677 678 static int 679 bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int *ms) 680 { 681 int r; 682 683 if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL, 684 ms)) != FIDO_OK || 685 (r = bio_rx_info(dev, i, ms)) != FIDO_OK) { 686 fido_log_debug("%s: tx/rx", __func__); 687 return (r); 688 } 689 690 return (FIDO_OK); 691 } 692 693 int 694 fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i) 695 { 696 int ms = dev->timeout_ms; 697 698 return (bio_get_info_wait(dev, i, &ms)); 699 } 700 701 const char * 702 fido_bio_template_name(const fido_bio_template_t *t) 703 { 704 return (t->name); 705 } 706 707 const unsigned char * 708 fido_bio_template_id_ptr(const fido_bio_template_t *t) 709 { 710 return (t->id.ptr); 711 } 712 713 size_t 714 fido_bio_template_id_len(const fido_bio_template_t *t) 715 { 716 return (t->id.len); 717 } 718 719 size_t 720 fido_bio_template_array_count(const fido_bio_template_array_t *ta) 721 { 722 return (ta->n_rx); 723 } 724 725 fido_bio_template_array_t * 726 fido_bio_template_array_new(void) 727 { 728 return (calloc(1, sizeof(fido_bio_template_array_t))); 729 } 730 731 fido_bio_template_t * 732 fido_bio_template_new(void) 733 { 734 return (calloc(1, sizeof(fido_bio_template_t))); 735 } 736 737 void 738 fido_bio_template_array_free(fido_bio_template_array_t **tap) 739 { 740 fido_bio_template_array_t *ta; 741 742 if (tap == NULL || (ta = *tap) == NULL) 743 return; 744 745 bio_reset_template_array(ta); 746 free(ta); 747 *tap = NULL; 748 } 749 750 void 751 fido_bio_template_free(fido_bio_template_t **tp) 752 { 753 fido_bio_template_t *t; 754 755 if (tp == NULL || (t = *tp) == NULL) 756 return; 757 758 bio_reset_template(t); 759 free(t); 760 *tp = NULL; 761 } 762 763 int 764 fido_bio_template_set_name(fido_bio_template_t *t, const char *name) 765 { 766 free(t->name); 767 t->name = NULL; 768 769 if (name && (t->name = strdup(name)) == NULL) 770 return (FIDO_ERR_INTERNAL); 771 772 return (FIDO_OK); 773 } 774 775 int 776 fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr, 777 size_t len) 778 { 779 fido_blob_reset(&t->id); 780 781 if (ptr && fido_blob_set(&t->id, ptr, len) < 0) 782 return (FIDO_ERR_INTERNAL); 783 784 return (FIDO_OK); 785 } 786 787 const fido_bio_template_t * 788 fido_bio_template(const fido_bio_template_array_t *ta, size_t idx) 789 { 790 if (idx >= ta->n_alloc) 791 return (NULL); 792 793 return (&ta->ptr[idx]); 794 } 795 796 fido_bio_enroll_t * 797 fido_bio_enroll_new(void) 798 { 799 return (calloc(1, sizeof(fido_bio_enroll_t))); 800 } 801 802 fido_bio_info_t * 803 fido_bio_info_new(void) 804 { 805 return (calloc(1, sizeof(fido_bio_info_t))); 806 } 807 808 uint8_t 809 fido_bio_info_type(const fido_bio_info_t *i) 810 { 811 return (i->type); 812 } 813 814 uint8_t 815 fido_bio_info_max_samples(const fido_bio_info_t *i) 816 { 817 return (i->max_samples); 818 } 819 820 void 821 fido_bio_enroll_free(fido_bio_enroll_t **ep) 822 { 823 fido_bio_enroll_t *e; 824 825 if (ep == NULL || (e = *ep) == NULL) 826 return; 827 828 bio_reset_enroll(e); 829 830 free(e); 831 *ep = NULL; 832 } 833 834 void 835 fido_bio_info_free(fido_bio_info_t **ip) 836 { 837 fido_bio_info_t *i; 838 839 if (ip == NULL || (i = *ip) == NULL) 840 return; 841 842 free(i); 843 *ip = NULL; 844 } 845 846 uint8_t 847 fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e) 848 { 849 return (e->remaining_samples); 850 } 851 852 uint8_t 853 fido_bio_enroll_last_status(const fido_bio_enroll_t *e) 854 { 855 return (e->last_status); 856 } 857