1 /* 2 * Copyright (c) 2018-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 10 static int 11 decode_string(const cbor_item_t *item, void *arg) 12 { 13 fido_str_array_t *a = arg; 14 const size_t i = a->len; 15 16 /* keep ptr[x] and len consistent */ 17 if (cbor_string_copy(item, &a->ptr[i]) < 0) { 18 fido_log_debug("%s: cbor_string_copy", __func__); 19 return (-1); 20 } 21 22 a->len++; 23 24 return (0); 25 } 26 27 static int 28 decode_string_array(const cbor_item_t *item, fido_str_array_t *v) 29 { 30 v->ptr = NULL; 31 v->len = 0; 32 33 if (cbor_isa_array(item) == false || 34 cbor_array_is_definite(item) == false) { 35 fido_log_debug("%s: cbor type", __func__); 36 return (-1); 37 } 38 39 v->ptr = calloc(cbor_array_size(item), sizeof(char *)); 40 if (v->ptr == NULL) 41 return (-1); 42 43 if (cbor_array_iter(item, v, decode_string) < 0) { 44 fido_log_debug("%s: decode_string", __func__); 45 return (-1); 46 } 47 48 return (0); 49 } 50 51 static int 52 decode_aaguid(const cbor_item_t *item, unsigned char *aaguid, size_t aaguid_len) 53 { 54 if (cbor_isa_bytestring(item) == false || 55 cbor_bytestring_is_definite(item) == false || 56 cbor_bytestring_length(item) != aaguid_len) { 57 fido_log_debug("%s: cbor type", __func__); 58 return (-1); 59 } 60 61 memcpy(aaguid, cbor_bytestring_handle(item), aaguid_len); 62 63 return (0); 64 } 65 66 static int 67 decode_option(const cbor_item_t *key, const cbor_item_t *val, void *arg) 68 { 69 fido_opt_array_t *o = arg; 70 const size_t i = o->len; 71 72 if (cbor_decode_bool(val, NULL) < 0) { 73 fido_log_debug("%s: cbor_decode_bool", __func__); 74 return (0); /* ignore */ 75 } 76 77 if (cbor_string_copy(key, &o->name[i]) < 0) { 78 fido_log_debug("%s: cbor_string_copy", __func__); 79 return (0); /* ignore */ 80 } 81 82 /* keep name/value and len consistent */ 83 o->value[i] = cbor_ctrl_value(val) == CBOR_CTRL_TRUE; 84 o->len++; 85 86 return (0); 87 } 88 89 static int 90 decode_options(const cbor_item_t *item, fido_opt_array_t *o) 91 { 92 o->name = NULL; 93 o->value = NULL; 94 o->len = 0; 95 96 if (cbor_isa_map(item) == false || 97 cbor_map_is_definite(item) == false) { 98 fido_log_debug("%s: cbor type", __func__); 99 return (-1); 100 } 101 102 o->name = calloc(cbor_map_size(item), sizeof(char *)); 103 o->value = calloc(cbor_map_size(item), sizeof(bool)); 104 if (o->name == NULL || o->value == NULL) 105 return (-1); 106 107 return (cbor_map_iter(item, o, decode_option)); 108 } 109 110 static int 111 decode_protocol(const cbor_item_t *item, void *arg) 112 { 113 fido_byte_array_t *p = arg; 114 const size_t i = p->len; 115 116 if (cbor_isa_uint(item) == false || 117 cbor_int_get_width(item) != CBOR_INT_8) { 118 fido_log_debug("%s: cbor type", __func__); 119 return (-1); 120 } 121 122 /* keep ptr[x] and len consistent */ 123 p->ptr[i] = cbor_get_uint8(item); 124 p->len++; 125 126 return (0); 127 } 128 129 static int 130 decode_protocols(const cbor_item_t *item, fido_byte_array_t *p) 131 { 132 p->ptr = NULL; 133 p->len = 0; 134 135 if (cbor_isa_array(item) == false || 136 cbor_array_is_definite(item) == false) { 137 fido_log_debug("%s: cbor type", __func__); 138 return (-1); 139 } 140 141 p->ptr = calloc(cbor_array_size(item), sizeof(uint8_t)); 142 if (p->ptr == NULL) 143 return (-1); 144 145 if (cbor_array_iter(item, p, decode_protocol) < 0) { 146 fido_log_debug("%s: decode_protocol", __func__); 147 return (-1); 148 } 149 150 return (0); 151 } 152 153 static int 154 decode_algorithm_entry(const cbor_item_t *key, const cbor_item_t *val, 155 void *arg) 156 { 157 fido_algo_t *alg = arg; 158 char *name = NULL; 159 int ok = -1; 160 161 if (cbor_string_copy(key, &name) < 0) { 162 fido_log_debug("%s: cbor type", __func__); 163 ok = 0; /* ignore */ 164 goto out; 165 } 166 167 if (!strcmp(name, "alg")) { 168 if (cbor_isa_negint(val) == false || 169 cbor_get_int(val) > INT_MAX || alg->cose != 0) { 170 fido_log_debug("%s: alg", __func__); 171 goto out; 172 } 173 alg->cose = -(int)cbor_get_int(val) - 1; 174 } else if (!strcmp(name, "type")) { 175 if (cbor_string_copy(val, &alg->type) < 0) { 176 fido_log_debug("%s: type", __func__); 177 goto out; 178 } 179 } 180 181 ok = 0; 182 out: 183 free(name); 184 185 return (ok); 186 } 187 188 static int 189 decode_algorithm(const cbor_item_t *item, void *arg) 190 { 191 fido_algo_array_t *aa = arg; 192 const size_t i = aa->len; 193 194 if (cbor_isa_map(item) == false || 195 cbor_map_is_definite(item) == false) { 196 fido_log_debug("%s: cbor type", __func__); 197 return (-1); 198 } 199 200 memset(&aa->ptr[i], 0, sizeof(aa->ptr[i])); 201 202 if (cbor_map_iter(item, &aa->ptr[i], decode_algorithm_entry) < 0) { 203 fido_log_debug("%s: decode_algorithm_entry", __func__); 204 fido_algo_free(&aa->ptr[i]); 205 return (-1); 206 } 207 208 /* keep ptr[x] and len consistent */ 209 aa->len++; 210 211 return (0); 212 } 213 214 static int 215 decode_algorithms(const cbor_item_t *item, fido_algo_array_t *aa) 216 { 217 aa->ptr = NULL; 218 aa->len = 0; 219 220 if (cbor_isa_array(item) == false || 221 cbor_array_is_definite(item) == false) { 222 fido_log_debug("%s: cbor type", __func__); 223 return (-1); 224 } 225 226 aa->ptr = calloc(cbor_array_size(item), sizeof(fido_algo_t)); 227 if (aa->ptr == NULL) 228 return (-1); 229 230 if (cbor_array_iter(item, aa, decode_algorithm) < 0) { 231 fido_log_debug("%s: decode_algorithm", __func__); 232 return (-1); 233 } 234 235 return (0); 236 } 237 238 static int 239 decode_cert(const cbor_item_t *key, const cbor_item_t *val, void *arg) 240 { 241 fido_cert_array_t *c = arg; 242 const size_t i = c->len; 243 244 if (cbor_is_int(val) == false) { 245 fido_log_debug("%s: cbor_is_int", __func__); 246 return (0); /* ignore */ 247 } 248 249 if (cbor_string_copy(key, &c->name[i]) < 0) { 250 fido_log_debug("%s: cbor_string_copy", __func__); 251 return (0); /* ignore */ 252 } 253 254 /* keep name/value and len consistent */ 255 c->value[i] = cbor_get_int(val); 256 c->len++; 257 258 return (0); 259 } 260 261 static int 262 decode_certs(const cbor_item_t *item, fido_cert_array_t *c) 263 { 264 c->name = NULL; 265 c->value = NULL; 266 c->len = 0; 267 268 if (cbor_isa_map(item) == false || 269 cbor_map_is_definite(item) == false) { 270 fido_log_debug("%s: cbor type", __func__); 271 return (-1); 272 } 273 274 c->name = calloc(cbor_map_size(item), sizeof(char *)); 275 c->value = calloc(cbor_map_size(item), sizeof(uint64_t)); 276 if (c->name == NULL || c->value == NULL) 277 return (-1); 278 279 return (cbor_map_iter(item, c, decode_cert)); 280 } 281 282 static int 283 parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg) 284 { 285 fido_cbor_info_t *ci = arg; 286 uint64_t x; 287 288 if (cbor_isa_uint(key) == false || 289 cbor_int_get_width(key) != CBOR_INT_8) { 290 fido_log_debug("%s: cbor type", __func__); 291 return (0); /* ignore */ 292 } 293 294 switch (cbor_get_uint8(key)) { 295 case 1: /* versions */ 296 return (decode_string_array(val, &ci->versions)); 297 case 2: /* extensions */ 298 return (decode_string_array(val, &ci->extensions)); 299 case 3: /* aaguid */ 300 return (decode_aaguid(val, ci->aaguid, sizeof(ci->aaguid))); 301 case 4: /* options */ 302 return (decode_options(val, &ci->options)); 303 case 5: /* maxMsgSize */ 304 return (cbor_decode_uint64(val, &ci->maxmsgsiz)); 305 case 6: /* pinProtocols */ 306 return (decode_protocols(val, &ci->protocols)); 307 case 7: /* maxCredentialCountInList */ 308 return (cbor_decode_uint64(val, &ci->maxcredcntlst)); 309 case 8: /* maxCredentialIdLength */ 310 return (cbor_decode_uint64(val, &ci->maxcredidlen)); 311 case 9: /* transports */ 312 return (decode_string_array(val, &ci->transports)); 313 case 10: /* algorithms */ 314 return (decode_algorithms(val, &ci->algorithms)); 315 case 11: /* maxSerializedLargeBlobArray */ 316 return (cbor_decode_uint64(val, &ci->maxlargeblob)); 317 case 12: /* forcePINChange */ 318 return (cbor_decode_bool(val, &ci->new_pin_reqd)); 319 case 13: /* minPINLength */ 320 return (cbor_decode_uint64(val, &ci->minpinlen)); 321 case 14: /* fwVersion */ 322 return (cbor_decode_uint64(val, &ci->fwversion)); 323 case 15: /* maxCredBlobLen */ 324 return (cbor_decode_uint64(val, &ci->maxcredbloblen)); 325 case 16: /* maxRPIDsForSetMinPINLength */ 326 return (cbor_decode_uint64(val, &ci->maxrpid_minlen)); 327 case 17: /* preferredPlatformUvAttempts */ 328 return (cbor_decode_uint64(val, &ci->uv_attempts)); 329 case 18: /* uvModality */ 330 return (cbor_decode_uint64(val, &ci->uv_modality)); 331 case 19: /* certifications */ 332 return (decode_certs(val, &ci->certs)); 333 case 20: /* remainingDiscoverableCredentials */ 334 if (cbor_decode_uint64(val, &x) < 0 || x > INT64_MAX) { 335 fido_log_debug("%s: cbor_decode_uint64", __func__); 336 return (-1); 337 } 338 ci->rk_remaining = (int64_t)x; 339 return (0); 340 default: /* ignore */ 341 fido_log_debug("%s: cbor type: 0x%02x", __func__, cbor_get_uint8(key)); 342 return (0); 343 } 344 } 345 346 static int 347 fido_dev_get_cbor_info_tx(fido_dev_t *dev, int *ms) 348 { 349 const unsigned char cbor[] = { CTAP_CBOR_GETINFO }; 350 351 fido_log_debug("%s: dev=%p", __func__, (void *)dev); 352 353 if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) { 354 fido_log_debug("%s: fido_tx", __func__); 355 return (FIDO_ERR_TX); 356 } 357 358 return (FIDO_OK); 359 } 360 361 static int 362 fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms) 363 { 364 unsigned char *msg; 365 int msglen; 366 int r; 367 368 fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev, 369 (void *)ci, *ms); 370 371 fido_cbor_info_reset(ci); 372 373 if ((msg = malloc(FIDO_MAXMSG)) == NULL) { 374 r = FIDO_ERR_INTERNAL; 375 goto out; 376 } 377 378 if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) { 379 fido_log_debug("%s: fido_rx", __func__); 380 r = FIDO_ERR_RX; 381 goto out; 382 } 383 384 r = cbor_parse_reply(msg, (size_t)msglen, ci, parse_reply_element); 385 out: 386 freezero(msg, FIDO_MAXMSG); 387 388 return (r); 389 } 390 391 int 392 fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms) 393 { 394 int r; 395 396 #ifdef USE_WINHELLO 397 if (dev->flags & FIDO_DEV_WINHELLO) 398 return (fido_winhello_get_cbor_info(dev, ci)); 399 #endif 400 if ((r = fido_dev_get_cbor_info_tx(dev, ms)) != FIDO_OK || 401 (r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK) 402 return (r); 403 404 return (FIDO_OK); 405 } 406 407 int 408 fido_dev_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci) 409 { 410 int ms = dev->timeout_ms; 411 412 return (fido_dev_get_cbor_info_wait(dev, ci, &ms)); 413 } 414 415 /* 416 * get/set functions for fido_cbor_info_t; always at the end of the file 417 */ 418 419 fido_cbor_info_t * 420 fido_cbor_info_new(void) 421 { 422 fido_cbor_info_t *ci; 423 424 if ((ci = calloc(1, sizeof(fido_cbor_info_t))) == NULL) 425 return (NULL); 426 427 fido_cbor_info_reset(ci); 428 429 return (ci); 430 } 431 432 void 433 fido_cbor_info_reset(fido_cbor_info_t *ci) 434 { 435 fido_str_array_free(&ci->versions); 436 fido_str_array_free(&ci->extensions); 437 fido_str_array_free(&ci->transports); 438 fido_opt_array_free(&ci->options); 439 fido_byte_array_free(&ci->protocols); 440 fido_algo_array_free(&ci->algorithms); 441 fido_cert_array_free(&ci->certs); 442 ci->rk_remaining = -1; 443 } 444 445 void 446 fido_cbor_info_free(fido_cbor_info_t **ci_p) 447 { 448 fido_cbor_info_t *ci; 449 450 if (ci_p == NULL || (ci = *ci_p) == NULL) 451 return; 452 fido_cbor_info_reset(ci); 453 free(ci); 454 *ci_p = NULL; 455 } 456 457 char ** 458 fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci) 459 { 460 return (ci->versions.ptr); 461 } 462 463 size_t 464 fido_cbor_info_versions_len(const fido_cbor_info_t *ci) 465 { 466 return (ci->versions.len); 467 } 468 469 char ** 470 fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci) 471 { 472 return (ci->extensions.ptr); 473 } 474 475 size_t 476 fido_cbor_info_extensions_len(const fido_cbor_info_t *ci) 477 { 478 return (ci->extensions.len); 479 } 480 481 char ** 482 fido_cbor_info_transports_ptr(const fido_cbor_info_t *ci) 483 { 484 return (ci->transports.ptr); 485 } 486 487 size_t 488 fido_cbor_info_transports_len(const fido_cbor_info_t *ci) 489 { 490 return (ci->transports.len); 491 } 492 493 const unsigned char * 494 fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci) 495 { 496 return (ci->aaguid); 497 } 498 499 size_t 500 fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci) 501 { 502 return (sizeof(ci->aaguid)); 503 } 504 505 char ** 506 fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci) 507 { 508 return (ci->options.name); 509 } 510 511 const bool * 512 fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci) 513 { 514 return (ci->options.value); 515 } 516 517 size_t 518 fido_cbor_info_options_len(const fido_cbor_info_t *ci) 519 { 520 return (ci->options.len); 521 } 522 523 uint64_t 524 fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *ci) 525 { 526 return (ci->maxcredbloblen); 527 } 528 529 uint64_t 530 fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci) 531 { 532 return (ci->maxmsgsiz); 533 } 534 535 uint64_t 536 fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *ci) 537 { 538 return (ci->maxcredcntlst); 539 } 540 541 uint64_t 542 fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci) 543 { 544 return (ci->maxcredidlen); 545 } 546 547 uint64_t 548 fido_cbor_info_maxlargeblob(const fido_cbor_info_t *ci) 549 { 550 return (ci->maxlargeblob); 551 } 552 553 uint64_t 554 fido_cbor_info_fwversion(const fido_cbor_info_t *ci) 555 { 556 return (ci->fwversion); 557 } 558 559 uint64_t 560 fido_cbor_info_minpinlen(const fido_cbor_info_t *ci) 561 { 562 return (ci->minpinlen); 563 } 564 565 uint64_t 566 fido_cbor_info_maxrpid_minpinlen(const fido_cbor_info_t *ci) 567 { 568 return (ci->maxrpid_minlen); 569 } 570 571 uint64_t 572 fido_cbor_info_uv_attempts(const fido_cbor_info_t *ci) 573 { 574 return (ci->uv_attempts); 575 } 576 577 uint64_t 578 fido_cbor_info_uv_modality(const fido_cbor_info_t *ci) 579 { 580 return (ci->uv_modality); 581 } 582 583 int64_t 584 fido_cbor_info_rk_remaining(const fido_cbor_info_t *ci) 585 { 586 return (ci->rk_remaining); 587 } 588 589 const uint8_t * 590 fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci) 591 { 592 return (ci->protocols.ptr); 593 } 594 595 size_t 596 fido_cbor_info_protocols_len(const fido_cbor_info_t *ci) 597 { 598 return (ci->protocols.len); 599 } 600 601 size_t 602 fido_cbor_info_algorithm_count(const fido_cbor_info_t *ci) 603 { 604 return (ci->algorithms.len); 605 } 606 607 const char * 608 fido_cbor_info_algorithm_type(const fido_cbor_info_t *ci, size_t idx) 609 { 610 if (idx >= ci->algorithms.len) 611 return (NULL); 612 613 return (ci->algorithms.ptr[idx].type); 614 } 615 616 int 617 fido_cbor_info_algorithm_cose(const fido_cbor_info_t *ci, size_t idx) 618 { 619 if (idx >= ci->algorithms.len) 620 return (0); 621 622 return (ci->algorithms.ptr[idx].cose); 623 } 624 625 bool 626 fido_cbor_info_new_pin_required(const fido_cbor_info_t *ci) 627 { 628 return (ci->new_pin_reqd); 629 } 630 631 char ** 632 fido_cbor_info_certs_name_ptr(const fido_cbor_info_t *ci) 633 { 634 return (ci->certs.name); 635 } 636 637 const uint64_t * 638 fido_cbor_info_certs_value_ptr(const fido_cbor_info_t *ci) 639 { 640 return (ci->certs.value); 641 } 642 643 size_t 644 fido_cbor_info_certs_len(const fido_cbor_info_t *ci) 645 { 646 return (ci->certs.len); 647 } 648