Coverage Report

Created: 2023-09-23 17:42

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