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
18int
19fido_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
34static int
35pin_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;
62fail:
63	fido_blob_free(&ph);
64
65	return (r);
66}
67
68static int
69pad64(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
95static int
96pin_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;
119fail:
120	fido_blob_free(&ppin);
121
122	return (r);
123}
124
125static cbor_item_t *
126encode_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
147static int
148ctap20_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;
195fail:
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
204static int
205ctap21_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;
258fail:
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
267static int
268parse_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
282static int
283uv_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;
316fail:
317	fido_blob_free(&aes_token);
318
319	return (r);
320}
321
322static int
323uv_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
341int
342fido_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
349static int
350fido_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;
408fail:
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
421static int
422fido_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;
462fail:
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
472static int
473fido_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
504int
505fido_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
512static int
513parse_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
536static int
537parse_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
542static int
543parse_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
548static int
549fido_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;
572fail:
573	cbor_vector_free(argv, nitems(argv));
574	free(f.ptr);
575
576	return (r);
577}
578
579static int
580fido_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
603static int
604fido_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
615int
616fido_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
623static int
624fido_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
647static int
648fido_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
659int
660fido_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
667int
668cbor_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;
694fail:
695	fido_blob_free(&token);
696
697	return (r);
698}
699