xref: /freebsd/contrib/libfido2/src/pin.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
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, int *ms)
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, ms) < 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, int *ms)
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, ms) < 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, ms);
333 	else
334 		r = ctap20_uv_token_tx(dev, pin, ecdh, pk, ms);
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, int *ms)
345 {
346 	return (uv_token_wait(dev, cmd, pin, ecdh, pk, rpid, token, ms));
347 }
348 
349 static int
350 fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin,
351     int *ms)
352 {
353 	fido_blob_t	 f;
354 	fido_blob_t	*ppine = NULL;
355 	fido_blob_t	*ecdh = NULL;
356 	fido_blob_t	*opin = NULL;
357 	fido_blob_t	*opinhe = NULL;
358 	cbor_item_t	*argv[6];
359 	es256_pk_t	*pk = NULL;
360 	int r;
361 
362 	memset(&f, 0, sizeof(f));
363 	memset(argv, 0, sizeof(argv));
364 
365 	if ((opin = fido_blob_new()) == NULL || fido_blob_set(opin,
366 	    (const unsigned char *)oldpin, strlen(oldpin)) < 0) {
367 		fido_log_debug("%s: fido_blob_set", __func__);
368 		r = FIDO_ERR_INVALID_ARGUMENT;
369 		goto fail;
370 	}
371 
372 	if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
373 		fido_log_debug("%s: fido_do_ecdh", __func__);
374 		goto fail;
375 	}
376 
377 	/* pad and encrypt new pin */
378 	if ((r = pin_pad64_enc(dev, ecdh, pin, &ppine)) != FIDO_OK) {
379 		fido_log_debug("%s: pin_pad64_enc", __func__);
380 		goto fail;
381 	}
382 
383 	/* hash and encrypt old pin */
384 	if ((r = pin_sha256_enc(dev, ecdh, opin, &opinhe)) != FIDO_OK) {
385 		fido_log_debug("%s: pin_sha256_enc", __func__);
386 		goto fail;
387 	}
388 
389 	if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
390 	    (argv[1] = cbor_build_uint8(4)) == NULL ||
391 	    (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
392 	    (argv[3] = cbor_encode_change_pin_auth(dev, ecdh, ppine, opinhe)) == NULL ||
393 	    (argv[4] = fido_blob_encode(ppine)) == NULL ||
394 	    (argv[5] = fido_blob_encode(opinhe)) == NULL) {
395 		fido_log_debug("%s: cbor encode", __func__);
396 		r = FIDO_ERR_INTERNAL;
397 		goto fail;
398 	}
399 
400 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
401 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
402 		fido_log_debug("%s: fido_tx", __func__);
403 		r = FIDO_ERR_TX;
404 		goto fail;
405 	}
406 
407 	r = FIDO_OK;
408 fail:
409 	cbor_vector_free(argv, nitems(argv));
410 	es256_pk_free(&pk);
411 	fido_blob_free(&ppine);
412 	fido_blob_free(&ecdh);
413 	fido_blob_free(&opin);
414 	fido_blob_free(&opinhe);
415 	free(f.ptr);
416 
417 	return (r);
418 
419 }
420 
421 static int
422 fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin, int *ms)
423 {
424 	fido_blob_t	 f;
425 	fido_blob_t	*ppine = NULL;
426 	fido_blob_t	*ecdh = NULL;
427 	cbor_item_t	*argv[5];
428 	es256_pk_t	*pk = NULL;
429 	int		 r;
430 
431 	memset(&f, 0, sizeof(f));
432 	memset(argv, 0, sizeof(argv));
433 
434 	if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
435 		fido_log_debug("%s: fido_do_ecdh", __func__);
436 		goto fail;
437 	}
438 
439 	if ((r = pin_pad64_enc(dev, ecdh, pin, &ppine)) != FIDO_OK) {
440 		fido_log_debug("%s: pin_pad64_enc", __func__);
441 		goto fail;
442 	}
443 
444 	if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
445 	    (argv[1] = cbor_build_uint8(3)) == NULL ||
446 	    (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
447 	    (argv[3] = cbor_encode_pin_auth(dev, ecdh, ppine)) == NULL ||
448 	    (argv[4] = fido_blob_encode(ppine)) == NULL) {
449 		fido_log_debug("%s: cbor encode", __func__);
450 		r = FIDO_ERR_INTERNAL;
451 		goto fail;
452 	}
453 
454 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
455 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
456 		fido_log_debug("%s: fido_tx", __func__);
457 		r = FIDO_ERR_TX;
458 		goto fail;
459 	}
460 
461 	r = FIDO_OK;
462 fail:
463 	cbor_vector_free(argv, nitems(argv));
464 	es256_pk_free(&pk);
465 	fido_blob_free(&ppine);
466 	fido_blob_free(&ecdh);
467 	free(f.ptr);
468 
469 	return (r);
470 }
471 
472 static int
473 fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
474     int *ms)
475 {
476 	int r;
477 
478 	if (oldpin != NULL) {
479 		if ((r = fido_dev_change_pin_tx(dev, pin, oldpin,
480 		    ms)) != FIDO_OK) {
481 			fido_log_debug("%s: fido_dev_change_pin_tx", __func__);
482 			return (r);
483 		}
484 	} else {
485 		if ((r = fido_dev_set_pin_tx(dev, pin, ms)) != FIDO_OK) {
486 			fido_log_debug("%s: fido_dev_set_pin_tx", __func__);
487 			return (r);
488 		}
489 	}
490 
491 	if ((r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
492 		fido_log_debug("%s: fido_rx_cbor_status", __func__);
493 		return (r);
494 	}
495 
496 	if (dev->flags & FIDO_DEV_PIN_UNSET) {
497 		dev->flags &= ~FIDO_DEV_PIN_UNSET;
498 		dev->flags |= FIDO_DEV_PIN_SET;
499 	}
500 
501 	return (FIDO_OK);
502 }
503 
504 int
505 fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin)
506 {
507 	int ms = dev->timeout_ms;
508 
509 	return (fido_dev_set_pin_wait(dev, pin, oldpin, &ms));
510 }
511 
512 static int
513 parse_retry_count(const uint8_t keyval, const cbor_item_t *key,
514     const cbor_item_t *val, void *arg)
515 {
516 	int		*retries = arg;
517 	uint64_t	 n;
518 
519 	if (cbor_isa_uint(key) == false ||
520 	    cbor_int_get_width(key) != CBOR_INT_8 ||
521 	    cbor_get_uint8(key) != keyval) {
522 		fido_log_debug("%s: cbor type", __func__);
523 		return (0); /* ignore */
524 	}
525 
526 	if (cbor_decode_uint64(val, &n) < 0 || n > INT_MAX) {
527 		fido_log_debug("%s: cbor_decode_uint64", __func__);
528 		return (-1);
529 	}
530 
531 	*retries = (int)n;
532 
533 	return (0);
534 }
535 
536 static int
537 parse_pin_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
538 {
539 	return (parse_retry_count(3, key, val, arg));
540 }
541 
542 static int
543 parse_uv_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
544 {
545 	return (parse_retry_count(5, key, val, arg));
546 }
547 
548 static int
549 fido_dev_get_retry_count_tx(fido_dev_t *dev, uint8_t subcmd, int *ms)
550 {
551 	fido_blob_t	 f;
552 	cbor_item_t	*argv[2];
553 	int		 r;
554 
555 	memset(&f, 0, sizeof(f));
556 	memset(argv, 0, sizeof(argv));
557 
558 	if ((argv[0] = cbor_build_uint8(1)) == NULL ||
559 	    (argv[1] = cbor_build_uint8(subcmd)) == NULL) {
560 		r = FIDO_ERR_INTERNAL;
561 		goto fail;
562 	}
563 
564 	if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
565 	    &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
566 		fido_log_debug("%s: fido_tx", __func__);
567 		r = FIDO_ERR_TX;
568 		goto fail;
569 	}
570 
571 	r = FIDO_OK;
572 fail:
573 	cbor_vector_free(argv, nitems(argv));
574 	free(f.ptr);
575 
576 	return (r);
577 }
578 
579 static int
580 fido_dev_get_pin_retry_count_rx(fido_dev_t *dev, int *retries, int *ms)
581 {
582 	unsigned char	reply[FIDO_MAXMSG];
583 	int		reply_len;
584 	int		r;
585 
586 	*retries = 0;
587 
588 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
589 	    ms)) < 0) {
590 		fido_log_debug("%s: fido_rx", __func__);
591 		return (FIDO_ERR_RX);
592 	}
593 
594 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, retries,
595 	    parse_pin_retry_count)) != FIDO_OK) {
596 		fido_log_debug("%s: parse_pin_retry_count", __func__);
597 		return (r);
598 	}
599 
600 	return (FIDO_OK);
601 }
602 
603 static int
604 fido_dev_get_pin_retry_count_wait(fido_dev_t *dev, int *retries, int *ms)
605 {
606 	int r;
607 
608 	if ((r = fido_dev_get_retry_count_tx(dev, 1, ms)) != FIDO_OK ||
609 	    (r = fido_dev_get_pin_retry_count_rx(dev, retries, ms)) != FIDO_OK)
610 		return (r);
611 
612 	return (FIDO_OK);
613 }
614 
615 int
616 fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
617 {
618 	int ms = dev->timeout_ms;
619 
620 	return (fido_dev_get_pin_retry_count_wait(dev, retries, &ms));
621 }
622 
623 static int
624 fido_dev_get_uv_retry_count_rx(fido_dev_t *dev, int *retries, int *ms)
625 {
626 	unsigned char	reply[FIDO_MAXMSG];
627 	int		reply_len;
628 	int		r;
629 
630 	*retries = 0;
631 
632 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
633 	    ms)) < 0) {
634 		fido_log_debug("%s: fido_rx", __func__);
635 		return (FIDO_ERR_RX);
636 	}
637 
638 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, retries,
639 	    parse_uv_retry_count)) != FIDO_OK) {
640 		fido_log_debug("%s: parse_uv_retry_count", __func__);
641 		return (r);
642 	}
643 
644 	return (FIDO_OK);
645 }
646 
647 static int
648 fido_dev_get_uv_retry_count_wait(fido_dev_t *dev, int *retries, int *ms)
649 {
650 	int r;
651 
652 	if ((r = fido_dev_get_retry_count_tx(dev, 7, ms)) != FIDO_OK ||
653 	    (r = fido_dev_get_uv_retry_count_rx(dev, retries, ms)) != FIDO_OK)
654 		return (r);
655 
656 	return (FIDO_OK);
657 }
658 
659 int
660 fido_dev_get_uv_retry_count(fido_dev_t *dev, int *retries)
661 {
662 	int ms = dev->timeout_ms;
663 
664 	return (fido_dev_get_uv_retry_count_wait(dev, retries, &ms));
665 }
666 
667 int
668 cbor_add_uv_params(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *hmac_data,
669     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin,
670     const char *rpid, cbor_item_t **auth, cbor_item_t **opt, int *ms)
671 {
672 	fido_blob_t	*token = NULL;
673 	int		 r;
674 
675 	if ((token = fido_blob_new()) == NULL) {
676 		r = FIDO_ERR_INTERNAL;
677 		goto fail;
678 	}
679 
680 	if ((r = fido_dev_get_uv_token(dev, cmd, pin, ecdh, pk, rpid,
681 	    token, ms)) != FIDO_OK) {
682 		fido_log_debug("%s: fido_dev_get_uv_token", __func__);
683 		goto fail;
684 	}
685 
686 	if ((*auth = cbor_encode_pin_auth(dev, token, hmac_data)) == NULL ||
687 	    (*opt = cbor_encode_pin_opt(dev)) == NULL) {
688 		fido_log_debug("%s: cbor encode", __func__);
689 		r = FIDO_ERR_INTERNAL;
690 		goto fail;
691 	}
692 
693 	r = FIDO_OK;
694 fail:
695 	fido_blob_free(&token);
696 
697 	return (r);
698 }
699