Home | History | Annotate | Line # | Download | only in src
      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