xref: /openbsd/lib/libfido2/src/u2f.c (revision ab19a69e)
1d75efeb7Sdjm /*
2*ab19a69eSdjm  * Copyright (c) 2018-2021 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 
7d75efeb7Sdjm #include <openssl/sha.h>
8d75efeb7Sdjm #include <openssl/x509.h>
9d75efeb7Sdjm 
10d75efeb7Sdjm #ifdef HAVE_UNISTD_H
11d75efeb7Sdjm #include <unistd.h>
12d75efeb7Sdjm #endif
13*ab19a69eSdjm #include <errno.h>
14d75efeb7Sdjm 
15d75efeb7Sdjm #include "fido.h"
16d75efeb7Sdjm #include "fido/es256.h"
17d75efeb7Sdjm 
18*ab19a69eSdjm #define U2F_PACE_MS (100)
19*ab19a69eSdjm 
20d75efeb7Sdjm #if defined(_MSC_VER)
21d75efeb7Sdjm static int
usleep(unsigned int usec)22d75efeb7Sdjm usleep(unsigned int usec)
23d75efeb7Sdjm {
24d75efeb7Sdjm 	Sleep(usec / 1000);
25d75efeb7Sdjm 
26d75efeb7Sdjm 	return (0);
27d75efeb7Sdjm }
28d75efeb7Sdjm #endif
29d75efeb7Sdjm 
30d75efeb7Sdjm static int
delay_ms(unsigned int ms,int * ms_remain)31*ab19a69eSdjm delay_ms(unsigned int ms, int *ms_remain)
32*ab19a69eSdjm {
33*ab19a69eSdjm 	if (*ms_remain > -1 && (unsigned int)*ms_remain < ms)
34*ab19a69eSdjm 		ms = (unsigned int)*ms_remain;
35*ab19a69eSdjm 
36*ab19a69eSdjm 	if (ms > UINT_MAX / 1000) {
37*ab19a69eSdjm 		fido_log_debug("%s: ms=%u", __func__, ms);
38*ab19a69eSdjm 		return (-1);
39*ab19a69eSdjm 	}
40*ab19a69eSdjm 
41*ab19a69eSdjm 	if (usleep(ms * 1000) < 0) {
42*ab19a69eSdjm 		fido_log_error(errno, "%s: usleep", __func__);
43*ab19a69eSdjm 		return (-1);
44*ab19a69eSdjm 	}
45*ab19a69eSdjm 
46*ab19a69eSdjm 	if (*ms_remain > -1)
47*ab19a69eSdjm 		*ms_remain -= (int)ms;
48*ab19a69eSdjm 
49*ab19a69eSdjm 	return (0);
50*ab19a69eSdjm }
51*ab19a69eSdjm 
52*ab19a69eSdjm static int
sig_get(fido_blob_t * sig,const unsigned char ** buf,size_t * len)53d75efeb7Sdjm sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len)
54d75efeb7Sdjm {
55d75efeb7Sdjm 	sig->len = *len; /* consume the whole buffer */
56d75efeb7Sdjm 	if ((sig->ptr = calloc(1, sig->len)) == NULL ||
5732a20e26Sdjm 	    fido_buf_read(buf, len, sig->ptr, sig->len) < 0) {
5832a20e26Sdjm 		fido_log_debug("%s: fido_buf_read", __func__);
59c4a807edSdjm 		fido_blob_reset(sig);
60d75efeb7Sdjm 		return (-1);
61d75efeb7Sdjm 	}
62d75efeb7Sdjm 
63d75efeb7Sdjm 	return (0);
64d75efeb7Sdjm }
65d75efeb7Sdjm 
66d75efeb7Sdjm static int
x5c_get(fido_blob_t * x5c,const unsigned char ** buf,size_t * len)67d75efeb7Sdjm x5c_get(fido_blob_t *x5c, const unsigned char **buf, size_t *len)
68d75efeb7Sdjm {
69d75efeb7Sdjm 	X509	*cert = NULL;
70d75efeb7Sdjm 	int	 ok = -1;
71d75efeb7Sdjm 
72d75efeb7Sdjm 	if (*len > LONG_MAX) {
7332a20e26Sdjm 		fido_log_debug("%s: invalid len %zu", __func__, *len);
74d75efeb7Sdjm 		goto fail;
75d75efeb7Sdjm 	}
76d75efeb7Sdjm 
77d75efeb7Sdjm 	/* find out the certificate's length */
78d75efeb7Sdjm 	const unsigned char *end = *buf;
79d75efeb7Sdjm 	if ((cert = d2i_X509(NULL, &end, (long)*len)) == NULL || end <= *buf ||
80d75efeb7Sdjm 	    (x5c->len = (size_t)(end - *buf)) >= *len) {
8132a20e26Sdjm 		fido_log_debug("%s: d2i_X509", __func__);
82d75efeb7Sdjm 		goto fail;
83d75efeb7Sdjm 	}
84d75efeb7Sdjm 
85d75efeb7Sdjm 	/* read accordingly */
86d75efeb7Sdjm 	if ((x5c->ptr = calloc(1, x5c->len)) == NULL ||
8732a20e26Sdjm 	    fido_buf_read(buf, len, x5c->ptr, x5c->len) < 0) {
8832a20e26Sdjm 		fido_log_debug("%s: fido_buf_read", __func__);
89d75efeb7Sdjm 		goto fail;
90d75efeb7Sdjm 	}
91d75efeb7Sdjm 
92d75efeb7Sdjm 	ok = 0;
93d75efeb7Sdjm fail:
94d75efeb7Sdjm 	if (cert != NULL)
95d75efeb7Sdjm 		X509_free(cert);
96d75efeb7Sdjm 
97c4a807edSdjm 	if (ok < 0)
98c4a807edSdjm 		fido_blob_reset(x5c);
99d75efeb7Sdjm 
100d75efeb7Sdjm 	return (ok);
101d75efeb7Sdjm }
102d75efeb7Sdjm 
103d75efeb7Sdjm static int
authdata_fake(const char * rp_id,uint8_t flags,uint32_t sigcount,fido_blob_t * fake_cbor_ad)104d75efeb7Sdjm authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount,
105d75efeb7Sdjm     fido_blob_t *fake_cbor_ad)
106d75efeb7Sdjm {
107d75efeb7Sdjm 	fido_authdata_t	 ad;
108d75efeb7Sdjm 	cbor_item_t	*item = NULL;
109d75efeb7Sdjm 	size_t		 alloc_len;
110d75efeb7Sdjm 
111d75efeb7Sdjm 	memset(&ad, 0, sizeof(ad));
112d75efeb7Sdjm 
113d75efeb7Sdjm 	if (SHA256((const void *)rp_id, strlen(rp_id),
114d75efeb7Sdjm 	    ad.rp_id_hash) != ad.rp_id_hash) {
11532a20e26Sdjm 		fido_log_debug("%s: sha256", __func__);
116d75efeb7Sdjm 		return (-1);
117d75efeb7Sdjm 	}
118d75efeb7Sdjm 
119d75efeb7Sdjm 	ad.flags = flags; /* XXX translate? */
120d75efeb7Sdjm 	ad.sigcount = sigcount;
121d75efeb7Sdjm 
122d75efeb7Sdjm 	if ((item = cbor_build_bytestring((const unsigned char *)&ad,
123d75efeb7Sdjm 	    sizeof(ad))) == NULL) {
12432a20e26Sdjm 		fido_log_debug("%s: cbor_build_bytestring", __func__);
125d75efeb7Sdjm 		return (-1);
126d75efeb7Sdjm 	}
127d75efeb7Sdjm 
128d75efeb7Sdjm 	if (fake_cbor_ad->ptr != NULL ||
129d75efeb7Sdjm 	    (fake_cbor_ad->len = cbor_serialize_alloc(item, &fake_cbor_ad->ptr,
130d75efeb7Sdjm 	    &alloc_len)) == 0) {
13132a20e26Sdjm 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
132d75efeb7Sdjm 		cbor_decref(&item);
133d75efeb7Sdjm 		return (-1);
134d75efeb7Sdjm 	}
135d75efeb7Sdjm 
136d75efeb7Sdjm 	cbor_decref(&item);
137d75efeb7Sdjm 
138d75efeb7Sdjm 	return (0);
139d75efeb7Sdjm }
140d75efeb7Sdjm 
141739189a3Sdjm /* TODO: use u2f_get_touch_begin & u2f_get_touch_status instead */
142d75efeb7Sdjm static int
send_dummy_register(fido_dev_t * dev,int * ms)143*ab19a69eSdjm send_dummy_register(fido_dev_t *dev, int *ms)
144d75efeb7Sdjm {
145d75efeb7Sdjm 	iso7816_apdu_t	*apdu = NULL;
146d75efeb7Sdjm 	unsigned char	 challenge[SHA256_DIGEST_LENGTH];
147d75efeb7Sdjm 	unsigned char	 application[SHA256_DIGEST_LENGTH];
14832a20e26Sdjm 	unsigned char	 reply[FIDO_MAXMSG];
149d75efeb7Sdjm 	int		 r;
150d75efeb7Sdjm 
151d75efeb7Sdjm 	/* dummy challenge & application */
152d75efeb7Sdjm 	memset(&challenge, 0xff, sizeof(challenge));
153d75efeb7Sdjm 	memset(&application, 0xff, sizeof(application));
154d75efeb7Sdjm 
155c4a807edSdjm 	if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
156d75efeb7Sdjm 	    SHA256_DIGEST_LENGTH)) == NULL ||
157d75efeb7Sdjm 	    iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
158d75efeb7Sdjm 	    iso7816_add(apdu, &application, sizeof(application)) < 0) {
15932a20e26Sdjm 		fido_log_debug("%s: iso7816", __func__);
160d75efeb7Sdjm 		r = FIDO_ERR_INTERNAL;
161d75efeb7Sdjm 		goto fail;
162d75efeb7Sdjm 	}
163d75efeb7Sdjm 
164d75efeb7Sdjm 	do {
16532a20e26Sdjm 		if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
166*ab19a69eSdjm 		    iso7816_len(apdu), ms) < 0) {
16732a20e26Sdjm 			fido_log_debug("%s: fido_tx", __func__);
168d75efeb7Sdjm 			r = FIDO_ERR_TX;
169d75efeb7Sdjm 			goto fail;
170d75efeb7Sdjm 		}
17132a20e26Sdjm 		if (fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply), ms) < 2) {
17232a20e26Sdjm 			fido_log_debug("%s: fido_rx", __func__);
173d75efeb7Sdjm 			r = FIDO_ERR_RX;
174d75efeb7Sdjm 			goto fail;
175d75efeb7Sdjm 		}
176*ab19a69eSdjm 		if (delay_ms(U2F_PACE_MS, ms) != 0) {
177*ab19a69eSdjm 			fido_log_debug("%s: delay_ms", __func__);
178d75efeb7Sdjm 			r = FIDO_ERR_RX;
179d75efeb7Sdjm 			goto fail;
180d75efeb7Sdjm 		}
181d75efeb7Sdjm 	} while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
182d75efeb7Sdjm 
183d75efeb7Sdjm 	r = FIDO_OK;
184d75efeb7Sdjm fail:
185d75efeb7Sdjm 	iso7816_free(&apdu);
186d75efeb7Sdjm 
187d75efeb7Sdjm 	return (r);
188d75efeb7Sdjm }
189d75efeb7Sdjm 
190d75efeb7Sdjm static int
key_lookup(fido_dev_t * dev,const char * rp_id,const fido_blob_t * key_id,int * found,int * ms)191d75efeb7Sdjm key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
192*ab19a69eSdjm     int *found, int *ms)
193d75efeb7Sdjm {
194d75efeb7Sdjm 	iso7816_apdu_t	*apdu = NULL;
195d75efeb7Sdjm 	unsigned char	 challenge[SHA256_DIGEST_LENGTH];
196d75efeb7Sdjm 	unsigned char	 rp_id_hash[SHA256_DIGEST_LENGTH];
19732a20e26Sdjm 	unsigned char	 reply[FIDO_MAXMSG];
198d75efeb7Sdjm 	uint8_t		 key_id_len;
199d75efeb7Sdjm 	int		 r;
200d75efeb7Sdjm 
201d75efeb7Sdjm 	if (key_id->len > UINT8_MAX || rp_id == NULL) {
20232a20e26Sdjm 		fido_log_debug("%s: key_id->len=%zu, rp_id=%p", __func__,
203d75efeb7Sdjm 		    key_id->len, (const void *)rp_id);
204d75efeb7Sdjm 		r = FIDO_ERR_INVALID_ARGUMENT;
205d75efeb7Sdjm 		goto fail;
206d75efeb7Sdjm 	}
207d75efeb7Sdjm 
208d75efeb7Sdjm 	memset(&challenge, 0xff, sizeof(challenge));
209d75efeb7Sdjm 	memset(&rp_id_hash, 0, sizeof(rp_id_hash));
210d75efeb7Sdjm 
211d75efeb7Sdjm 	if (SHA256((const void *)rp_id, strlen(rp_id),
212d75efeb7Sdjm 	    rp_id_hash) != rp_id_hash) {
21332a20e26Sdjm 		fido_log_debug("%s: sha256", __func__);
214d75efeb7Sdjm 		r = FIDO_ERR_INTERNAL;
215d75efeb7Sdjm 		goto fail;
216d75efeb7Sdjm 	}
217d75efeb7Sdjm 
218d75efeb7Sdjm 	key_id_len = (uint8_t)key_id->len;
219d75efeb7Sdjm 
220c4a807edSdjm 	if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 *
221739189a3Sdjm 	    SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
222d75efeb7Sdjm 	    iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
223d75efeb7Sdjm 	    iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
224d75efeb7Sdjm 	    iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
225d75efeb7Sdjm 	    iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
22632a20e26Sdjm 		fido_log_debug("%s: iso7816", __func__);
227d75efeb7Sdjm 		r = FIDO_ERR_INTERNAL;
228d75efeb7Sdjm 		goto fail;
229d75efeb7Sdjm 	}
230d75efeb7Sdjm 
23132a20e26Sdjm 	if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
232*ab19a69eSdjm 	    iso7816_len(apdu), ms) < 0) {
23332a20e26Sdjm 		fido_log_debug("%s: fido_tx", __func__);
234d75efeb7Sdjm 		r = FIDO_ERR_TX;
235d75efeb7Sdjm 		goto fail;
236d75efeb7Sdjm 	}
23732a20e26Sdjm 	if (fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply), ms) != 2) {
23832a20e26Sdjm 		fido_log_debug("%s: fido_rx", __func__);
239d75efeb7Sdjm 		r = FIDO_ERR_RX;
240d75efeb7Sdjm 		goto fail;
241d75efeb7Sdjm 	}
242d75efeb7Sdjm 
243d75efeb7Sdjm 	switch ((reply[0] << 8) | reply[1]) {
244d75efeb7Sdjm 	case SW_CONDITIONS_NOT_SATISFIED:
245d75efeb7Sdjm 		*found = 1; /* key exists */
246d75efeb7Sdjm 		break;
247d75efeb7Sdjm 	case SW_WRONG_DATA:
248d75efeb7Sdjm 		*found = 0; /* key does not exist */
249d75efeb7Sdjm 		break;
250d75efeb7Sdjm 	default:
251d75efeb7Sdjm 		/* unexpected sw */
252d75efeb7Sdjm 		r = FIDO_ERR_INTERNAL;
253d75efeb7Sdjm 		goto fail;
254d75efeb7Sdjm 	}
255d75efeb7Sdjm 
256d75efeb7Sdjm 	r = FIDO_OK;
257d75efeb7Sdjm fail:
258d75efeb7Sdjm 	iso7816_free(&apdu);
259d75efeb7Sdjm 
260d75efeb7Sdjm 	return (r);
261d75efeb7Sdjm }
262d75efeb7Sdjm 
263d75efeb7Sdjm static int
parse_auth_reply(fido_blob_t * sig,fido_blob_t * ad,const char * rp_id,const unsigned char * reply,size_t len)264d75efeb7Sdjm parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id,
265d75efeb7Sdjm     const unsigned char *reply, size_t len)
266d75efeb7Sdjm {
267d75efeb7Sdjm 	uint8_t		flags;
268d75efeb7Sdjm 	uint32_t	sigcount;
269d75efeb7Sdjm 
270d75efeb7Sdjm 	if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
27132a20e26Sdjm 		fido_log_debug("%s: unexpected sw", __func__);
272d75efeb7Sdjm 		return (FIDO_ERR_RX);
273d75efeb7Sdjm 	}
274d75efeb7Sdjm 
275d75efeb7Sdjm 	len -= 2;
276d75efeb7Sdjm 
27732a20e26Sdjm 	if (fido_buf_read(&reply, &len, &flags, sizeof(flags)) < 0 ||
27832a20e26Sdjm 	    fido_buf_read(&reply, &len, &sigcount, sizeof(sigcount)) < 0) {
27932a20e26Sdjm 		fido_log_debug("%s: fido_buf_read", __func__);
280d75efeb7Sdjm 		return (FIDO_ERR_RX);
281d75efeb7Sdjm 	}
282d75efeb7Sdjm 
283d75efeb7Sdjm 	if (sig_get(sig, &reply, &len) < 0) {
28432a20e26Sdjm 		fido_log_debug("%s: sig_get", __func__);
285d75efeb7Sdjm 		return (FIDO_ERR_RX);
286d75efeb7Sdjm 	}
287d75efeb7Sdjm 
288d75efeb7Sdjm 	if (authdata_fake(rp_id, flags, sigcount, ad) < 0) {
28932a20e26Sdjm 		fido_log_debug("%s; authdata_fake", __func__);
290d75efeb7Sdjm 		return (FIDO_ERR_RX);
291d75efeb7Sdjm 	}
292d75efeb7Sdjm 
293d75efeb7Sdjm 	return (FIDO_OK);
294d75efeb7Sdjm }
295d75efeb7Sdjm 
296d75efeb7Sdjm static int
do_auth(fido_dev_t * dev,const fido_blob_t * cdh,const char * rp_id,const fido_blob_t * key_id,fido_blob_t * sig,fido_blob_t * ad,int * ms)297d75efeb7Sdjm do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
298*ab19a69eSdjm     const fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int *ms)
299d75efeb7Sdjm {
300d75efeb7Sdjm 	iso7816_apdu_t	*apdu = NULL;
301d75efeb7Sdjm 	unsigned char	 rp_id_hash[SHA256_DIGEST_LENGTH];
30232a20e26Sdjm 	unsigned char	 reply[FIDO_MAXMSG];
303d75efeb7Sdjm 	int		 reply_len;
304d75efeb7Sdjm 	uint8_t		 key_id_len;
305d75efeb7Sdjm 	int		 r;
306d75efeb7Sdjm 
307d75efeb7Sdjm #ifdef FIDO_FUZZ
308*ab19a69eSdjm 	*ms = 0; /* XXX */
309d75efeb7Sdjm #endif
310d75efeb7Sdjm 
311d75efeb7Sdjm 	if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX ||
312d75efeb7Sdjm 	    rp_id == NULL) {
313d75efeb7Sdjm 		r = FIDO_ERR_INVALID_ARGUMENT;
314d75efeb7Sdjm 		goto fail;
315d75efeb7Sdjm 	}
316d75efeb7Sdjm 
317d75efeb7Sdjm 	memset(&rp_id_hash, 0, sizeof(rp_id_hash));
318d75efeb7Sdjm 
319d75efeb7Sdjm 	if (SHA256((const void *)rp_id, strlen(rp_id),
320d75efeb7Sdjm 	    rp_id_hash) != rp_id_hash) {
32132a20e26Sdjm 		fido_log_debug("%s: sha256", __func__);
322d75efeb7Sdjm 		r = FIDO_ERR_INTERNAL;
323d75efeb7Sdjm 		goto fail;
324d75efeb7Sdjm 	}
325d75efeb7Sdjm 
326d75efeb7Sdjm 	key_id_len = (uint8_t)key_id->len;
327d75efeb7Sdjm 
328c4a807edSdjm 	if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 *
329739189a3Sdjm 	    SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
330d75efeb7Sdjm 	    iso7816_add(apdu, cdh->ptr, cdh->len) < 0 ||
331d75efeb7Sdjm 	    iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
332d75efeb7Sdjm 	    iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
333d75efeb7Sdjm 	    iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
33432a20e26Sdjm 		fido_log_debug("%s: iso7816", __func__);
335d75efeb7Sdjm 		r = FIDO_ERR_INTERNAL;
336d75efeb7Sdjm 		goto fail;
337d75efeb7Sdjm 	}
338d75efeb7Sdjm 
339d75efeb7Sdjm 	do {
34032a20e26Sdjm 		if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
341*ab19a69eSdjm 		    iso7816_len(apdu), ms) < 0) {
34232a20e26Sdjm 			fido_log_debug("%s: fido_tx", __func__);
343d75efeb7Sdjm 			r = FIDO_ERR_TX;
344d75efeb7Sdjm 			goto fail;
345d75efeb7Sdjm 		}
34632a20e26Sdjm 		if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, &reply,
34732a20e26Sdjm 		    sizeof(reply), ms)) < 2) {
34832a20e26Sdjm 			fido_log_debug("%s: fido_rx", __func__);
349d75efeb7Sdjm 			r = FIDO_ERR_RX;
350d75efeb7Sdjm 			goto fail;
351d75efeb7Sdjm 		}
352*ab19a69eSdjm 		if (delay_ms(U2F_PACE_MS, ms) != 0) {
353*ab19a69eSdjm 			fido_log_debug("%s: delay_ms", __func__);
354d75efeb7Sdjm 			r = FIDO_ERR_RX;
355d75efeb7Sdjm 			goto fail;
356d75efeb7Sdjm 		}
357d75efeb7Sdjm 	} while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
358d75efeb7Sdjm 
359d75efeb7Sdjm 	if ((r = parse_auth_reply(sig, ad, rp_id, reply,
360d75efeb7Sdjm 	    (size_t)reply_len)) != FIDO_OK) {
36132a20e26Sdjm 		fido_log_debug("%s: parse_auth_reply", __func__);
362d75efeb7Sdjm 		goto fail;
363d75efeb7Sdjm 	}
364d75efeb7Sdjm 
365d75efeb7Sdjm fail:
366d75efeb7Sdjm 	iso7816_free(&apdu);
367d75efeb7Sdjm 
368d75efeb7Sdjm 	return (r);
369d75efeb7Sdjm }
370d75efeb7Sdjm 
371d75efeb7Sdjm static int
cbor_blob_from_ec_point(const uint8_t * ec_point,size_t ec_point_len,fido_blob_t * cbor_blob)372d75efeb7Sdjm cbor_blob_from_ec_point(const uint8_t *ec_point, size_t ec_point_len,
373d75efeb7Sdjm     fido_blob_t *cbor_blob)
374d75efeb7Sdjm {
375d75efeb7Sdjm 	es256_pk_t	*pk = NULL;
376d75efeb7Sdjm 	cbor_item_t	*pk_cbor = NULL;
377d75efeb7Sdjm 	size_t		 alloc_len;
378d75efeb7Sdjm 	int		 ok = -1;
379d75efeb7Sdjm 
380d75efeb7Sdjm 	/* only handle uncompressed points */
381d75efeb7Sdjm 	if (ec_point_len != 65 || ec_point[0] != 0x04) {
38232a20e26Sdjm 		fido_log_debug("%s: unexpected format", __func__);
383d75efeb7Sdjm 		goto fail;
384d75efeb7Sdjm 	}
385d75efeb7Sdjm 
386d75efeb7Sdjm 	if ((pk = es256_pk_new()) == NULL ||
387d75efeb7Sdjm 	    es256_pk_set_x(pk, &ec_point[1]) < 0 ||
388d75efeb7Sdjm 	    es256_pk_set_y(pk, &ec_point[33]) < 0) {
38932a20e26Sdjm 		fido_log_debug("%s: es256_pk_set", __func__);
390d75efeb7Sdjm 		goto fail;
391d75efeb7Sdjm 	}
392d75efeb7Sdjm 
393d75efeb7Sdjm 	if ((pk_cbor = es256_pk_encode(pk, 0)) == NULL) {
39432a20e26Sdjm 		fido_log_debug("%s: es256_pk_encode", __func__);
395d75efeb7Sdjm 		goto fail;
396d75efeb7Sdjm 	}
397d75efeb7Sdjm 
398d75efeb7Sdjm 	if ((cbor_blob->len = cbor_serialize_alloc(pk_cbor, &cbor_blob->ptr,
399d75efeb7Sdjm 	    &alloc_len)) != 77) {
40032a20e26Sdjm 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
401d75efeb7Sdjm 		goto fail;
402d75efeb7Sdjm 	}
403d75efeb7Sdjm 
404d75efeb7Sdjm 	ok = 0;
405d75efeb7Sdjm fail:
406d75efeb7Sdjm 	es256_pk_free(&pk);
407d75efeb7Sdjm 
408d75efeb7Sdjm 	if (pk_cbor)
409d75efeb7Sdjm 		cbor_decref(&pk_cbor);
410d75efeb7Sdjm 
411d75efeb7Sdjm 	return (ok);
412d75efeb7Sdjm }
413d75efeb7Sdjm 
414d75efeb7Sdjm static int
encode_cred_attstmt(int cose_alg,const fido_blob_t * x5c,const fido_blob_t * sig,fido_blob_t * out)415*ab19a69eSdjm encode_cred_attstmt(int cose_alg, const fido_blob_t *x5c,
416*ab19a69eSdjm     const fido_blob_t *sig, fido_blob_t *out)
417*ab19a69eSdjm {
418*ab19a69eSdjm 	cbor_item_t		*item = NULL;
419*ab19a69eSdjm 	cbor_item_t		*x5c_cbor = NULL;
420*ab19a69eSdjm 	const uint8_t		 alg_cbor = (uint8_t)(-cose_alg - 1);
421*ab19a69eSdjm 	struct cbor_pair	 kv[3];
422*ab19a69eSdjm 	size_t			 alloc_len;
423*ab19a69eSdjm 	int			 ok = -1;
424*ab19a69eSdjm 
425*ab19a69eSdjm 	memset(&kv, 0, sizeof(kv));
426*ab19a69eSdjm 	memset(out, 0, sizeof(*out));
427*ab19a69eSdjm 
428*ab19a69eSdjm 	if ((item = cbor_new_definite_map(3)) == NULL) {
429*ab19a69eSdjm 		fido_log_debug("%s: cbor_new_definite_map", __func__);
430*ab19a69eSdjm 		goto fail;
431*ab19a69eSdjm 	}
432*ab19a69eSdjm 
433*ab19a69eSdjm 	if ((kv[0].key = cbor_build_string("alg")) == NULL ||
434*ab19a69eSdjm 	    (kv[0].value = cbor_build_negint8(alg_cbor)) == NULL ||
435*ab19a69eSdjm 	    !cbor_map_add(item, kv[0])) {
436*ab19a69eSdjm 		fido_log_debug("%s: alg", __func__);
437*ab19a69eSdjm 		goto fail;
438*ab19a69eSdjm 	}
439*ab19a69eSdjm 
440*ab19a69eSdjm 	if ((kv[1].key = cbor_build_string("sig")) == NULL ||
441*ab19a69eSdjm 	    (kv[1].value = fido_blob_encode(sig)) == NULL ||
442*ab19a69eSdjm 	    !cbor_map_add(item, kv[1])) {
443*ab19a69eSdjm 		fido_log_debug("%s: sig", __func__);
444*ab19a69eSdjm 		goto fail;
445*ab19a69eSdjm 	}
446*ab19a69eSdjm 
447*ab19a69eSdjm 	if ((kv[2].key = cbor_build_string("x5c")) == NULL ||
448*ab19a69eSdjm 	    (kv[2].value = cbor_new_definite_array(1)) == NULL ||
449*ab19a69eSdjm 	    (x5c_cbor = fido_blob_encode(x5c)) == NULL ||
450*ab19a69eSdjm 	    !cbor_array_push(kv[2].value, x5c_cbor) ||
451*ab19a69eSdjm 	    !cbor_map_add(item, kv[2])) {
452*ab19a69eSdjm 		fido_log_debug("%s: x5c", __func__);
453*ab19a69eSdjm 		goto fail;
454*ab19a69eSdjm 	}
455*ab19a69eSdjm 
456*ab19a69eSdjm 	if ((out->len = cbor_serialize_alloc(item, &out->ptr,
457*ab19a69eSdjm 	    &alloc_len)) == 0) {
458*ab19a69eSdjm 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
459*ab19a69eSdjm 		goto fail;
460*ab19a69eSdjm 	}
461*ab19a69eSdjm 
462*ab19a69eSdjm 	ok = 0;
463*ab19a69eSdjm fail:
464*ab19a69eSdjm 	if (item != NULL)
465*ab19a69eSdjm 		cbor_decref(&item);
466*ab19a69eSdjm 	if (x5c_cbor != NULL)
467*ab19a69eSdjm 		cbor_decref(&x5c_cbor);
468*ab19a69eSdjm 
469*ab19a69eSdjm 	for (size_t i = 0; i < nitems(kv); i++) {
470*ab19a69eSdjm 		if (kv[i].key)
471*ab19a69eSdjm 			cbor_decref(&kv[i].key);
472*ab19a69eSdjm 		if (kv[i].value)
473*ab19a69eSdjm 			cbor_decref(&kv[i].value);
474*ab19a69eSdjm 	}
475*ab19a69eSdjm 
476*ab19a69eSdjm 	return (ok);
477*ab19a69eSdjm }
478*ab19a69eSdjm 
479*ab19a69eSdjm static int
encode_cred_authdata(const char * rp_id,const uint8_t * kh,uint8_t kh_len,const uint8_t * pubkey,size_t pubkey_len,fido_blob_t * out)480d75efeb7Sdjm encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len,
481d75efeb7Sdjm     const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out)
482d75efeb7Sdjm {
483d75efeb7Sdjm 	fido_authdata_t	 	 authdata;
484d75efeb7Sdjm 	fido_attcred_raw_t	 attcred_raw;
485d75efeb7Sdjm 	fido_blob_t		 pk_blob;
486d75efeb7Sdjm 	fido_blob_t		 authdata_blob;
487d75efeb7Sdjm 	cbor_item_t		*authdata_cbor = NULL;
488d75efeb7Sdjm 	unsigned char		*ptr;
489d75efeb7Sdjm 	size_t			 len;
490d75efeb7Sdjm 	size_t			 alloc_len;
491d75efeb7Sdjm 	int			 ok = -1;
492d75efeb7Sdjm 
493d75efeb7Sdjm 	memset(&pk_blob, 0, sizeof(pk_blob));
494d75efeb7Sdjm 	memset(&authdata, 0, sizeof(authdata));
495d75efeb7Sdjm 	memset(&authdata_blob, 0, sizeof(authdata_blob));
496d75efeb7Sdjm 	memset(out, 0, sizeof(*out));
497d75efeb7Sdjm 
498d75efeb7Sdjm 	if (rp_id == NULL) {
49932a20e26Sdjm 		fido_log_debug("%s: NULL rp_id", __func__);
500d75efeb7Sdjm 		goto fail;
501d75efeb7Sdjm 	}
502d75efeb7Sdjm 
503d75efeb7Sdjm 	if (cbor_blob_from_ec_point(pubkey, pubkey_len, &pk_blob) < 0) {
50432a20e26Sdjm 		fido_log_debug("%s: cbor_blob_from_ec_point", __func__);
505d75efeb7Sdjm 		goto fail;
506d75efeb7Sdjm 	}
507d75efeb7Sdjm 
508d75efeb7Sdjm 	if (SHA256((const void *)rp_id, strlen(rp_id),
509d75efeb7Sdjm 	    authdata.rp_id_hash) != authdata.rp_id_hash) {
51032a20e26Sdjm 		fido_log_debug("%s: sha256", __func__);
511d75efeb7Sdjm 		goto fail;
512d75efeb7Sdjm 	}
513d75efeb7Sdjm 
514d75efeb7Sdjm 	authdata.flags = (CTAP_AUTHDATA_ATT_CRED | CTAP_AUTHDATA_USER_PRESENT);
515d75efeb7Sdjm 	authdata.sigcount = 0;
516d75efeb7Sdjm 
517d75efeb7Sdjm 	memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid));
51832a20e26Sdjm 	attcred_raw.id_len = htobe16(kh_len);
519d75efeb7Sdjm 
520d75efeb7Sdjm 	len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) +
521d75efeb7Sdjm 	    kh_len + pk_blob.len;
522d75efeb7Sdjm 	ptr = authdata_blob.ptr = calloc(1, authdata_blob.len);
523d75efeb7Sdjm 
52432a20e26Sdjm 	fido_log_debug("%s: ptr=%p, len=%zu", __func__, (void *)ptr, len);
525d75efeb7Sdjm 
526d75efeb7Sdjm 	if (authdata_blob.ptr == NULL)
527d75efeb7Sdjm 		goto fail;
528d75efeb7Sdjm 
52932a20e26Sdjm 	if (fido_buf_write(&ptr, &len, &authdata, sizeof(authdata)) < 0 ||
53032a20e26Sdjm 	    fido_buf_write(&ptr, &len, &attcred_raw, sizeof(attcred_raw)) < 0 ||
53132a20e26Sdjm 	    fido_buf_write(&ptr, &len, kh, kh_len) < 0 ||
53232a20e26Sdjm 	    fido_buf_write(&ptr, &len, pk_blob.ptr, pk_blob.len) < 0) {
53332a20e26Sdjm 		fido_log_debug("%s: fido_buf_write", __func__);
534d75efeb7Sdjm 		goto fail;
535d75efeb7Sdjm 	}
536d75efeb7Sdjm 
537d75efeb7Sdjm 	if ((authdata_cbor = fido_blob_encode(&authdata_blob)) == NULL) {
53832a20e26Sdjm 		fido_log_debug("%s: fido_blob_encode", __func__);
539d75efeb7Sdjm 		goto fail;
540d75efeb7Sdjm 	}
541d75efeb7Sdjm 
542d75efeb7Sdjm 	if ((out->len = cbor_serialize_alloc(authdata_cbor, &out->ptr,
543d75efeb7Sdjm 	    &alloc_len)) == 0) {
54432a20e26Sdjm 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
545d75efeb7Sdjm 		goto fail;
546d75efeb7Sdjm 	}
547d75efeb7Sdjm 
548d75efeb7Sdjm 	ok = 0;
549d75efeb7Sdjm fail:
550d75efeb7Sdjm 	if (authdata_cbor)
551d75efeb7Sdjm 		cbor_decref(&authdata_cbor);
552d75efeb7Sdjm 
553c4a807edSdjm 	fido_blob_reset(&pk_blob);
554c4a807edSdjm 	fido_blob_reset(&authdata_blob);
555d75efeb7Sdjm 
556d75efeb7Sdjm 	return (ok);
557d75efeb7Sdjm }
558d75efeb7Sdjm 
559d75efeb7Sdjm static int
parse_register_reply(fido_cred_t * cred,const unsigned char * reply,size_t len)560d75efeb7Sdjm parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
561d75efeb7Sdjm {
562d75efeb7Sdjm 	fido_blob_t	 x5c;
563d75efeb7Sdjm 	fido_blob_t	 sig;
564d75efeb7Sdjm 	fido_blob_t	 ad;
565*ab19a69eSdjm 	fido_blob_t	 stmt;
566d75efeb7Sdjm 	uint8_t		 dummy;
567d75efeb7Sdjm 	uint8_t		 pubkey[65];
568d75efeb7Sdjm 	uint8_t		 kh_len = 0;
569d75efeb7Sdjm 	uint8_t		*kh = NULL;
570d75efeb7Sdjm 	int		 r;
571d75efeb7Sdjm 
572d75efeb7Sdjm 	memset(&x5c, 0, sizeof(x5c));
573d75efeb7Sdjm 	memset(&sig, 0, sizeof(sig));
574d75efeb7Sdjm 	memset(&ad, 0, sizeof(ad));
575*ab19a69eSdjm 	memset(&stmt, 0, sizeof(stmt));
576d75efeb7Sdjm 	r = FIDO_ERR_RX;
577d75efeb7Sdjm 
578d75efeb7Sdjm 	/* status word */
579d75efeb7Sdjm 	if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
58032a20e26Sdjm 		fido_log_debug("%s: unexpected sw", __func__);
581d75efeb7Sdjm 		goto fail;
582d75efeb7Sdjm 	}
583d75efeb7Sdjm 
584d75efeb7Sdjm 	len -= 2;
585d75efeb7Sdjm 
586d75efeb7Sdjm 	/* reserved byte */
58732a20e26Sdjm 	if (fido_buf_read(&reply, &len, &dummy, sizeof(dummy)) < 0 ||
588d75efeb7Sdjm 	    dummy != 0x05) {
58932a20e26Sdjm 		fido_log_debug("%s: reserved byte", __func__);
590d75efeb7Sdjm 		goto fail;
591d75efeb7Sdjm 	}
592d75efeb7Sdjm 
593d75efeb7Sdjm 	/* pubkey + key handle */
59432a20e26Sdjm 	if (fido_buf_read(&reply, &len, &pubkey, sizeof(pubkey)) < 0 ||
59532a20e26Sdjm 	    fido_buf_read(&reply, &len, &kh_len, sizeof(kh_len)) < 0 ||
596d75efeb7Sdjm 	    (kh = calloc(1, kh_len)) == NULL ||
59732a20e26Sdjm 	    fido_buf_read(&reply, &len, kh, kh_len) < 0) {
59832a20e26Sdjm 		fido_log_debug("%s: fido_buf_read", __func__);
599d75efeb7Sdjm 		goto fail;
600d75efeb7Sdjm 	}
601d75efeb7Sdjm 
602d75efeb7Sdjm 	/* x5c + sig */
603d75efeb7Sdjm 	if (x5c_get(&x5c, &reply, &len) < 0 ||
604d75efeb7Sdjm 	    sig_get(&sig, &reply, &len) < 0) {
60532a20e26Sdjm 		fido_log_debug("%s: x5c || sig", __func__);
606d75efeb7Sdjm 		goto fail;
607d75efeb7Sdjm 	}
608d75efeb7Sdjm 
609*ab19a69eSdjm 	/* attstmt */
610*ab19a69eSdjm 	if (encode_cred_attstmt(COSE_ES256, &x5c, &sig, &stmt) < 0) {
611*ab19a69eSdjm 		fido_log_debug("%s: encode_cred_attstmt", __func__);
612*ab19a69eSdjm 		goto fail;
613*ab19a69eSdjm 	}
614*ab19a69eSdjm 
615d75efeb7Sdjm 	/* authdata */
616d75efeb7Sdjm 	if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey,
617d75efeb7Sdjm 	    sizeof(pubkey), &ad) < 0) {
61832a20e26Sdjm 		fido_log_debug("%s: encode_cred_authdata", __func__);
619d75efeb7Sdjm 		goto fail;
620d75efeb7Sdjm 	}
621d75efeb7Sdjm 
622d75efeb7Sdjm 	if (fido_cred_set_fmt(cred, "fido-u2f") != FIDO_OK ||
623d75efeb7Sdjm 	    fido_cred_set_authdata(cred, ad.ptr, ad.len) != FIDO_OK ||
624*ab19a69eSdjm 	    fido_cred_set_attstmt(cred, stmt.ptr, stmt.len) != FIDO_OK) {
62532a20e26Sdjm 		fido_log_debug("%s: fido_cred_set", __func__);
626d75efeb7Sdjm 		r = FIDO_ERR_INTERNAL;
627d75efeb7Sdjm 		goto fail;
628d75efeb7Sdjm 	}
629d75efeb7Sdjm 
630d75efeb7Sdjm 	r = FIDO_OK;
631d75efeb7Sdjm fail:
632c4a807edSdjm 	freezero(kh, kh_len);
633c4a807edSdjm 	fido_blob_reset(&x5c);
634c4a807edSdjm 	fido_blob_reset(&sig);
635c4a807edSdjm 	fido_blob_reset(&ad);
636*ab19a69eSdjm 	fido_blob_reset(&stmt);
637d75efeb7Sdjm 
638d75efeb7Sdjm 	return (r);
639d75efeb7Sdjm }
640d75efeb7Sdjm 
641d75efeb7Sdjm int
u2f_register(fido_dev_t * dev,fido_cred_t * cred,int * ms)642*ab19a69eSdjm u2f_register(fido_dev_t *dev, fido_cred_t *cred, int *ms)
643d75efeb7Sdjm {
644d75efeb7Sdjm 	iso7816_apdu_t	*apdu = NULL;
645d75efeb7Sdjm 	unsigned char	 rp_id_hash[SHA256_DIGEST_LENGTH];
64632a20e26Sdjm 	unsigned char	 reply[FIDO_MAXMSG];
647d75efeb7Sdjm 	int		 reply_len;
648d75efeb7Sdjm 	int		 found;
649d75efeb7Sdjm 	int		 r;
650d75efeb7Sdjm 
651d75efeb7Sdjm 	if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) {
65232a20e26Sdjm 		fido_log_debug("%s: rk=%d, uv=%d", __func__, cred->rk,
65332a20e26Sdjm 		    cred->uv);
654d75efeb7Sdjm 		return (FIDO_ERR_UNSUPPORTED_OPTION);
655d75efeb7Sdjm 	}
656d75efeb7Sdjm 
657d75efeb7Sdjm 	if (cred->type != COSE_ES256 || cred->cdh.ptr == NULL ||
658d75efeb7Sdjm 	    cred->rp.id == NULL || cred->cdh.len != SHA256_DIGEST_LENGTH) {
65932a20e26Sdjm 		fido_log_debug("%s: type=%d, cdh=(%p,%zu)" , __func__,
66032a20e26Sdjm 		    cred->type, (void *)cred->cdh.ptr, cred->cdh.len);
661d75efeb7Sdjm 		return (FIDO_ERR_INVALID_ARGUMENT);
662d75efeb7Sdjm 	}
663d75efeb7Sdjm 
664d75efeb7Sdjm 	for (size_t i = 0; i < cred->excl.len; i++) {
665d75efeb7Sdjm 		if ((r = key_lookup(dev, cred->rp.id, &cred->excl.ptr[i],
666d75efeb7Sdjm 		    &found, ms)) != FIDO_OK) {
66732a20e26Sdjm 			fido_log_debug("%s: key_lookup", __func__);
668d75efeb7Sdjm 			return (r);
669d75efeb7Sdjm 		}
670d75efeb7Sdjm 		if (found) {
671d75efeb7Sdjm 			if ((r = send_dummy_register(dev, ms)) != FIDO_OK) {
67232a20e26Sdjm 				fido_log_debug("%s: send_dummy_register",
67332a20e26Sdjm 				    __func__);
674d75efeb7Sdjm 				return (r);
675d75efeb7Sdjm 			}
676d75efeb7Sdjm 			return (FIDO_ERR_CREDENTIAL_EXCLUDED);
677d75efeb7Sdjm 		}
678d75efeb7Sdjm 	}
679d75efeb7Sdjm 
680d75efeb7Sdjm 	memset(&rp_id_hash, 0, sizeof(rp_id_hash));
681d75efeb7Sdjm 
682d75efeb7Sdjm 	if (SHA256((const void *)cred->rp.id, strlen(cred->rp.id),
683d75efeb7Sdjm 	    rp_id_hash) != rp_id_hash) {
68432a20e26Sdjm 		fido_log_debug("%s: sha256", __func__);
685d75efeb7Sdjm 		return (FIDO_ERR_INTERNAL);
686d75efeb7Sdjm 	}
687d75efeb7Sdjm 
688c4a807edSdjm 	if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
689d75efeb7Sdjm 	    SHA256_DIGEST_LENGTH)) == NULL ||
690d75efeb7Sdjm 	    iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 ||
691d75efeb7Sdjm 	    iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
69232a20e26Sdjm 		fido_log_debug("%s: iso7816", __func__);
693d75efeb7Sdjm 		r = FIDO_ERR_INTERNAL;
694d75efeb7Sdjm 		goto fail;
695d75efeb7Sdjm 	}
696d75efeb7Sdjm 
697d75efeb7Sdjm 	do {
69832a20e26Sdjm 		if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
699*ab19a69eSdjm 		    iso7816_len(apdu), ms) < 0) {
70032a20e26Sdjm 			fido_log_debug("%s: fido_tx", __func__);
701d75efeb7Sdjm 			r = FIDO_ERR_TX;
702d75efeb7Sdjm 			goto fail;
703d75efeb7Sdjm 		}
70432a20e26Sdjm 		if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, &reply,
70532a20e26Sdjm 		    sizeof(reply), ms)) < 2) {
70632a20e26Sdjm 			fido_log_debug("%s: fido_rx", __func__);
707d75efeb7Sdjm 			r = FIDO_ERR_RX;
708d75efeb7Sdjm 			goto fail;
709d75efeb7Sdjm 		}
710*ab19a69eSdjm 		if (delay_ms(U2F_PACE_MS, ms) != 0) {
711*ab19a69eSdjm 			fido_log_debug("%s: delay_ms", __func__);
712d75efeb7Sdjm 			r = FIDO_ERR_RX;
713d75efeb7Sdjm 			goto fail;
714d75efeb7Sdjm 		}
715d75efeb7Sdjm 	} while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
716d75efeb7Sdjm 
717d75efeb7Sdjm 	if ((r = parse_register_reply(cred, reply,
718d75efeb7Sdjm 	    (size_t)reply_len)) != FIDO_OK) {
71932a20e26Sdjm 		fido_log_debug("%s: parse_register_reply", __func__);
720d75efeb7Sdjm 		goto fail;
721d75efeb7Sdjm 	}
722d75efeb7Sdjm fail:
723d75efeb7Sdjm 	iso7816_free(&apdu);
724d75efeb7Sdjm 
725d75efeb7Sdjm 	return (r);
726d75efeb7Sdjm }
727d75efeb7Sdjm 
728d75efeb7Sdjm static int
u2f_authenticate_single(fido_dev_t * dev,const fido_blob_t * key_id,fido_assert_t * fa,size_t idx,int * ms)729d75efeb7Sdjm u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id,
730*ab19a69eSdjm     fido_assert_t *fa, size_t idx, int *ms)
731d75efeb7Sdjm {
732d75efeb7Sdjm 	fido_blob_t	sig;
733d75efeb7Sdjm 	fido_blob_t	ad;
734d75efeb7Sdjm 	int		found;
735d75efeb7Sdjm 	int		r;
736d75efeb7Sdjm 
737d75efeb7Sdjm 	memset(&sig, 0, sizeof(sig));
738d75efeb7Sdjm 	memset(&ad, 0, sizeof(ad));
739d75efeb7Sdjm 
740d75efeb7Sdjm 	if ((r = key_lookup(dev, fa->rp_id, key_id, &found, ms)) != FIDO_OK) {
74132a20e26Sdjm 		fido_log_debug("%s: key_lookup", __func__);
742d75efeb7Sdjm 		goto fail;
743d75efeb7Sdjm 	}
744d75efeb7Sdjm 
745d75efeb7Sdjm 	if (!found) {
74632a20e26Sdjm 		fido_log_debug("%s: not found", __func__);
747d75efeb7Sdjm 		r = FIDO_ERR_CREDENTIAL_EXCLUDED;
748d75efeb7Sdjm 		goto fail;
749d75efeb7Sdjm 	}
750d75efeb7Sdjm 
75132a20e26Sdjm 	if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0) {
75232a20e26Sdjm 		fido_log_debug("%s: fido_blob_set", __func__);
75332a20e26Sdjm 		r = FIDO_ERR_INTERNAL;
75432a20e26Sdjm 		goto fail;
75532a20e26Sdjm 	}
75632a20e26Sdjm 
757d75efeb7Sdjm 	if (fa->up == FIDO_OPT_FALSE) {
75832a20e26Sdjm 		fido_log_debug("%s: checking for key existence only", __func__);
759d75efeb7Sdjm 		r = FIDO_ERR_USER_PRESENCE_REQUIRED;
760d75efeb7Sdjm 		goto fail;
761d75efeb7Sdjm 	}
762d75efeb7Sdjm 
763d75efeb7Sdjm 	if ((r = do_auth(dev, &fa->cdh, fa->rp_id, key_id, &sig, &ad,
764d75efeb7Sdjm 	    ms)) != FIDO_OK) {
76532a20e26Sdjm 		fido_log_debug("%s: do_auth", __func__);
766d75efeb7Sdjm 		goto fail;
767d75efeb7Sdjm 	}
768d75efeb7Sdjm 
76932a20e26Sdjm 	if (fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK ||
770d75efeb7Sdjm 	    fido_assert_set_sig(fa, idx, sig.ptr, sig.len) != FIDO_OK) {
77132a20e26Sdjm 		fido_log_debug("%s: fido_assert_set", __func__);
772d75efeb7Sdjm 		r = FIDO_ERR_INTERNAL;
773d75efeb7Sdjm 		goto fail;
774d75efeb7Sdjm 	}
775d75efeb7Sdjm 
776d75efeb7Sdjm 	r = FIDO_OK;
777d75efeb7Sdjm fail:
778c4a807edSdjm 	fido_blob_reset(&sig);
779c4a807edSdjm 	fido_blob_reset(&ad);
780d75efeb7Sdjm 
781d75efeb7Sdjm 	return (r);
782d75efeb7Sdjm }
783d75efeb7Sdjm 
784d75efeb7Sdjm int
u2f_authenticate(fido_dev_t * dev,fido_assert_t * fa,int * ms)785*ab19a69eSdjm u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int *ms)
786d75efeb7Sdjm {
787739189a3Sdjm 	size_t	nfound = 0;
788739189a3Sdjm 	size_t	nauth_ok = 0;
789d75efeb7Sdjm 	int	r;
790d75efeb7Sdjm 
791d75efeb7Sdjm 	if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) {
79232a20e26Sdjm 		fido_log_debug("%s: uv=%d, allow_list=%p", __func__, fa->uv,
793d75efeb7Sdjm 		    (void *)fa->allow_list.ptr);
794d75efeb7Sdjm 		return (FIDO_ERR_UNSUPPORTED_OPTION);
795d75efeb7Sdjm 	}
796d75efeb7Sdjm 
797d75efeb7Sdjm 	if ((r = fido_assert_set_count(fa, fa->allow_list.len)) != FIDO_OK) {
79832a20e26Sdjm 		fido_log_debug("%s: fido_assert_set_count", __func__);
799d75efeb7Sdjm 		return (r);
800d75efeb7Sdjm 	}
801d75efeb7Sdjm 
802d75efeb7Sdjm 	for (size_t i = 0; i < fa->allow_list.len; i++) {
80332a20e26Sdjm 		switch ((r = u2f_authenticate_single(dev,
80432a20e26Sdjm 		    &fa->allow_list.ptr[i], fa, nfound, ms))) {
80532a20e26Sdjm 		case FIDO_OK:
806d75efeb7Sdjm 			nauth_ok++;
80732a20e26Sdjm 			/* FALLTHROUGH */
80832a20e26Sdjm 		case FIDO_ERR_USER_PRESENCE_REQUIRED:
80932a20e26Sdjm 			nfound++;
81032a20e26Sdjm 			break;
81132a20e26Sdjm 		default:
81232a20e26Sdjm 			if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) {
81332a20e26Sdjm 				fido_log_debug("%s: u2f_authenticate_single",
81432a20e26Sdjm 				    __func__);
815d75efeb7Sdjm 				return (r);
816d75efeb7Sdjm 			}
817d75efeb7Sdjm 			/* ignore credentials that don't exist */
818d75efeb7Sdjm 		}
81932a20e26Sdjm 	}
820d75efeb7Sdjm 
82132a20e26Sdjm 	fa->stmt_len = nfound;
822d75efeb7Sdjm 
82332a20e26Sdjm 	if (nfound == 0)
824d75efeb7Sdjm 		return (FIDO_ERR_NO_CREDENTIALS);
82532a20e26Sdjm 	if (nauth_ok == 0)
82632a20e26Sdjm 		return (FIDO_ERR_USER_PRESENCE_REQUIRED);
827d75efeb7Sdjm 
828d75efeb7Sdjm 	return (FIDO_OK);
829d75efeb7Sdjm }
830739189a3Sdjm 
831739189a3Sdjm int
u2f_get_touch_begin(fido_dev_t * dev,int * ms)832*ab19a69eSdjm u2f_get_touch_begin(fido_dev_t *dev, int *ms)
833739189a3Sdjm {
834739189a3Sdjm 	iso7816_apdu_t	*apdu = NULL;
835739189a3Sdjm 	const char	*clientdata = FIDO_DUMMY_CLIENTDATA;
836739189a3Sdjm 	const char	*rp_id = FIDO_DUMMY_RP_ID;
837739189a3Sdjm 	unsigned char	 clientdata_hash[SHA256_DIGEST_LENGTH];
838739189a3Sdjm 	unsigned char	 rp_id_hash[SHA256_DIGEST_LENGTH];
839739189a3Sdjm 	unsigned char	 reply[FIDO_MAXMSG];
840739189a3Sdjm 	int		 r;
841739189a3Sdjm 
842739189a3Sdjm 	memset(&clientdata_hash, 0, sizeof(clientdata_hash));
843739189a3Sdjm 	memset(&rp_id_hash, 0, sizeof(rp_id_hash));
844739189a3Sdjm 
845739189a3Sdjm 	if (SHA256((const void *)clientdata, strlen(clientdata),
846739189a3Sdjm 	    clientdata_hash) != clientdata_hash || SHA256((const void *)rp_id,
847739189a3Sdjm 	    strlen(rp_id), rp_id_hash) != rp_id_hash) {
848739189a3Sdjm 		fido_log_debug("%s: sha256", __func__);
849739189a3Sdjm 		return (FIDO_ERR_INTERNAL);
850739189a3Sdjm 	}
851739189a3Sdjm 
852c4a807edSdjm 	if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
853739189a3Sdjm 	    SHA256_DIGEST_LENGTH)) == NULL ||
854739189a3Sdjm 	    iso7816_add(apdu, clientdata_hash, sizeof(clientdata_hash)) < 0 ||
855739189a3Sdjm 	    iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
856739189a3Sdjm 		fido_log_debug("%s: iso7816", __func__);
857739189a3Sdjm 		r = FIDO_ERR_INTERNAL;
858739189a3Sdjm 		goto fail;
859739189a3Sdjm 	}
860739189a3Sdjm 
861739189a3Sdjm 	if (dev->attr.flags & FIDO_CAP_WINK) {
862*ab19a69eSdjm 		fido_tx(dev, CTAP_CMD_WINK, NULL, 0, ms);
863*ab19a69eSdjm 		fido_rx(dev, CTAP_CMD_WINK, &reply, sizeof(reply), ms);
864739189a3Sdjm 	}
865739189a3Sdjm 
866739189a3Sdjm 	if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
867*ab19a69eSdjm 	    iso7816_len(apdu), ms) < 0) {
868739189a3Sdjm 		fido_log_debug("%s: fido_tx", __func__);
869739189a3Sdjm 		r = FIDO_ERR_TX;
870739189a3Sdjm 		goto fail;
871739189a3Sdjm 	}
872739189a3Sdjm 
873739189a3Sdjm 	r = FIDO_OK;
874739189a3Sdjm fail:
875739189a3Sdjm 	iso7816_free(&apdu);
876739189a3Sdjm 
877739189a3Sdjm 	return (r);
878739189a3Sdjm }
879739189a3Sdjm 
880739189a3Sdjm int
u2f_get_touch_status(fido_dev_t * dev,int * touched,int * ms)881*ab19a69eSdjm u2f_get_touch_status(fido_dev_t *dev, int *touched, int *ms)
882739189a3Sdjm {
883739189a3Sdjm 	unsigned char	reply[FIDO_MAXMSG];
884739189a3Sdjm 	int		reply_len;
885739189a3Sdjm 	int		r;
886739189a3Sdjm 
887739189a3Sdjm 	if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply),
888739189a3Sdjm 	    ms)) < 2) {
889739189a3Sdjm 		fido_log_debug("%s: fido_rx", __func__);
890739189a3Sdjm 		return (FIDO_OK); /* ignore */
891739189a3Sdjm 	}
892739189a3Sdjm 
893739189a3Sdjm 	switch ((reply[reply_len - 2] << 8) | reply[reply_len - 1]) {
894739189a3Sdjm 	case SW_CONDITIONS_NOT_SATISFIED:
895*ab19a69eSdjm 		if ((r = u2f_get_touch_begin(dev, ms)) != FIDO_OK) {
896739189a3Sdjm 			fido_log_debug("%s: u2f_get_touch_begin", __func__);
897739189a3Sdjm 			return (r);
898739189a3Sdjm 		}
899739189a3Sdjm 		*touched = 0;
900739189a3Sdjm 		break;
901739189a3Sdjm 	case SW_NO_ERROR:
902739189a3Sdjm 		*touched = 1;
903739189a3Sdjm 		break;
904739189a3Sdjm 	default:
905739189a3Sdjm 		fido_log_debug("%s: unexpected sw", __func__);
906739189a3Sdjm 		return (FIDO_ERR_RX);
907739189a3Sdjm 	}
908739189a3Sdjm 
909739189a3Sdjm 	return (FIDO_OK);
910739189a3Sdjm }
911