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 <openssl/sha.h>
      9 #include "fido.h"
     10 #include "fido/es256.h"
     11 
     12 #define CTAP21_UV_TOKEN_PERM_MAKECRED	0x01
     13 #define CTAP21_UV_TOKEN_PERM_ASSERT	0x02
     14 #define CTAP21_UV_TOKEN_PERM_CRED_MGMT	0x04
     15 #define CTAP21_UV_TOKEN_PERM_BIO	0x08
     16 #define CTAP21_UV_TOKEN_PERM_LARGEBLOB	0x10
     17 #define CTAP21_UV_TOKEN_PERM_CONFIG	0x20
     18 
     19 int
     20 fido_sha256(fido_blob_t *digest, const u_char *data, size_t data_len)
     21 {
     22 	if ((digest->ptr = calloc(1, SHA256_DIGEST_LENGTH)) == NULL)
     23 		return (-1);
     24 
     25 	digest->len = SHA256_DIGEST_LENGTH;
     26 
     27 	if (SHA256(data, data_len, digest->ptr) != digest->ptr) {
     28 		fido_blob_reset(digest);
     29 		return (-1);
     30 	}
     31 
     32 	return (0);
     33 }
     34 
     35 static int
     36 pin_sha256_enc(const fido_dev_t *dev, const fido_blob_t *shared,
     37     const fido_blob_t *pin, fido_blob_t **out)
     38 {
     39 	fido_blob_t	*ph = NULL;
     40 	int		 r;
     41 
     42 	if ((*out = fido_blob_new()) == NULL ||
     43 	    (ph = fido_blob_new()) == NULL) {
     44 		r = FIDO_ERR_INTERNAL;
     45 		goto fail;
     46 	}
     47 
     48 	if (fido_sha256(ph, pin->ptr, pin->len) < 0 || ph->len < 16) {
     49 		fido_log_debug("%s: SHA256", __func__);
     50 		r = FIDO_ERR_INTERNAL;
     51 		goto fail;
     52 	}
     53 
     54 	ph->len = 16; /* first 16 bytes */
     55 
     56 	if (aes256_cbc_enc(dev, shared, ph, *out) < 0) {
     57 		fido_log_debug("%s: aes256_cbc_enc", __func__);
     58 		r = FIDO_ERR_INTERNAL;
     59 		goto fail;
     60 	}
     61 
     62 	r = FIDO_OK;
     63 fail:
     64 	fido_blob_free(&ph);
     65 
     66 	return (r);
     67 }
     68 
     69 static int
     70 pad64(const char *pin, fido_blob_t **ppin)
     71 {
     72 	size_t	pin_len;
     73 	size_t	ppin_len;
     74 
     75 	pin_len = strlen(pin);
     76 	if (pin_len < 4 || pin_len > 63) {
     77 		fido_log_debug("%s: invalid pin length", __func__);
     78 		return (FIDO_ERR_PIN_POLICY_VIOLATION);
     79 	}
     80 
     81 	if ((*ppin = fido_blob_new()) == NULL)
     82 		return (FIDO_ERR_INTERNAL);
     83 
     84 	ppin_len = (pin_len + 63U) & ~63U;
     85 	if (ppin_len < pin_len ||
     86 	    ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) {
     87 		fido_blob_free(ppin);
     88 		return (FIDO_ERR_INTERNAL);
     89 	}
     90 
     91 	memcpy((*ppin)->ptr, pin, pin_len);
     92 	(*ppin)->len = ppin_len;
     93 
     94 	return (FIDO_OK);
     95 }
     96 
     97 static int
     98 pin_pad64_enc(const fido_dev_t *dev, const fido_blob_t *shared,
     99     const char *pin, fido_blob_t **out)
    100 {
    101 	fido_blob_t *ppin = NULL;
    102 	int	     r;
    103 
    104 	if ((r = pad64(pin, &ppin)) != FIDO_OK) {
    105 		fido_log_debug("%s: pad64", __func__);
    106 		    goto fail;
    107 	}
    108 
    109 	if ((*out = fido_blob_new()) == NULL) {
    110 		r = FIDO_ERR_INTERNAL;
    111 		goto fail;
    112 	}
    113 
    114 	if (aes256_cbc_enc(dev, shared, ppin, *out) < 0) {
    115 		fido_log_debug("%s: aes256_cbc_enc", __func__);
    116 		r = FIDO_ERR_INTERNAL;
    117 		goto fail;
    118 	}
    119 
    120 	r = FIDO_OK;
    121 fail:
    122 	fido_blob_free(&ppin);
    123 
    124 	return (r);
    125 }
    126 
    127 static cbor_item_t *
    128 encode_uv_permission(uint8_t cmd)
    129 {
    130 	switch (cmd) {
    131 	case CTAP_CBOR_ASSERT:
    132 		return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_ASSERT));
    133 	case CTAP_CBOR_BIO_ENROLL_PRE:
    134 	case CTAP_CBOR_BIO_ENROLL:
    135 		return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_BIO));
    136 	case CTAP_CBOR_CONFIG:
    137 		return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_CONFIG));
    138 	case CTAP_CBOR_MAKECRED:
    139 		return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_MAKECRED));
    140 	case CTAP_CBOR_CRED_MGMT_PRE:
    141 	case CTAP_CBOR_CRED_MGMT:
    142 		return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_CRED_MGMT));
    143 	case CTAP_CBOR_LARGEBLOB:
    144 		return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_LARGEBLOB));
    145 	default:
    146 		fido_log_debug("%s: cmd 0x%02x", __func__, cmd);
    147 		return (NULL);
    148 	}
    149 }
    150 
    151 static int
    152 ctap20_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh,
    153     const es256_pk_t *pk, int *ms)
    154 {
    155 	fido_blob_t	 f;
    156 	fido_blob_t	*p = NULL;
    157 	fido_blob_t	*phe = NULL;
    158 	cbor_item_t	*argv[6];
    159 	int		 r;
    160 
    161 	memset(&f, 0, sizeof(f));
    162 	memset(argv, 0, sizeof(argv));
    163 
    164 	if (pin == NULL) {
    165 		fido_log_debug("%s: NULL pin", __func__);
    166 		r = FIDO_ERR_PIN_REQUIRED;
    167 		goto fail;
    168 	}
    169 
    170 	if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
    171 	    (const unsigned char *)pin, strlen(pin)) < 0) {
    172 		fido_log_debug("%s: fido_blob_set", __func__);
    173 		r = FIDO_ERR_INVALID_ARGUMENT;
    174 		goto fail;
    175 	}
    176 
    177 	if ((r = pin_sha256_enc(dev, ecdh, p, &phe)) != FIDO_OK) {
    178 		fido_log_debug("%s: pin_sha256_enc", __func__);
    179 		goto fail;
    180 	}
    181 
    182 	if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
    183 	    (argv[1] = cbor_build_uint8(5)) == NULL ||
    184 	    (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
    185 	    (argv[5] = fido_blob_encode(phe)) == NULL) {
    186 		fido_log_debug("%s: cbor encode", __func__);
    187 		r = FIDO_ERR_INTERNAL;
    188 		goto fail;
    189 	}
    190 
    191 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
    192 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
    193 		fido_log_debug("%s: fido_tx", __func__);
    194 		r = FIDO_ERR_TX;
    195 		goto fail;
    196 	}
    197 
    198 	r = FIDO_OK;
    199 fail:
    200 	cbor_vector_free(argv, nitems(argv));
    201 	fido_blob_free(&p);
    202 	fido_blob_free(&phe);
    203 	free(f.ptr);
    204 
    205 	return (r);
    206 }
    207 
    208 static int
    209 ctap21_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh,
    210     const es256_pk_t *pk, uint8_t cmd, const char *rpid, int *ms)
    211 {
    212 	fido_blob_t	 f;
    213 	fido_blob_t	*p = NULL;
    214 	fido_blob_t	*phe = NULL;
    215 	cbor_item_t	*argv[10];
    216 	uint8_t		 subcmd;
    217 	int		 r;
    218 
    219 	memset(&f, 0, sizeof(f));
    220 	memset(argv, 0, sizeof(argv));
    221 
    222 	if (pin != NULL) {
    223 		if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
    224 		    (const unsigned char *)pin, strlen(pin)) < 0) {
    225 			fido_log_debug("%s: fido_blob_set", __func__);
    226 			r = FIDO_ERR_INVALID_ARGUMENT;
    227 			goto fail;
    228 		}
    229 		if ((r = pin_sha256_enc(dev, ecdh, p, &phe)) != FIDO_OK) {
    230 			fido_log_debug("%s: pin_sha256_enc", __func__);
    231 			goto fail;
    232 		}
    233 		subcmd = 9; /* getPinUvAuthTokenUsingPinWithPermissions */
    234 	} else {
    235 		if (fido_dev_has_uv(dev) == false) {
    236 			fido_log_debug("%s: fido_dev_has_uv", __func__);
    237 			r = FIDO_ERR_PIN_REQUIRED;
    238 			goto fail;
    239 		}
    240 		subcmd = 6; /* getPinUvAuthTokenUsingUvWithPermissions */
    241 	}
    242 
    243 	if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
    244 	    (argv[1] = cbor_build_uint8(subcmd)) == NULL ||
    245 	    (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
    246 	    (phe != NULL && (argv[5] = fido_blob_encode(phe)) == NULL) ||
    247 	    (argv[8] = encode_uv_permission(cmd)) == NULL ||
    248 	    (rpid != NULL && (argv[9] = cbor_build_string(rpid)) == NULL)) {
    249 		fido_log_debug("%s: cbor encode", __func__);
    250 		r = FIDO_ERR_INTERNAL;
    251 		goto fail;
    252 	}
    253 
    254 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
    255 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
    256 		fido_log_debug("%s:  fido_tx", __func__);
    257 		r = FIDO_ERR_TX;
    258 		goto fail;
    259 	}
    260 
    261 	r = FIDO_OK;
    262 fail:
    263 	cbor_vector_free(argv, nitems(argv));
    264 	fido_blob_free(&p);
    265 	fido_blob_free(&phe);
    266 	free(f.ptr);
    267 
    268 	return (r);
    269 }
    270 
    271 static int
    272 parse_uv_token(const cbor_item_t *key, const cbor_item_t *val, void *arg)
    273 {
    274 	fido_blob_t *token = arg;
    275 
    276 	if (cbor_isa_uint(key) == false ||
    277 	    cbor_int_get_width(key) != CBOR_INT_8 ||
    278 	    cbor_get_uint8(key) != 2) {
    279 		fido_log_debug("%s: cbor type", __func__);
    280 		return (0); /* ignore */
    281 	}
    282 
    283 	return (fido_blob_decode(val, token));
    284 }
    285 
    286 static int
    287 uv_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh, fido_blob_t *token,
    288     int *ms)
    289 {
    290 	fido_blob_t	*aes_token = NULL;
    291 	unsigned char	*msg = NULL;
    292 	int		 msglen;
    293 	int		 r;
    294 
    295 	if ((aes_token = fido_blob_new()) == NULL) {
    296 		r = FIDO_ERR_INTERNAL;
    297 		goto fail;
    298 	}
    299 
    300 	if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
    301 		r = FIDO_ERR_INTERNAL;
    302 		goto fail;
    303 	}
    304 
    305 	if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
    306 		fido_log_debug("%s: fido_rx", __func__);
    307 		r = FIDO_ERR_RX;
    308 		goto fail;
    309 	}
    310 
    311 	if ((r = cbor_parse_reply(msg, (size_t)msglen, aes_token,
    312 	    parse_uv_token)) != FIDO_OK) {
    313 		fido_log_debug("%s: parse_uv_token", __func__);
    314 		goto fail;
    315 	}
    316 
    317 	if  (aes256_cbc_dec(dev, ecdh, aes_token, token) < 0) {
    318 		fido_log_debug("%s: aes256_cbc_dec", __func__);
    319 		r = FIDO_ERR_RX;
    320 		goto fail;
    321 	}
    322 
    323 	r = FIDO_OK;
    324 fail:
    325 	fido_blob_free(&aes_token);
    326 	freezero(msg, FIDO_MAXMSG);
    327 
    328 	return (r);
    329 }
    330 
    331 static int
    332 uv_token_wait(fido_dev_t *dev, uint8_t cmd, const char *pin,
    333     const fido_blob_t *ecdh, const es256_pk_t *pk, const char *rpid,
    334     fido_blob_t *token, int *ms)
    335 {
    336 	int r;
    337 
    338 	if (ecdh == NULL || pk == NULL)
    339 		return (FIDO_ERR_INVALID_ARGUMENT);
    340 	if (fido_dev_supports_permissions(dev))
    341 		r = ctap21_uv_token_tx(dev, pin, ecdh, pk, cmd, rpid, ms);
    342 	else
    343 		r = ctap20_uv_token_tx(dev, pin, ecdh, pk, ms);
    344 	if (r != FIDO_OK)
    345 		return (r);
    346 
    347 	return (uv_token_rx(dev, ecdh, token, ms));
    348 }
    349 
    350 int
    351 fido_dev_get_uv_token(fido_dev_t *dev, uint8_t cmd, const char *pin,
    352     const fido_blob_t *ecdh, const es256_pk_t *pk, const char *rpid,
    353     fido_blob_t *token, int *ms)
    354 {
    355 	return (uv_token_wait(dev, cmd, pin, ecdh, pk, rpid, token, ms));
    356 }
    357 
    358 static int
    359 fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin,
    360     int *ms)
    361 {
    362 	fido_blob_t	 f;
    363 	fido_blob_t	*ppine = NULL;
    364 	fido_blob_t	*ecdh = NULL;
    365 	fido_blob_t	*opin = NULL;
    366 	fido_blob_t	*opinhe = NULL;
    367 	cbor_item_t	*argv[6];
    368 	es256_pk_t	*pk = NULL;
    369 	int r;
    370 
    371 	memset(&f, 0, sizeof(f));
    372 	memset(argv, 0, sizeof(argv));
    373 
    374 	if ((opin = fido_blob_new()) == NULL || fido_blob_set(opin,
    375 	    (const unsigned char *)oldpin, strlen(oldpin)) < 0) {
    376 		fido_log_debug("%s: fido_blob_set", __func__);
    377 		r = FIDO_ERR_INVALID_ARGUMENT;
    378 		goto fail;
    379 	}
    380 
    381 	if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
    382 		fido_log_debug("%s: fido_do_ecdh", __func__);
    383 		goto fail;
    384 	}
    385 
    386 	/* pad and encrypt new pin */
    387 	if ((r = pin_pad64_enc(dev, ecdh, pin, &ppine)) != FIDO_OK) {
    388 		fido_log_debug("%s: pin_pad64_enc", __func__);
    389 		goto fail;
    390 	}
    391 
    392 	/* hash and encrypt old pin */
    393 	if ((r = pin_sha256_enc(dev, ecdh, opin, &opinhe)) != FIDO_OK) {
    394 		fido_log_debug("%s: pin_sha256_enc", __func__);
    395 		goto fail;
    396 	}
    397 
    398 	if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
    399 	    (argv[1] = cbor_build_uint8(4)) == NULL ||
    400 	    (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
    401 	    (argv[3] = cbor_encode_change_pin_auth(dev, ecdh, ppine, opinhe)) == NULL ||
    402 	    (argv[4] = fido_blob_encode(ppine)) == NULL ||
    403 	    (argv[5] = fido_blob_encode(opinhe)) == NULL) {
    404 		fido_log_debug("%s: cbor encode", __func__);
    405 		r = FIDO_ERR_INTERNAL;
    406 		goto fail;
    407 	}
    408 
    409 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
    410 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
    411 		fido_log_debug("%s: fido_tx", __func__);
    412 		r = FIDO_ERR_TX;
    413 		goto fail;
    414 	}
    415 
    416 	r = FIDO_OK;
    417 fail:
    418 	cbor_vector_free(argv, nitems(argv));
    419 	es256_pk_free(&pk);
    420 	fido_blob_free(&ppine);
    421 	fido_blob_free(&ecdh);
    422 	fido_blob_free(&opin);
    423 	fido_blob_free(&opinhe);
    424 	free(f.ptr);
    425 
    426 	return (r);
    427 
    428 }
    429 
    430 static int
    431 fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin, int *ms)
    432 {
    433 	fido_blob_t	 f;
    434 	fido_blob_t	*ppine = NULL;
    435 	fido_blob_t	*ecdh = NULL;
    436 	cbor_item_t	*argv[5];
    437 	es256_pk_t	*pk = NULL;
    438 	int		 r;
    439 
    440 	memset(&f, 0, sizeof(f));
    441 	memset(argv, 0, sizeof(argv));
    442 
    443 	if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
    444 		fido_log_debug("%s: fido_do_ecdh", __func__);
    445 		goto fail;
    446 	}
    447 
    448 	if ((r = pin_pad64_enc(dev, ecdh, pin, &ppine)) != FIDO_OK) {
    449 		fido_log_debug("%s: pin_pad64_enc", __func__);
    450 		goto fail;
    451 	}
    452 
    453 	if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
    454 	    (argv[1] = cbor_build_uint8(3)) == NULL ||
    455 	    (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
    456 	    (argv[3] = cbor_encode_pin_auth(dev, ecdh, ppine)) == NULL ||
    457 	    (argv[4] = fido_blob_encode(ppine)) == NULL) {
    458 		fido_log_debug("%s: cbor encode", __func__);
    459 		r = FIDO_ERR_INTERNAL;
    460 		goto fail;
    461 	}
    462 
    463 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
    464 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
    465 		fido_log_debug("%s: fido_tx", __func__);
    466 		r = FIDO_ERR_TX;
    467 		goto fail;
    468 	}
    469 
    470 	r = FIDO_OK;
    471 fail:
    472 	cbor_vector_free(argv, nitems(argv));
    473 	es256_pk_free(&pk);
    474 	fido_blob_free(&ppine);
    475 	fido_blob_free(&ecdh);
    476 	free(f.ptr);
    477 
    478 	return (r);
    479 }
    480 
    481 static int
    482 fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
    483     int *ms)
    484 {
    485 	int r;
    486 
    487 	if (oldpin != NULL) {
    488 		if ((r = fido_dev_change_pin_tx(dev, pin, oldpin,
    489 		    ms)) != FIDO_OK) {
    490 			fido_log_debug("%s: fido_dev_change_pin_tx", __func__);
    491 			return (r);
    492 		}
    493 	} else {
    494 		if ((r = fido_dev_set_pin_tx(dev, pin, ms)) != FIDO_OK) {
    495 			fido_log_debug("%s: fido_dev_set_pin_tx", __func__);
    496 			return (r);
    497 		}
    498 	}
    499 
    500 	if ((r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
    501 		fido_log_debug("%s: fido_rx_cbor_status", __func__);
    502 		return (r);
    503 	}
    504 
    505 	if (dev->flags & FIDO_DEV_PIN_UNSET) {
    506 		dev->flags &= ~FIDO_DEV_PIN_UNSET;
    507 		dev->flags |= FIDO_DEV_PIN_SET;
    508 	}
    509 
    510 	return (FIDO_OK);
    511 }
    512 
    513 int
    514 fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin)
    515 {
    516 	int ms = dev->timeout_ms;
    517 
    518 	return (fido_dev_set_pin_wait(dev, pin, oldpin, &ms));
    519 }
    520 
    521 static int
    522 parse_retry_count(const uint8_t keyval, const cbor_item_t *key,
    523     const cbor_item_t *val, void *arg)
    524 {
    525 	int		*retries = arg;
    526 	uint64_t	 n;
    527 
    528 	if (cbor_isa_uint(key) == false ||
    529 	    cbor_int_get_width(key) != CBOR_INT_8 ||
    530 	    cbor_get_uint8(key) != keyval) {
    531 		fido_log_debug("%s: cbor type", __func__);
    532 		return (0); /* ignore */
    533 	}
    534 
    535 	if (cbor_decode_uint64(val, &n) < 0 || n > INT_MAX) {
    536 		fido_log_debug("%s: cbor_decode_uint64", __func__);
    537 		return (-1);
    538 	}
    539 
    540 	*retries = (int)n;
    541 
    542 	return (0);
    543 }
    544 
    545 static int
    546 parse_pin_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
    547 {
    548 	return (parse_retry_count(3, key, val, arg));
    549 }
    550 
    551 static int
    552 parse_uv_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
    553 {
    554 	return (parse_retry_count(5, key, val, arg));
    555 }
    556 
    557 static int
    558 fido_dev_get_retry_count_tx(fido_dev_t *dev, uint8_t subcmd, int *ms)
    559 {
    560 	fido_blob_t	 f;
    561 	cbor_item_t	*argv[2];
    562 	int		 r;
    563 
    564 	memset(&f, 0, sizeof(f));
    565 	memset(argv, 0, sizeof(argv));
    566 
    567 	if ((argv[0] = cbor_build_uint8(1)) == NULL ||
    568 	    (argv[1] = cbor_build_uint8(subcmd)) == NULL) {
    569 		r = FIDO_ERR_INTERNAL;
    570 		goto fail;
    571 	}
    572 
    573 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
    574 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
    575 		fido_log_debug("%s: fido_tx", __func__);
    576 		r = FIDO_ERR_TX;
    577 		goto fail;
    578 	}
    579 
    580 	r = FIDO_OK;
    581 fail:
    582 	cbor_vector_free(argv, nitems(argv));
    583 	free(f.ptr);
    584 
    585 	return (r);
    586 }
    587 
    588 static int
    589 fido_dev_get_pin_retry_count_rx(fido_dev_t *dev, int *retries, int *ms)
    590 {
    591 	unsigned char	*msg;
    592 	int		 msglen;
    593 	int		 r;
    594 
    595 	*retries = 0;
    596 
    597 	if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
    598 		r = FIDO_ERR_INTERNAL;
    599 		goto fail;
    600 	}
    601 
    602 	if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
    603 		fido_log_debug("%s: fido_rx", __func__);
    604 		r = FIDO_ERR_RX;
    605 		goto fail;
    606 	}
    607 
    608 	if ((r = cbor_parse_reply(msg, (size_t)msglen, retries,
    609 	    parse_pin_retry_count)) != FIDO_OK) {
    610 		fido_log_debug("%s: parse_pin_retry_count", __func__);
    611 		goto fail;
    612 	}
    613 
    614 	r = FIDO_OK;
    615 fail:
    616 	freezero(msg, FIDO_MAXMSG);
    617 
    618 	return (r);
    619 }
    620 
    621 static int
    622 fido_dev_get_pin_retry_count_wait(fido_dev_t *dev, int *retries, int *ms)
    623 {
    624 	int r;
    625 
    626 	if ((r = fido_dev_get_retry_count_tx(dev, 1, ms)) != FIDO_OK ||
    627 	    (r = fido_dev_get_pin_retry_count_rx(dev, retries, ms)) != FIDO_OK)
    628 		return (r);
    629 
    630 	return (FIDO_OK);
    631 }
    632 
    633 int
    634 fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
    635 {
    636 	int ms = dev->timeout_ms;
    637 
    638 	return (fido_dev_get_pin_retry_count_wait(dev, retries, &ms));
    639 }
    640 
    641 static int
    642 fido_dev_get_uv_retry_count_rx(fido_dev_t *dev, int *retries, int *ms)
    643 {
    644 	unsigned char	*msg;
    645 	int		 msglen;
    646 	int		 r;
    647 
    648 	*retries = 0;
    649 
    650 	if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
    651 		r = FIDO_ERR_INTERNAL;
    652 		goto fail;
    653 	}
    654 
    655 	if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
    656 		fido_log_debug("%s: fido_rx", __func__);
    657 		r = FIDO_ERR_RX;
    658 		goto fail;
    659 	}
    660 
    661 	if ((r = cbor_parse_reply(msg, (size_t)msglen, retries,
    662 	    parse_uv_retry_count)) != FIDO_OK) {
    663 		fido_log_debug("%s: parse_uv_retry_count", __func__);
    664 		goto fail;
    665 	}
    666 
    667 	r = FIDO_OK;
    668 fail:
    669 	freezero(msg, FIDO_MAXMSG);
    670 
    671 	return (r);
    672 }
    673 
    674 static int
    675 fido_dev_get_uv_retry_count_wait(fido_dev_t *dev, int *retries, int *ms)
    676 {
    677 	int r;
    678 
    679 	if ((r = fido_dev_get_retry_count_tx(dev, 7, ms)) != FIDO_OK ||
    680 	    (r = fido_dev_get_uv_retry_count_rx(dev, retries, ms)) != FIDO_OK)
    681 		return (r);
    682 
    683 	return (FIDO_OK);
    684 }
    685 
    686 int
    687 fido_dev_get_uv_retry_count(fido_dev_t *dev, int *retries)
    688 {
    689 	int ms = dev->timeout_ms;
    690 
    691 	return (fido_dev_get_uv_retry_count_wait(dev, retries, &ms));
    692 }
    693 
    694 int
    695 cbor_add_uv_params(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *hmac_data,
    696     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin,
    697     const char *rpid, cbor_item_t **auth, cbor_item_t **opt, int *ms)
    698 {
    699 	fido_blob_t	*token = NULL;
    700 	int		 r;
    701 
    702 	if ((token = fido_blob_new()) == NULL) {
    703 		r = FIDO_ERR_INTERNAL;
    704 		goto fail;
    705 	}
    706 
    707 	if ((r = fido_dev_get_uv_token(dev, cmd, pin, ecdh, pk, rpid,
    708 	    token, ms)) != FIDO_OK) {
    709 		fido_log_debug("%s: fido_dev_get_uv_token", __func__);
    710 		goto fail;
    711 	}
    712 
    713 	if ((*auth = cbor_encode_pin_auth(dev, token, hmac_data)) == NULL ||
    714 	    (*opt = cbor_encode_pin_opt(dev)) == NULL) {
    715 		fido_log_debug("%s: cbor encode", __func__);
    716 		r = FIDO_ERR_INTERNAL;
    717 		goto fail;
    718 	}
    719 
    720 	r = FIDO_OK;
    721 fail:
    722 	fido_blob_free(&token);
    723 
    724 	return (r);
    725 }
    726