Coverage Report

Created: 2026-04-08 06:32

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