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