Coverage Report

Created: 2022-07-22 12:05

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