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 10static int 11decode_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 27static int 28decode_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 51static int 52decode_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 66static int 67decode_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 89static int 90decode_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 110static int 111decode_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 129static int 130decode_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 153static int 154decode_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; 182out: 183 free(name); 184 185 return (ok); 186} 187 188static int 189decode_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 214static int 215decode_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 238static int 239decode_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 261static int 262decode_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 282static int 283parse_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 346static int 347fido_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 361static int 362fido_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); 385out: 386 freezero(msg, FIDO_MAXMSG); 387 388 return (r); 389} 390 391int 392fido_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 407int 408fido_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 419fido_cbor_info_t * 420fido_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 432void 433fido_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 445void 446fido_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 457char ** 458fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci) 459{ 460 return (ci->versions.ptr); 461} 462 463size_t 464fido_cbor_info_versions_len(const fido_cbor_info_t *ci) 465{ 466 return (ci->versions.len); 467} 468 469char ** 470fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci) 471{ 472 return (ci->extensions.ptr); 473} 474 475size_t 476fido_cbor_info_extensions_len(const fido_cbor_info_t *ci) 477{ 478 return (ci->extensions.len); 479} 480 481char ** 482fido_cbor_info_transports_ptr(const fido_cbor_info_t *ci) 483{ 484 return (ci->transports.ptr); 485} 486 487size_t 488fido_cbor_info_transports_len(const fido_cbor_info_t *ci) 489{ 490 return (ci->transports.len); 491} 492 493const unsigned char * 494fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci) 495{ 496 return (ci->aaguid); 497} 498 499size_t 500fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci) 501{ 502 return (sizeof(ci->aaguid)); 503} 504 505char ** 506fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci) 507{ 508 return (ci->options.name); 509} 510 511const bool * 512fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci) 513{ 514 return (ci->options.value); 515} 516 517size_t 518fido_cbor_info_options_len(const fido_cbor_info_t *ci) 519{ 520 return (ci->options.len); 521} 522 523uint64_t 524fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *ci) 525{ 526 return (ci->maxcredbloblen); 527} 528 529uint64_t 530fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci) 531{ 532 return (ci->maxmsgsiz); 533} 534 535uint64_t 536fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *ci) 537{ 538 return (ci->maxcredcntlst); 539} 540 541uint64_t 542fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci) 543{ 544 return (ci->maxcredidlen); 545} 546 547uint64_t 548fido_cbor_info_maxlargeblob(const fido_cbor_info_t *ci) 549{ 550 return (ci->maxlargeblob); 551} 552 553uint64_t 554fido_cbor_info_fwversion(const fido_cbor_info_t *ci) 555{ 556 return (ci->fwversion); 557} 558 559uint64_t 560fido_cbor_info_minpinlen(const fido_cbor_info_t *ci) 561{ 562 return (ci->minpinlen); 563} 564 565uint64_t 566fido_cbor_info_maxrpid_minpinlen(const fido_cbor_info_t *ci) 567{ 568 return (ci->maxrpid_minlen); 569} 570 571uint64_t 572fido_cbor_info_uv_attempts(const fido_cbor_info_t *ci) 573{ 574 return (ci->uv_attempts); 575} 576 577uint64_t 578fido_cbor_info_uv_modality(const fido_cbor_info_t *ci) 579{ 580 return (ci->uv_modality); 581} 582 583int64_t 584fido_cbor_info_rk_remaining(const fido_cbor_info_t *ci) 585{ 586 return (ci->rk_remaining); 587} 588 589const uint8_t * 590fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci) 591{ 592 return (ci->protocols.ptr); 593} 594 595size_t 596fido_cbor_info_protocols_len(const fido_cbor_info_t *ci) 597{ 598 return (ci->protocols.len); 599} 600 601size_t 602fido_cbor_info_algorithm_count(const fido_cbor_info_t *ci) 603{ 604 return (ci->algorithms.len); 605} 606 607const char * 608fido_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 616int 617fido_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 625bool 626fido_cbor_info_new_pin_required(const fido_cbor_info_t *ci) 627{ 628 return (ci->new_pin_reqd); 629} 630 631char ** 632fido_cbor_info_certs_name_ptr(const fido_cbor_info_t *ci) 633{ 634 return (ci->certs.name); 635} 636 637const uint64_t * 638fido_cbor_info_certs_value_ptr(const fido_cbor_info_t *ci) 639{ 640 return (ci->certs.value); 641} 642 643size_t 644fido_cbor_info_certs_len(const fido_cbor_info_t *ci) 645{ 646 return (ci->certs.len); 647} 648