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