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