1 /* 2 * Copyright (c) 2018 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 <openssl/sha.h> 8 #include "fido.h" 9 10 #ifndef TLS 11 #define TLS 12 #endif 13 14 typedef struct dev_manifest_func_node { 15 dev_manifest_func_t manifest_func; 16 struct dev_manifest_func_node *next; 17 } dev_manifest_func_node_t; 18 19 static TLS dev_manifest_func_node_t *manifest_funcs = NULL; 20 static TLS bool disable_u2f_fallback; 21 22 static void 23 find_manifest_func_node(dev_manifest_func_t f, dev_manifest_func_node_t **curr, 24 dev_manifest_func_node_t **prev) 25 { 26 *prev = NULL; 27 *curr = manifest_funcs; 28 29 while (*curr != NULL && (*curr)->manifest_func != f) { 30 *prev = *curr; 31 *curr = (*curr)->next; 32 } 33 } 34 35 #ifdef FIDO_FUZZ 36 static void 37 set_random_report_len(fido_dev_t *dev) 38 { 39 dev->rx_len = CTAP_MIN_REPORT_LEN + 40 uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1); 41 dev->tx_len = CTAP_MIN_REPORT_LEN + 42 uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1); 43 } 44 #endif 45 46 static void 47 fido_dev_set_extension_flags(fido_dev_t *dev, const fido_cbor_info_t *info) 48 { 49 char * const *ptr = fido_cbor_info_extensions_ptr(info); 50 size_t len = fido_cbor_info_extensions_len(info); 51 52 for (size_t i = 0; i < len; i++) 53 if (strcmp(ptr[i], "credProtect") == 0) 54 dev->flags |= FIDO_DEV_CRED_PROT; 55 } 56 57 static void 58 fido_dev_set_option_flags(fido_dev_t *dev, const fido_cbor_info_t *info) 59 { 60 char * const *ptr = fido_cbor_info_options_name_ptr(info); 61 const bool *val = fido_cbor_info_options_value_ptr(info); 62 size_t len = fido_cbor_info_options_len(info); 63 64 for (size_t i = 0; i < len; i++) 65 if (strcmp(ptr[i], "clientPin") == 0) { 66 dev->flags |= val[i] ? FIDO_DEV_PIN_SET : FIDO_DEV_PIN_UNSET; 67 } else if (strcmp(ptr[i], "credMgmt") == 0 || 68 strcmp(ptr[i], "credentialMgmtPreview") == 0) { 69 if (val[i]) 70 dev->flags |= FIDO_DEV_CREDMAN; 71 } else if (strcmp(ptr[i], "uv") == 0) { 72 dev->flags |= val[i] ? FIDO_DEV_UV_SET : FIDO_DEV_UV_UNSET; 73 } else if (strcmp(ptr[i], "pinUvAuthToken") == 0) { 74 if (val[i]) 75 dev->flags |= FIDO_DEV_TOKEN_PERMS; 76 } 77 } 78 79 static void 80 fido_dev_set_protocol_flags(fido_dev_t *dev, const fido_cbor_info_t *info) 81 { 82 const uint8_t *ptr = fido_cbor_info_protocols_ptr(info); 83 size_t len = fido_cbor_info_protocols_len(info); 84 85 for (size_t i = 0; i < len; i++) 86 switch (ptr[i]) { 87 case CTAP_PIN_PROTOCOL1: 88 dev->flags |= FIDO_DEV_PIN_PROTOCOL1; 89 break; 90 case CTAP_PIN_PROTOCOL2: 91 dev->flags |= FIDO_DEV_PIN_PROTOCOL2; 92 break; 93 default: 94 fido_log_debug("%s: unknown protocol %u", __func__, 95 ptr[i]); 96 break; 97 } 98 } 99 100 static void 101 fido_dev_set_flags(fido_dev_t *dev, const fido_cbor_info_t *info) 102 { 103 fido_dev_set_extension_flags(dev, info); 104 fido_dev_set_option_flags(dev, info); 105 fido_dev_set_protocol_flags(dev, info); 106 } 107 108 static int 109 fido_dev_open_tx(fido_dev_t *dev, const char *path, int *ms) 110 { 111 int r; 112 113 if (dev->io_handle != NULL) { 114 fido_log_debug("%s: handle=%p", __func__, dev->io_handle); 115 return (FIDO_ERR_INVALID_ARGUMENT); 116 } 117 118 if (dev->io.open == NULL || dev->io.close == NULL) { 119 fido_log_debug("%s: NULL open/close", __func__); 120 return (FIDO_ERR_INVALID_ARGUMENT); 121 } 122 123 if (dev->cid != CTAP_CID_BROADCAST) { 124 fido_log_debug("%s: cid=0x%x", __func__, dev->cid); 125 return (FIDO_ERR_INVALID_ARGUMENT); 126 } 127 128 if (fido_get_random(&dev->nonce, sizeof(dev->nonce)) < 0) { 129 fido_log_debug("%s: fido_get_random", __func__); 130 return (FIDO_ERR_INTERNAL); 131 } 132 133 if ((dev->io_handle = dev->io.open(path)) == NULL) { 134 fido_log_debug("%s: dev->io.open", __func__); 135 return (FIDO_ERR_INTERNAL); 136 } 137 138 if (dev->io_own) { 139 dev->rx_len = CTAP_MAX_REPORT_LEN; 140 dev->tx_len = CTAP_MAX_REPORT_LEN; 141 } else { 142 dev->rx_len = fido_hid_report_in_len(dev->io_handle); 143 dev->tx_len = fido_hid_report_out_len(dev->io_handle); 144 } 145 146 #ifdef FIDO_FUZZ 147 set_random_report_len(dev); 148 #endif 149 150 if (dev->rx_len < CTAP_MIN_REPORT_LEN || 151 dev->rx_len > CTAP_MAX_REPORT_LEN) { 152 fido_log_debug("%s: invalid rx_len %zu", __func__, dev->rx_len); 153 r = FIDO_ERR_RX; 154 goto fail; 155 } 156 157 if (dev->tx_len < CTAP_MIN_REPORT_LEN || 158 dev->tx_len > CTAP_MAX_REPORT_LEN) { 159 fido_log_debug("%s: invalid tx_len %zu", __func__, dev->tx_len); 160 r = FIDO_ERR_TX; 161 goto fail; 162 } 163 164 if (fido_tx(dev, CTAP_CMD_INIT, &dev->nonce, sizeof(dev->nonce), 165 ms) < 0) { 166 fido_log_debug("%s: fido_tx", __func__); 167 r = FIDO_ERR_TX; 168 goto fail; 169 } 170 171 return (FIDO_OK); 172 fail: 173 dev->io.close(dev->io_handle); 174 dev->io_handle = NULL; 175 176 return (r); 177 } 178 179 static int 180 fido_dev_open_rx(fido_dev_t *dev, int *ms) 181 { 182 fido_cbor_info_t *info = NULL; 183 int reply_len; 184 int r; 185 186 if ((reply_len = fido_rx(dev, CTAP_CMD_INIT, &dev->attr, 187 sizeof(dev->attr), ms)) < 0) { 188 fido_log_debug("%s: fido_rx", __func__); 189 r = FIDO_ERR_RX; 190 goto fail; 191 } 192 193 #ifdef FIDO_FUZZ 194 dev->attr.nonce = dev->nonce; 195 #endif 196 197 if ((size_t)reply_len != sizeof(dev->attr) || 198 dev->attr.nonce != dev->nonce) { 199 fido_log_debug("%s: invalid nonce", __func__); 200 r = FIDO_ERR_RX; 201 goto fail; 202 } 203 204 dev->flags = 0; 205 dev->cid = dev->attr.cid; 206 207 if (fido_dev_is_fido2(dev)) { 208 if ((info = fido_cbor_info_new()) == NULL) { 209 fido_log_debug("%s: fido_cbor_info_new", __func__); 210 r = FIDO_ERR_INTERNAL; 211 goto fail; 212 } 213 if ((r = fido_dev_get_cbor_info_wait(dev, info, 214 ms)) != FIDO_OK) { 215 fido_log_debug("%s: fido_dev_cbor_info_wait: %d", 216 __func__, r); 217 if (disable_u2f_fallback) 218 goto fail; 219 fido_log_debug("%s: falling back to u2f", __func__); 220 fido_dev_force_u2f(dev); 221 } else { 222 fido_dev_set_flags(dev, info); 223 } 224 } 225 226 if (fido_dev_is_fido2(dev) && info != NULL) { 227 dev->maxmsgsize = fido_cbor_info_maxmsgsiz(info); 228 fido_log_debug("%s: FIDO_MAXMSG=%d, maxmsgsiz=%lu", __func__, 229 FIDO_MAXMSG, (unsigned long)dev->maxmsgsize); 230 } 231 232 r = FIDO_OK; 233 fail: 234 fido_cbor_info_free(&info); 235 236 if (r != FIDO_OK) { 237 dev->io.close(dev->io_handle); 238 dev->io_handle = NULL; 239 } 240 241 return (r); 242 } 243 244 static int 245 fido_dev_open_wait(fido_dev_t *dev, const char *path, int *ms) 246 { 247 int r; 248 249 #ifdef USE_WINHELLO 250 if (strcmp(path, FIDO_WINHELLO_PATH) == 0) 251 return (fido_winhello_open(dev)); 252 #endif 253 if ((r = fido_dev_open_tx(dev, path, ms)) != FIDO_OK || 254 (r = fido_dev_open_rx(dev, ms)) != FIDO_OK) 255 return (r); 256 257 return (FIDO_OK); 258 } 259 260 int 261 fido_dev_register_manifest_func(const dev_manifest_func_t f) 262 { 263 dev_manifest_func_node_t *prev, *curr, *n; 264 265 find_manifest_func_node(f, &curr, &prev); 266 if (curr != NULL) 267 return (FIDO_OK); 268 269 if ((n = calloc(1, sizeof(*n))) == NULL) { 270 fido_log_debug("%s: calloc", __func__); 271 return (FIDO_ERR_INTERNAL); 272 } 273 274 n->manifest_func = f; 275 n->next = manifest_funcs; 276 manifest_funcs = n; 277 278 return (FIDO_OK); 279 } 280 281 void 282 fido_dev_unregister_manifest_func(const dev_manifest_func_t f) 283 { 284 dev_manifest_func_node_t *prev, *curr; 285 286 find_manifest_func_node(f, &curr, &prev); 287 if (curr == NULL) 288 return; 289 if (prev != NULL) 290 prev->next = curr->next; 291 else 292 manifest_funcs = curr->next; 293 294 free(curr); 295 } 296 297 int 298 fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 299 { 300 dev_manifest_func_node_t *curr = NULL; 301 dev_manifest_func_t m_func; 302 size_t curr_olen; 303 int r; 304 305 *olen = 0; 306 307 if (fido_dev_register_manifest_func(fido_hid_manifest) != FIDO_OK) 308 return (FIDO_ERR_INTERNAL); 309 #ifdef NFC_LINUX 310 if (fido_dev_register_manifest_func(fido_nfc_manifest) != FIDO_OK) 311 return (FIDO_ERR_INTERNAL); 312 #endif 313 #ifdef USE_WINHELLO 314 if (fido_dev_register_manifest_func(fido_winhello_manifest) != FIDO_OK) 315 return (FIDO_ERR_INTERNAL); 316 #endif 317 318 for (curr = manifest_funcs; curr != NULL; curr = curr->next) { 319 curr_olen = 0; 320 m_func = curr->manifest_func; 321 r = m_func(devlist + *olen, ilen - *olen, &curr_olen); 322 if (r != FIDO_OK) 323 return (r); 324 *olen += curr_olen; 325 if (*olen == ilen) 326 break; 327 } 328 329 return (FIDO_OK); 330 } 331 332 int 333 fido_dev_open_with_info(fido_dev_t *dev) 334 { 335 int ms = dev->timeout_ms; 336 337 if (dev->path == NULL) 338 return (FIDO_ERR_INVALID_ARGUMENT); 339 340 return (fido_dev_open_wait(dev, dev->path, &ms)); 341 } 342 343 int 344 fido_dev_open(fido_dev_t *dev, const char *path) 345 { 346 int ms = dev->timeout_ms; 347 348 #ifdef NFC_LINUX 349 if (strncmp(path, FIDO_NFC_PREFIX, strlen(FIDO_NFC_PREFIX)) == 0) { 350 dev->io_own = true; 351 dev->io = (fido_dev_io_t) { 352 fido_nfc_open, 353 fido_nfc_close, 354 fido_nfc_read, 355 fido_nfc_write, 356 }; 357 dev->transport = (fido_dev_transport_t) { 358 fido_nfc_rx, 359 fido_nfc_tx, 360 }; 361 } 362 #endif 363 364 return (fido_dev_open_wait(dev, path, &ms)); 365 } 366 367 int 368 fido_dev_close(fido_dev_t *dev) 369 { 370 #ifdef USE_WINHELLO 371 if (dev->flags & FIDO_DEV_WINHELLO) 372 return (fido_winhello_close(dev)); 373 #endif 374 if (dev->io_handle == NULL || dev->io.close == NULL) 375 return (FIDO_ERR_INVALID_ARGUMENT); 376 377 dev->io.close(dev->io_handle); 378 dev->io_handle = NULL; 379 dev->cid = CTAP_CID_BROADCAST; 380 381 return (FIDO_OK); 382 } 383 384 int 385 fido_dev_set_sigmask(fido_dev_t *dev, const fido_sigset_t *sigmask) 386 { 387 if (dev->io_handle == NULL || sigmask == NULL) 388 return (FIDO_ERR_INVALID_ARGUMENT); 389 390 #ifdef NFC_LINUX 391 if (dev->transport.rx == fido_nfc_rx && dev->io.read == fido_nfc_read) 392 return (fido_nfc_set_sigmask(dev->io_handle, sigmask)); 393 #endif 394 if (dev->transport.rx == NULL && dev->io.read == fido_hid_read) 395 return (fido_hid_set_sigmask(dev->io_handle, sigmask)); 396 397 return (FIDO_ERR_INVALID_ARGUMENT); 398 } 399 400 int 401 fido_dev_cancel(fido_dev_t *dev) 402 { 403 int ms = dev->timeout_ms; 404 405 #ifdef USE_WINHELLO 406 if (dev->flags & FIDO_DEV_WINHELLO) 407 return (fido_winhello_cancel(dev)); 408 #endif 409 if (fido_dev_is_fido2(dev) == false) 410 return (FIDO_ERR_INVALID_ARGUMENT); 411 if (fido_tx(dev, CTAP_CMD_CANCEL, NULL, 0, &ms) < 0) 412 return (FIDO_ERR_TX); 413 414 return (FIDO_OK); 415 } 416 417 int 418 fido_dev_get_touch_begin(fido_dev_t *dev) 419 { 420 fido_blob_t f; 421 cbor_item_t *argv[9]; 422 const char *clientdata = FIDO_DUMMY_CLIENTDATA; 423 const uint8_t user_id = FIDO_DUMMY_USER_ID; 424 unsigned char cdh[SHA256_DIGEST_LENGTH]; 425 fido_rp_t rp; 426 fido_user_t user; 427 int ms = dev->timeout_ms; 428 int r = FIDO_ERR_INTERNAL; 429 430 memset(&f, 0, sizeof(f)); 431 memset(argv, 0, sizeof(argv)); 432 memset(cdh, 0, sizeof(cdh)); 433 memset(&rp, 0, sizeof(rp)); 434 memset(&user, 0, sizeof(user)); 435 436 if (fido_dev_is_fido2(dev) == false) 437 return (u2f_get_touch_begin(dev, &ms)); 438 439 if (SHA256((const void *)clientdata, strlen(clientdata), cdh) != cdh) { 440 fido_log_debug("%s: sha256", __func__); 441 return (FIDO_ERR_INTERNAL); 442 } 443 444 if ((rp.id = strdup(FIDO_DUMMY_RP_ID)) == NULL || 445 (user.name = strdup(FIDO_DUMMY_USER_NAME)) == NULL) { 446 fido_log_debug("%s: strdup", __func__); 447 goto fail; 448 } 449 450 if (fido_blob_set(&user.id, &user_id, sizeof(user_id)) < 0) { 451 fido_log_debug("%s: fido_blob_set", __func__); 452 goto fail; 453 } 454 455 if ((argv[0] = cbor_build_bytestring(cdh, sizeof(cdh))) == NULL || 456 (argv[1] = cbor_encode_rp_entity(&rp)) == NULL || 457 (argv[2] = cbor_encode_user_entity(&user)) == NULL || 458 (argv[3] = cbor_encode_pubkey_param(COSE_ES256)) == NULL) { 459 fido_log_debug("%s: cbor encode", __func__); 460 goto fail; 461 } 462 463 if (fido_dev_supports_pin(dev)) { 464 if ((argv[7] = cbor_new_definite_bytestring()) == NULL || 465 (argv[8] = cbor_encode_pin_opt(dev)) == NULL) { 466 fido_log_debug("%s: cbor encode", __func__); 467 goto fail; 468 } 469 } 470 471 if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, nitems(argv), &f) < 0 || 472 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, &ms) < 0) { 473 fido_log_debug("%s: fido_tx", __func__); 474 r = FIDO_ERR_TX; 475 goto fail; 476 } 477 478 r = FIDO_OK; 479 fail: 480 cbor_vector_free(argv, nitems(argv)); 481 free(f.ptr); 482 free(rp.id); 483 free(user.name); 484 free(user.id.ptr); 485 486 return (r); 487 } 488 489 int 490 fido_dev_get_touch_status(fido_dev_t *dev, int *touched, int ms) 491 { 492 int r; 493 494 *touched = 0; 495 496 if (fido_dev_is_fido2(dev) == false) 497 return (u2f_get_touch_status(dev, touched, &ms)); 498 499 switch ((r = fido_rx_cbor_status(dev, &ms))) { 500 case FIDO_ERR_PIN_AUTH_INVALID: 501 case FIDO_ERR_PIN_INVALID: 502 case FIDO_ERR_PIN_NOT_SET: 503 case FIDO_ERR_SUCCESS: 504 *touched = 1; 505 break; 506 case FIDO_ERR_RX: 507 /* ignore */ 508 break; 509 default: 510 fido_log_debug("%s: fido_rx_cbor_status", __func__); 511 return (r); 512 } 513 514 return (FIDO_OK); 515 } 516 517 int 518 fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io) 519 { 520 if (dev->io_handle != NULL) { 521 fido_log_debug("%s: non-NULL handle", __func__); 522 return (FIDO_ERR_INVALID_ARGUMENT); 523 } 524 525 if (io == NULL || io->open == NULL || io->close == NULL || 526 io->read == NULL || io->write == NULL) { 527 fido_log_debug("%s: NULL function", __func__); 528 return (FIDO_ERR_INVALID_ARGUMENT); 529 } 530 531 dev->io = *io; 532 dev->io_own = true; 533 534 return (FIDO_OK); 535 } 536 537 int 538 fido_dev_set_transport_functions(fido_dev_t *dev, const fido_dev_transport_t *t) 539 { 540 if (dev->io_handle != NULL) { 541 fido_log_debug("%s: non-NULL handle", __func__); 542 return (FIDO_ERR_INVALID_ARGUMENT); 543 } 544 545 dev->transport = *t; 546 dev->io_own = true; 547 548 return (FIDO_OK); 549 } 550 551 void * 552 fido_dev_io_handle(const fido_dev_t *dev) 553 { 554 555 return (dev->io_handle); 556 } 557 558 void 559 fido_init(int flags) 560 { 561 if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL) 562 fido_log_init(); 563 564 disable_u2f_fallback = (flags & FIDO_DISABLE_U2F_FALLBACK); 565 } 566 567 fido_dev_t * 568 fido_dev_new(void) 569 { 570 fido_dev_t *dev; 571 572 if ((dev = calloc(1, sizeof(*dev))) == NULL) 573 return (NULL); 574 575 dev->cid = CTAP_CID_BROADCAST; 576 dev->timeout_ms = -1; 577 dev->io = (fido_dev_io_t) { 578 &fido_hid_open, 579 &fido_hid_close, 580 &fido_hid_read, 581 &fido_hid_write, 582 }; 583 584 return (dev); 585 } 586 587 fido_dev_t * 588 fido_dev_new_with_info(const fido_dev_info_t *di) 589 { 590 fido_dev_t *dev; 591 592 if ((dev = calloc(1, sizeof(*dev))) == NULL) 593 return (NULL); 594 595 #if 0 596 if (di->io.open == NULL || di->io.close == NULL || 597 di->io.read == NULL || di->io.write == NULL) { 598 fido_log_debug("%s: NULL function", __func__); 599 fido_dev_free(&dev); 600 return (NULL); 601 } 602 #endif 603 604 dev->io = di->io; 605 dev->io_own = di->transport.tx != NULL || di->transport.rx != NULL; 606 dev->transport = di->transport; 607 dev->cid = CTAP_CID_BROADCAST; 608 dev->timeout_ms = -1; 609 610 if ((dev->path = strdup(di->path)) == NULL) { 611 fido_log_debug("%s: strdup", __func__); 612 fido_dev_free(&dev); 613 return (NULL); 614 } 615 616 return (dev); 617 } 618 619 void 620 fido_dev_free(fido_dev_t **dev_p) 621 { 622 fido_dev_t *dev; 623 624 if (dev_p == NULL || (dev = *dev_p) == NULL) 625 return; 626 627 free(dev->path); 628 free(dev); 629 630 *dev_p = NULL; 631 } 632 633 uint8_t 634 fido_dev_protocol(const fido_dev_t *dev) 635 { 636 return (dev->attr.protocol); 637 } 638 639 uint8_t 640 fido_dev_major(const fido_dev_t *dev) 641 { 642 return (dev->attr.major); 643 } 644 645 uint8_t 646 fido_dev_minor(const fido_dev_t *dev) 647 { 648 return (dev->attr.minor); 649 } 650 651 uint8_t 652 fido_dev_build(const fido_dev_t *dev) 653 { 654 return (dev->attr.build); 655 } 656 657 uint8_t 658 fido_dev_flags(const fido_dev_t *dev) 659 { 660 return (dev->attr.flags); 661 } 662 663 bool 664 fido_dev_is_fido2(const fido_dev_t *dev) 665 { 666 return (dev->attr.flags & FIDO_CAP_CBOR); 667 } 668 669 bool 670 fido_dev_is_winhello(const fido_dev_t *dev) 671 { 672 return (dev->flags & FIDO_DEV_WINHELLO); 673 } 674 675 bool 676 fido_dev_supports_pin(const fido_dev_t *dev) 677 { 678 return (dev->flags & (FIDO_DEV_PIN_SET|FIDO_DEV_PIN_UNSET)); 679 } 680 681 bool 682 fido_dev_has_pin(const fido_dev_t *dev) 683 { 684 return (dev->flags & FIDO_DEV_PIN_SET); 685 } 686 687 bool 688 fido_dev_supports_cred_prot(const fido_dev_t *dev) 689 { 690 return (dev->flags & FIDO_DEV_CRED_PROT); 691 } 692 693 bool 694 fido_dev_supports_credman(const fido_dev_t *dev) 695 { 696 return (dev->flags & FIDO_DEV_CREDMAN); 697 } 698 699 bool 700 fido_dev_supports_uv(const fido_dev_t *dev) 701 { 702 return (dev->flags & (FIDO_DEV_UV_SET|FIDO_DEV_UV_UNSET)); 703 } 704 705 bool 706 fido_dev_has_uv(const fido_dev_t *dev) 707 { 708 return (dev->flags & FIDO_DEV_UV_SET); 709 } 710 711 bool 712 fido_dev_supports_permissions(const fido_dev_t *dev) 713 { 714 return (dev->flags & FIDO_DEV_TOKEN_PERMS); 715 } 716 717 void 718 fido_dev_force_u2f(fido_dev_t *dev) 719 { 720 dev->attr.flags &= (uint8_t)~FIDO_CAP_CBOR; 721 dev->flags = 0; 722 } 723 724 void 725 fido_dev_force_fido2(fido_dev_t *dev) 726 { 727 dev->attr.flags |= FIDO_CAP_CBOR; 728 } 729 730 uint8_t 731 fido_dev_get_pin_protocol(const fido_dev_t *dev) 732 { 733 if (dev->flags & FIDO_DEV_PIN_PROTOCOL2) 734 return (CTAP_PIN_PROTOCOL2); 735 else if (dev->flags & FIDO_DEV_PIN_PROTOCOL1) 736 return (CTAP_PIN_PROTOCOL1); 737 738 return (0); 739 } 740 741 uint64_t 742 fido_dev_maxmsgsize(const fido_dev_t *dev) 743 { 744 return (dev->maxmsgsize); 745 } 746 747 int 748 fido_dev_set_timeout(fido_dev_t *dev, int ms) 749 { 750 if (ms < -1) 751 return (FIDO_ERR_INVALID_ARGUMENT); 752 753 dev->timeout_ms = ms; 754 755 return (FIDO_OK); 756 } 757