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