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