1/*
2 * Copyright (c) 2018-2022 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <openssl/sha.h>
9#include <openssl/x509.h>
10
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif
14#include <errno.h>
15
16#include "fido.h"
17#include "fido/es256.h"
18#include "fallthrough.h"
19
20#define U2F_PACE_MS (100)
21
22#if defined(_MSC_VER)
23static int
24usleep(unsigned int usec)
25{
26	Sleep(usec / 1000);
27
28	return (0);
29}
30#endif
31
32static int
33delay_ms(unsigned int ms, int *ms_remain)
34{
35	if (*ms_remain > -1 && (unsigned int)*ms_remain < ms)
36		ms = (unsigned int)*ms_remain;
37
38	if (ms > UINT_MAX / 1000) {
39		fido_log_debug("%s: ms=%u", __func__, ms);
40		return (-1);
41	}
42
43	if (usleep(ms * 1000) < 0) {
44		fido_log_error(errno, "%s: usleep", __func__);
45		return (-1);
46	}
47
48	if (*ms_remain > -1)
49		*ms_remain -= (int)ms;
50
51	return (0);
52}
53
54static int
55sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len)
56{
57	sig->len = *len; /* consume the whole buffer */
58	if ((sig->ptr = calloc(1, sig->len)) == NULL ||
59	    fido_buf_read(buf, len, sig->ptr, sig->len) < 0) {
60		fido_log_debug("%s: fido_buf_read", __func__);
61		fido_blob_reset(sig);
62		return (-1);
63	}
64
65	return (0);
66}
67
68static int
69x5c_get(fido_blob_t *x5c, const unsigned char **buf, size_t *len)
70{
71	X509	*cert = NULL;
72	int	 ok = -1;
73
74	if (*len > LONG_MAX) {
75		fido_log_debug("%s: invalid len %zu", __func__, *len);
76		goto fail;
77	}
78
79	/* find out the certificate's length */
80	const unsigned char *end = *buf;
81	if ((cert = d2i_X509(NULL, &end, (long)*len)) == NULL || end <= *buf ||
82	    (x5c->len = (size_t)(end - *buf)) >= *len) {
83		fido_log_debug("%s: d2i_X509", __func__);
84		goto fail;
85	}
86
87	/* read accordingly */
88	if ((x5c->ptr = calloc(1, x5c->len)) == NULL ||
89	    fido_buf_read(buf, len, x5c->ptr, x5c->len) < 0) {
90		fido_log_debug("%s: fido_buf_read", __func__);
91		goto fail;
92	}
93
94	ok = 0;
95fail:
96	if (cert != NULL)
97		X509_free(cert);
98
99	if (ok < 0)
100		fido_blob_reset(x5c);
101
102	return (ok);
103}
104
105static int
106authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount,
107    fido_blob_t *fake_cbor_ad)
108{
109	fido_authdata_t	 ad;
110	cbor_item_t	*item = NULL;
111	size_t		 alloc_len;
112
113	memset(&ad, 0, sizeof(ad));
114
115	if (SHA256((const void *)rp_id, strlen(rp_id),
116	    ad.rp_id_hash) != ad.rp_id_hash) {
117		fido_log_debug("%s: sha256", __func__);
118		return (-1);
119	}
120
121	ad.flags = flags; /* XXX translate? */
122	ad.sigcount = sigcount;
123
124	if ((item = cbor_build_bytestring((const unsigned char *)&ad,
125	    sizeof(ad))) == NULL) {
126		fido_log_debug("%s: cbor_build_bytestring", __func__);
127		return (-1);
128	}
129
130	if (fake_cbor_ad->ptr != NULL ||
131	    (fake_cbor_ad->len = cbor_serialize_alloc(item, &fake_cbor_ad->ptr,
132	    &alloc_len)) == 0) {
133		fido_log_debug("%s: cbor_serialize_alloc", __func__);
134		cbor_decref(&item);
135		return (-1);
136	}
137
138	cbor_decref(&item);
139
140	return (0);
141}
142
143/* TODO: use u2f_get_touch_begin & u2f_get_touch_status instead */
144static int
145send_dummy_register(fido_dev_t *dev, int *ms)
146{
147	iso7816_apdu_t	*apdu = NULL;
148	unsigned char	*reply = NULL;
149	unsigned char	 challenge[SHA256_DIGEST_LENGTH];
150	unsigned char	 application[SHA256_DIGEST_LENGTH];
151	int		 r;
152
153	/* dummy challenge & application */
154	memset(&challenge, 0xff, sizeof(challenge));
155	memset(&application, 0xff, sizeof(application));
156
157	if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
158	    SHA256_DIGEST_LENGTH)) == NULL ||
159	    iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
160	    iso7816_add(apdu, &application, sizeof(application)) < 0) {
161		fido_log_debug("%s: iso7816", __func__);
162		r = FIDO_ERR_INTERNAL;
163		goto fail;
164	}
165
166	if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
167		fido_log_debug("%s: malloc", __func__);
168		r = FIDO_ERR_INTERNAL;
169		goto fail;
170	}
171
172	do {
173		if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
174		    iso7816_len(apdu), ms) < 0) {
175			fido_log_debug("%s: fido_tx", __func__);
176			r = FIDO_ERR_TX;
177			goto fail;
178		}
179		if (fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms) < 2) {
180			fido_log_debug("%s: fido_rx", __func__);
181			r = FIDO_ERR_RX;
182			goto fail;
183		}
184		if (delay_ms(U2F_PACE_MS, ms) != 0) {
185			fido_log_debug("%s: delay_ms", __func__);
186			r = FIDO_ERR_RX;
187			goto fail;
188		}
189	} while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
190
191	r = FIDO_OK;
192fail:
193	iso7816_free(&apdu);
194	freezero(reply, FIDO_MAXMSG);
195
196	return (r);
197}
198
199static int
200key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
201    int *found, int *ms)
202{
203	iso7816_apdu_t	*apdu = NULL;
204	unsigned char	*reply = NULL;
205	unsigned char	 challenge[SHA256_DIGEST_LENGTH];
206	unsigned char	 rp_id_hash[SHA256_DIGEST_LENGTH];
207	uint8_t		 key_id_len;
208	int		 r;
209
210	if (key_id->len > UINT8_MAX || rp_id == NULL) {
211		fido_log_debug("%s: key_id->len=%zu, rp_id=%p", __func__,
212		    key_id->len, (const void *)rp_id);
213		r = FIDO_ERR_INVALID_ARGUMENT;
214		goto fail;
215	}
216
217	memset(&challenge, 0xff, sizeof(challenge));
218	memset(&rp_id_hash, 0, sizeof(rp_id_hash));
219
220	if (SHA256((const void *)rp_id, strlen(rp_id),
221	    rp_id_hash) != rp_id_hash) {
222		fido_log_debug("%s: sha256", __func__);
223		r = FIDO_ERR_INTERNAL;
224		goto fail;
225	}
226
227	key_id_len = (uint8_t)key_id->len;
228
229	if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 *
230	    SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
231	    iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
232	    iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
233	    iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
234	    iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
235		fido_log_debug("%s: iso7816", __func__);
236		r = FIDO_ERR_INTERNAL;
237		goto fail;
238	}
239
240	if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
241		fido_log_debug("%s: malloc", __func__);
242		r = FIDO_ERR_INTERNAL;
243		goto fail;
244	}
245
246	if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
247	    iso7816_len(apdu), ms) < 0) {
248		fido_log_debug("%s: fido_tx", __func__);
249		r = FIDO_ERR_TX;
250		goto fail;
251	}
252	if (fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms) != 2) {
253		fido_log_debug("%s: fido_rx", __func__);
254		r = FIDO_ERR_RX;
255		goto fail;
256	}
257
258	switch ((reply[0] << 8) | reply[1]) {
259	case SW_CONDITIONS_NOT_SATISFIED:
260		*found = 1; /* key exists */
261		break;
262	case SW_WRONG_DATA:
263		*found = 0; /* key does not exist */
264		break;
265	default:
266		/* unexpected sw */
267		r = FIDO_ERR_INTERNAL;
268		goto fail;
269	}
270
271	r = FIDO_OK;
272fail:
273	iso7816_free(&apdu);
274	freezero(reply, FIDO_MAXMSG);
275
276	return (r);
277}
278
279static int
280parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id,
281    const unsigned char *reply, size_t len)
282{
283	uint8_t		flags;
284	uint32_t	sigcount;
285
286	if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
287		fido_log_debug("%s: unexpected sw", __func__);
288		return (FIDO_ERR_RX);
289	}
290
291	len -= 2;
292
293	if (fido_buf_read(&reply, &len, &flags, sizeof(flags)) < 0 ||
294	    fido_buf_read(&reply, &len, &sigcount, sizeof(sigcount)) < 0) {
295		fido_log_debug("%s: fido_buf_read", __func__);
296		return (FIDO_ERR_RX);
297	}
298
299	if (sig_get(sig, &reply, &len) < 0) {
300		fido_log_debug("%s: sig_get", __func__);
301		return (FIDO_ERR_RX);
302	}
303
304	if (authdata_fake(rp_id, flags, sigcount, ad) < 0) {
305		fido_log_debug("%s; authdata_fake", __func__);
306		return (FIDO_ERR_RX);
307	}
308
309	return (FIDO_OK);
310}
311
312static int
313do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
314    const fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int *ms)
315{
316	iso7816_apdu_t	*apdu = NULL;
317	unsigned char	*reply = NULL;
318	unsigned char	 rp_id_hash[SHA256_DIGEST_LENGTH];
319	int		 reply_len;
320	uint8_t		 key_id_len;
321	int		 r;
322
323#ifdef FIDO_FUZZ
324	*ms = 0; /* XXX */
325#endif
326
327	if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX ||
328	    rp_id == NULL) {
329		r = FIDO_ERR_INVALID_ARGUMENT;
330		goto fail;
331	}
332
333	memset(&rp_id_hash, 0, sizeof(rp_id_hash));
334
335	if (SHA256((const void *)rp_id, strlen(rp_id),
336	    rp_id_hash) != rp_id_hash) {
337		fido_log_debug("%s: sha256", __func__);
338		r = FIDO_ERR_INTERNAL;
339		goto fail;
340	}
341
342	key_id_len = (uint8_t)key_id->len;
343
344	if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 *
345	    SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
346	    iso7816_add(apdu, cdh->ptr, cdh->len) < 0 ||
347	    iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
348	    iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
349	    iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
350		fido_log_debug("%s: iso7816", __func__);
351		r = FIDO_ERR_INTERNAL;
352		goto fail;
353	}
354
355	if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
356		fido_log_debug("%s: malloc", __func__);
357		r = FIDO_ERR_INTERNAL;
358		goto fail;
359	}
360
361	do {
362		if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
363		    iso7816_len(apdu), ms) < 0) {
364			fido_log_debug("%s: fido_tx", __func__);
365			r = FIDO_ERR_TX;
366			goto fail;
367		}
368		if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply,
369		    FIDO_MAXMSG, ms)) < 2) {
370			fido_log_debug("%s: fido_rx", __func__);
371			r = FIDO_ERR_RX;
372			goto fail;
373		}
374		if (delay_ms(U2F_PACE_MS, ms) != 0) {
375			fido_log_debug("%s: delay_ms", __func__);
376			r = FIDO_ERR_RX;
377			goto fail;
378		}
379	} while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
380
381	if ((r = parse_auth_reply(sig, ad, rp_id, reply,
382	    (size_t)reply_len)) != FIDO_OK) {
383		fido_log_debug("%s: parse_auth_reply", __func__);
384		goto fail;
385	}
386
387fail:
388	iso7816_free(&apdu);
389	freezero(reply, FIDO_MAXMSG);
390
391	return (r);
392}
393
394static int
395cbor_blob_from_ec_point(const uint8_t *ec_point, size_t ec_point_len,
396    fido_blob_t *cbor_blob)
397{
398	es256_pk_t	*pk = NULL;
399	cbor_item_t	*pk_cbor = NULL;
400	size_t		 alloc_len;
401	int		 ok = -1;
402
403	/* only handle uncompressed points */
404	if (ec_point_len != 65 || ec_point[0] != 0x04) {
405		fido_log_debug("%s: unexpected format", __func__);
406		goto fail;
407	}
408
409	if ((pk = es256_pk_new()) == NULL ||
410	    es256_pk_set_x(pk, &ec_point[1]) < 0 ||
411	    es256_pk_set_y(pk, &ec_point[33]) < 0) {
412		fido_log_debug("%s: es256_pk_set", __func__);
413		goto fail;
414	}
415
416	if ((pk_cbor = es256_pk_encode(pk, 0)) == NULL) {
417		fido_log_debug("%s: es256_pk_encode", __func__);
418		goto fail;
419	}
420
421	if ((cbor_blob->len = cbor_serialize_alloc(pk_cbor, &cbor_blob->ptr,
422	    &alloc_len)) != 77) {
423		fido_log_debug("%s: cbor_serialize_alloc", __func__);
424		goto fail;
425	}
426
427	ok = 0;
428fail:
429	es256_pk_free(&pk);
430
431	if (pk_cbor)
432		cbor_decref(&pk_cbor);
433
434	return (ok);
435}
436
437static int
438encode_cred_attstmt(int cose_alg, const fido_blob_t *x5c,
439    const fido_blob_t *sig, fido_blob_t *out)
440{
441	cbor_item_t		*item = NULL;
442	cbor_item_t		*x5c_cbor = NULL;
443	const uint8_t		 alg_cbor = (uint8_t)(-cose_alg - 1);
444	struct cbor_pair	 kv[3];
445	size_t			 alloc_len;
446	int			 ok = -1;
447
448	memset(&kv, 0, sizeof(kv));
449	memset(out, 0, sizeof(*out));
450
451	if ((item = cbor_new_definite_map(3)) == NULL) {
452		fido_log_debug("%s: cbor_new_definite_map", __func__);
453		goto fail;
454	}
455
456	if ((kv[0].key = cbor_build_string("alg")) == NULL ||
457	    (kv[0].value = cbor_build_negint8(alg_cbor)) == NULL ||
458	    !cbor_map_add(item, kv[0])) {
459		fido_log_debug("%s: alg", __func__);
460		goto fail;
461	}
462
463	if ((kv[1].key = cbor_build_string("sig")) == NULL ||
464	    (kv[1].value = fido_blob_encode(sig)) == NULL ||
465	    !cbor_map_add(item, kv[1])) {
466		fido_log_debug("%s: sig", __func__);
467		goto fail;
468	}
469
470	if ((kv[2].key = cbor_build_string("x5c")) == NULL ||
471	    (kv[2].value = cbor_new_definite_array(1)) == NULL ||
472	    (x5c_cbor = fido_blob_encode(x5c)) == NULL ||
473	    !cbor_array_push(kv[2].value, x5c_cbor) ||
474	    !cbor_map_add(item, kv[2])) {
475		fido_log_debug("%s: x5c", __func__);
476		goto fail;
477	}
478
479	if ((out->len = cbor_serialize_alloc(item, &out->ptr,
480	    &alloc_len)) == 0) {
481		fido_log_debug("%s: cbor_serialize_alloc", __func__);
482		goto fail;
483	}
484
485	ok = 0;
486fail:
487	if (item != NULL)
488		cbor_decref(&item);
489	if (x5c_cbor != NULL)
490		cbor_decref(&x5c_cbor);
491
492	for (size_t i = 0; i < nitems(kv); i++) {
493		if (kv[i].key)
494			cbor_decref(&kv[i].key);
495		if (kv[i].value)
496			cbor_decref(&kv[i].value);
497	}
498
499	return (ok);
500}
501
502static int
503encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len,
504    const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out)
505{
506	fido_authdata_t	 	 authdata;
507	fido_attcred_raw_t	 attcred_raw;
508	fido_blob_t		 pk_blob;
509	fido_blob_t		 authdata_blob;
510	cbor_item_t		*authdata_cbor = NULL;
511	unsigned char		*ptr;
512	size_t			 len;
513	size_t			 alloc_len;
514	int			 ok = -1;
515
516	memset(&pk_blob, 0, sizeof(pk_blob));
517	memset(&authdata, 0, sizeof(authdata));
518	memset(&authdata_blob, 0, sizeof(authdata_blob));
519	memset(out, 0, sizeof(*out));
520
521	if (rp_id == NULL) {
522		fido_log_debug("%s: NULL rp_id", __func__);
523		goto fail;
524	}
525
526	if (cbor_blob_from_ec_point(pubkey, pubkey_len, &pk_blob) < 0) {
527		fido_log_debug("%s: cbor_blob_from_ec_point", __func__);
528		goto fail;
529	}
530
531	if (SHA256((const void *)rp_id, strlen(rp_id),
532	    authdata.rp_id_hash) != authdata.rp_id_hash) {
533		fido_log_debug("%s: sha256", __func__);
534		goto fail;
535	}
536
537	authdata.flags = (CTAP_AUTHDATA_ATT_CRED | CTAP_AUTHDATA_USER_PRESENT);
538	authdata.sigcount = 0;
539
540	memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid));
541	attcred_raw.id_len = htobe16(kh_len);
542
543	len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) +
544	    kh_len + pk_blob.len;
545	ptr = authdata_blob.ptr = calloc(1, authdata_blob.len);
546
547	fido_log_debug("%s: ptr=%p, len=%zu", __func__, (void *)ptr, len);
548
549	if (authdata_blob.ptr == NULL)
550		goto fail;
551
552	if (fido_buf_write(&ptr, &len, &authdata, sizeof(authdata)) < 0 ||
553	    fido_buf_write(&ptr, &len, &attcred_raw, sizeof(attcred_raw)) < 0 ||
554	    fido_buf_write(&ptr, &len, kh, kh_len) < 0 ||
555	    fido_buf_write(&ptr, &len, pk_blob.ptr, pk_blob.len) < 0) {
556		fido_log_debug("%s: fido_buf_write", __func__);
557		goto fail;
558	}
559
560	if ((authdata_cbor = fido_blob_encode(&authdata_blob)) == NULL) {
561		fido_log_debug("%s: fido_blob_encode", __func__);
562		goto fail;
563	}
564
565	if ((out->len = cbor_serialize_alloc(authdata_cbor, &out->ptr,
566	    &alloc_len)) == 0) {
567		fido_log_debug("%s: cbor_serialize_alloc", __func__);
568		goto fail;
569	}
570
571	ok = 0;
572fail:
573	if (authdata_cbor)
574		cbor_decref(&authdata_cbor);
575
576	fido_blob_reset(&pk_blob);
577	fido_blob_reset(&authdata_blob);
578
579	return (ok);
580}
581
582static int
583parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
584{
585	fido_blob_t	 x5c;
586	fido_blob_t	 sig;
587	fido_blob_t	 ad;
588	fido_blob_t	 stmt;
589	uint8_t		 dummy;
590	uint8_t		 pubkey[65];
591	uint8_t		 kh_len = 0;
592	uint8_t		*kh = NULL;
593	int		 r;
594
595	memset(&x5c, 0, sizeof(x5c));
596	memset(&sig, 0, sizeof(sig));
597	memset(&ad, 0, sizeof(ad));
598	memset(&stmt, 0, sizeof(stmt));
599	r = FIDO_ERR_RX;
600
601	/* status word */
602	if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
603		fido_log_debug("%s: unexpected sw", __func__);
604		goto fail;
605	}
606
607	len -= 2;
608
609	/* reserved byte */
610	if (fido_buf_read(&reply, &len, &dummy, sizeof(dummy)) < 0 ||
611	    dummy != 0x05) {
612		fido_log_debug("%s: reserved byte", __func__);
613		goto fail;
614	}
615
616	/* pubkey + key handle */
617	if (fido_buf_read(&reply, &len, &pubkey, sizeof(pubkey)) < 0 ||
618	    fido_buf_read(&reply, &len, &kh_len, sizeof(kh_len)) < 0 ||
619	    (kh = calloc(1, kh_len)) == NULL ||
620	    fido_buf_read(&reply, &len, kh, kh_len) < 0) {
621		fido_log_debug("%s: fido_buf_read", __func__);
622		goto fail;
623	}
624
625	/* x5c + sig */
626	if (x5c_get(&x5c, &reply, &len) < 0 ||
627	    sig_get(&sig, &reply, &len) < 0) {
628		fido_log_debug("%s: x5c || sig", __func__);
629		goto fail;
630	}
631
632	/* attstmt */
633	if (encode_cred_attstmt(COSE_ES256, &x5c, &sig, &stmt) < 0) {
634		fido_log_debug("%s: encode_cred_attstmt", __func__);
635		goto fail;
636	}
637
638	/* authdata */
639	if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey,
640	    sizeof(pubkey), &ad) < 0) {
641		fido_log_debug("%s: encode_cred_authdata", __func__);
642		goto fail;
643	}
644
645	if (fido_cred_set_fmt(cred, "fido-u2f") != FIDO_OK ||
646	    fido_cred_set_authdata(cred, ad.ptr, ad.len) != FIDO_OK ||
647	    fido_cred_set_attstmt(cred, stmt.ptr, stmt.len) != FIDO_OK) {
648		fido_log_debug("%s: fido_cred_set", __func__);
649		r = FIDO_ERR_INTERNAL;
650		goto fail;
651	}
652
653	r = FIDO_OK;
654fail:
655	freezero(kh, kh_len);
656	fido_blob_reset(&x5c);
657	fido_blob_reset(&sig);
658	fido_blob_reset(&ad);
659	fido_blob_reset(&stmt);
660
661	return (r);
662}
663
664int
665u2f_register(fido_dev_t *dev, fido_cred_t *cred, int *ms)
666{
667	iso7816_apdu_t	*apdu = NULL;
668	unsigned char	 rp_id_hash[SHA256_DIGEST_LENGTH];
669	unsigned char	*reply = NULL;
670	int		 reply_len;
671	int		 found;
672	int		 r;
673
674	if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) {
675		fido_log_debug("%s: rk=%d, uv=%d", __func__, cred->rk,
676		    cred->uv);
677		return (FIDO_ERR_UNSUPPORTED_OPTION);
678	}
679
680	if (cred->type != COSE_ES256 || cred->cdh.ptr == NULL ||
681	    cred->rp.id == NULL || cred->cdh.len != SHA256_DIGEST_LENGTH) {
682		fido_log_debug("%s: type=%d, cdh=(%p,%zu)" , __func__,
683		    cred->type, (void *)cred->cdh.ptr, cred->cdh.len);
684		return (FIDO_ERR_INVALID_ARGUMENT);
685	}
686
687	for (size_t i = 0; i < cred->excl.len; i++) {
688		if ((r = key_lookup(dev, cred->rp.id, &cred->excl.ptr[i],
689		    &found, ms)) != FIDO_OK) {
690			fido_log_debug("%s: key_lookup", __func__);
691			return (r);
692		}
693		if (found) {
694			if ((r = send_dummy_register(dev, ms)) != FIDO_OK) {
695				fido_log_debug("%s: send_dummy_register",
696				    __func__);
697				return (r);
698			}
699			return (FIDO_ERR_CREDENTIAL_EXCLUDED);
700		}
701	}
702
703	memset(&rp_id_hash, 0, sizeof(rp_id_hash));
704
705	if (SHA256((const void *)cred->rp.id, strlen(cred->rp.id),
706	    rp_id_hash) != rp_id_hash) {
707		fido_log_debug("%s: sha256", __func__);
708		return (FIDO_ERR_INTERNAL);
709	}
710
711	if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
712	    SHA256_DIGEST_LENGTH)) == NULL ||
713	    iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 ||
714	    iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
715		fido_log_debug("%s: iso7816", __func__);
716		r = FIDO_ERR_INTERNAL;
717		goto fail;
718	}
719
720	if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
721		fido_log_debug("%s: malloc", __func__);
722		r = FIDO_ERR_INTERNAL;
723		goto fail;
724	}
725
726	do {
727		if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
728		    iso7816_len(apdu), ms) < 0) {
729			fido_log_debug("%s: fido_tx", __func__);
730			r = FIDO_ERR_TX;
731			goto fail;
732		}
733		if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply,
734		    FIDO_MAXMSG, ms)) < 2) {
735			fido_log_debug("%s: fido_rx", __func__);
736			r = FIDO_ERR_RX;
737			goto fail;
738		}
739		if (delay_ms(U2F_PACE_MS, ms) != 0) {
740			fido_log_debug("%s: delay_ms", __func__);
741			r = FIDO_ERR_RX;
742			goto fail;
743		}
744	} while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
745
746	if ((r = parse_register_reply(cred, reply,
747	    (size_t)reply_len)) != FIDO_OK) {
748		fido_log_debug("%s: parse_register_reply", __func__);
749		goto fail;
750	}
751fail:
752	iso7816_free(&apdu);
753	freezero(reply, FIDO_MAXMSG);
754
755	return (r);
756}
757
758static int
759u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id,
760    fido_assert_t *fa, size_t idx, int *ms)
761{
762	fido_blob_t	sig;
763	fido_blob_t	ad;
764	int		found;
765	int		r;
766
767	memset(&sig, 0, sizeof(sig));
768	memset(&ad, 0, sizeof(ad));
769
770	if ((r = key_lookup(dev, fa->rp_id, key_id, &found, ms)) != FIDO_OK) {
771		fido_log_debug("%s: key_lookup", __func__);
772		goto fail;
773	}
774
775	if (!found) {
776		fido_log_debug("%s: not found", __func__);
777		r = FIDO_ERR_CREDENTIAL_EXCLUDED;
778		goto fail;
779	}
780
781	if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0) {
782		fido_log_debug("%s: fido_blob_set", __func__);
783		r = FIDO_ERR_INTERNAL;
784		goto fail;
785	}
786
787	if (fa->up == FIDO_OPT_FALSE) {
788		fido_log_debug("%s: checking for key existence only", __func__);
789		r = FIDO_ERR_USER_PRESENCE_REQUIRED;
790		goto fail;
791	}
792
793	if ((r = do_auth(dev, &fa->cdh, fa->rp_id, key_id, &sig, &ad,
794	    ms)) != FIDO_OK) {
795		fido_log_debug("%s: do_auth", __func__);
796		goto fail;
797	}
798
799	if (fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK ||
800	    fido_assert_set_sig(fa, idx, sig.ptr, sig.len) != FIDO_OK) {
801		fido_log_debug("%s: fido_assert_set", __func__);
802		r = FIDO_ERR_INTERNAL;
803		goto fail;
804	}
805
806	r = FIDO_OK;
807fail:
808	fido_blob_reset(&sig);
809	fido_blob_reset(&ad);
810
811	return (r);
812}
813
814int
815u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int *ms)
816{
817	size_t	nfound = 0;
818	size_t	nauth_ok = 0;
819	int	r;
820
821	if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) {
822		fido_log_debug("%s: uv=%d, allow_list=%p", __func__, fa->uv,
823		    (void *)fa->allow_list.ptr);
824		return (FIDO_ERR_UNSUPPORTED_OPTION);
825	}
826
827	if ((r = fido_assert_set_count(fa, fa->allow_list.len)) != FIDO_OK) {
828		fido_log_debug("%s: fido_assert_set_count", __func__);
829		return (r);
830	}
831
832	for (size_t i = 0; i < fa->allow_list.len; i++) {
833		switch ((r = u2f_authenticate_single(dev,
834		    &fa->allow_list.ptr[i], fa, nfound, ms))) {
835		case FIDO_OK:
836			nauth_ok++;
837			FALLTHROUGH
838		case FIDO_ERR_USER_PRESENCE_REQUIRED:
839			nfound++;
840			break;
841		default:
842			if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) {
843				fido_log_debug("%s: u2f_authenticate_single",
844				    __func__);
845				return (r);
846			}
847			/* ignore credentials that don't exist */
848		}
849	}
850
851	fa->stmt_len = nfound;
852
853	if (nfound == 0)
854		return (FIDO_ERR_NO_CREDENTIALS);
855	if (nauth_ok == 0)
856		return (FIDO_ERR_USER_PRESENCE_REQUIRED);
857
858	return (FIDO_OK);
859}
860
861int
862u2f_get_touch_begin(fido_dev_t *dev, int *ms)
863{
864	iso7816_apdu_t	*apdu = NULL;
865	const char	*clientdata = FIDO_DUMMY_CLIENTDATA;
866	const char	*rp_id = FIDO_DUMMY_RP_ID;
867	unsigned char	*reply = NULL;
868	unsigned char	 clientdata_hash[SHA256_DIGEST_LENGTH];
869	unsigned char	 rp_id_hash[SHA256_DIGEST_LENGTH];
870	int		 r;
871
872	memset(&clientdata_hash, 0, sizeof(clientdata_hash));
873	memset(&rp_id_hash, 0, sizeof(rp_id_hash));
874
875	if (SHA256((const void *)clientdata, strlen(clientdata),
876	    clientdata_hash) != clientdata_hash || SHA256((const void *)rp_id,
877	    strlen(rp_id), rp_id_hash) != rp_id_hash) {
878		fido_log_debug("%s: sha256", __func__);
879		return (FIDO_ERR_INTERNAL);
880	}
881
882	if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
883	    SHA256_DIGEST_LENGTH)) == NULL ||
884	    iso7816_add(apdu, clientdata_hash, sizeof(clientdata_hash)) < 0 ||
885	    iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
886		fido_log_debug("%s: iso7816", __func__);
887		r = FIDO_ERR_INTERNAL;
888		goto fail;
889	}
890
891	if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
892		fido_log_debug("%s: malloc", __func__);
893		r =  FIDO_ERR_INTERNAL;
894		goto fail;
895	}
896
897	if (dev->attr.flags & FIDO_CAP_WINK) {
898		fido_tx(dev, CTAP_CMD_WINK, NULL, 0, ms);
899		fido_rx(dev, CTAP_CMD_WINK, reply, FIDO_MAXMSG, ms);
900	}
901
902	if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
903	    iso7816_len(apdu), ms) < 0) {
904		fido_log_debug("%s: fido_tx", __func__);
905		r = FIDO_ERR_TX;
906		goto fail;
907	}
908
909	r = FIDO_OK;
910fail:
911	iso7816_free(&apdu);
912	freezero(reply, FIDO_MAXMSG);
913
914	return (r);
915}
916
917int
918u2f_get_touch_status(fido_dev_t *dev, int *touched, int *ms)
919{
920	unsigned char	*reply;
921	int		 reply_len;
922	int		 r;
923
924	if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
925		fido_log_debug("%s: malloc", __func__);
926		r =  FIDO_ERR_INTERNAL;
927		goto out;
928	}
929
930	if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG,
931	    ms)) < 2) {
932		fido_log_debug("%s: fido_rx", __func__);
933		r = FIDO_OK; /* ignore */
934		goto out;
935	}
936
937	switch ((reply[reply_len - 2] << 8) | reply[reply_len - 1]) {
938	case SW_CONDITIONS_NOT_SATISFIED:
939		if ((r = u2f_get_touch_begin(dev, ms)) != FIDO_OK) {
940			fido_log_debug("%s: u2f_get_touch_begin", __func__);
941			goto out;
942		}
943		*touched = 0;
944		break;
945	case SW_NO_ERROR:
946		*touched = 1;
947		break;
948	default:
949		fido_log_debug("%s: unexpected sw", __func__);
950		r = FIDO_ERR_RX;
951		goto out;
952	}
953
954	r = FIDO_OK;
955out:
956	freezero(reply, FIDO_MAXMSG);
957
958	return (r);
959}
960