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