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