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