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