Coverage Report

Created: 2021-03-26 11:35

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