xref: /freebsd/contrib/libfido2/src/pin.c (revision 9c77fb6aaa366cbabc80ee1b834bcfe4df135491)
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