]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libfido2/src/u2f.c
contrib/terminus: update to terminus-font-4.49.1
[FreeBSD/FreeBSD.git] / contrib / libfido2 / src / u2f.c
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 }