Coverage Report

Created: 2022-01-17 10:46

/libfido2/src/assert.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2018-2021 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
9
#include "fido.h"
10
#include "fido/es256.h"
11
#include "fido/rs256.h"
12
#include "fido/eddsa.h"
13
14
static int
15
adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
16
1.26k
{
17
1.26k
        fido_assert_t   *assert = arg;
18
1.26k
        uint64_t         n;
19
20
        /* numberOfCredentials; see section 6.2 */
21
1.26k
        if (cbor_isa_uint(key) == false ||
22
1.26k
            cbor_int_get_width(key) != CBOR_INT_8 ||
23
1.26k
            cbor_get_uint8(key) != 5) {
24
1.14k
                fido_log_debug("%s: cbor_type", __func__);
25
1.14k
                return (0); /* ignore */
26
1.14k
        }
27
28
124
        if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
29
1
                fido_log_debug("%s: cbor_decode_uint64", __func__);
30
1
                return (-1);
31
1
        }
32
33
123
        if (assert->stmt_len != 0 || assert->stmt_cnt != 1 ||
34
123
            (size_t)n < assert->stmt_cnt) {
35
1
                fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu",
36
1
                    __func__, assert->stmt_len, assert->stmt_cnt, (size_t)n);
37
1
                return (-1);
38
1
        }
39
40
122
        if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) {
41
44
                fido_log_debug("%s: fido_assert_set_count", __func__);
42
44
                return (-1);
43
44
        }
44
45
78
        assert->stmt_len = 0; /* XXX */
46
47
78
        return (0);
48
78
}
49
50
static int
51
parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
52
1.80k
{
53
1.80k
        fido_assert_stmt *stmt = arg;
54
55
1.80k
        if (cbor_isa_uint(key) == false ||
56
1.80k
            cbor_int_get_width(key) != CBOR_INT_8) {
57
59
                fido_log_debug("%s: cbor type", __func__);
58
59
                return (0); /* ignore */
59
59
        }
60
61
1.74k
        switch (cbor_get_uint8(key)) {
62
584
        case 1: /* credential id */
63
584
                return (cbor_decode_cred_id(val, &stmt->id));
64
424
        case 2: /* authdata */
65
424
                return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor,
66
424
                    &stmt->authdata, &stmt->authdata_ext));
67
381
        case 3: /* signature */
68
381
                return (fido_blob_decode(val, &stmt->sig));
69
268
        case 4: /* user attributes */
70
268
                return (cbor_decode_user(val, &stmt->user));
71
1
        case 7: /* large blob key */
72
1
                return (fido_blob_decode(val, &stmt->largeblob_key));
73
83
        default: /* ignore */
74
83
                fido_log_debug("%s: cbor type", __func__);
75
83
                return (0);
76
1.74k
        }
77
1.74k
}
78
79
static int
80
fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
81
    const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms)
82
891
{
83
891
        fido_blob_t      f;
84
891
        fido_opt_t       uv = assert->uv;
85
891
        cbor_item_t     *argv[7];
86
891
        const uint8_t    cmd = CTAP_CBOR_ASSERT;
87
891
        int              r;
88
89
891
        memset(argv, 0, sizeof(argv));
90
891
        memset(&f, 0, sizeof(f));
91
92
        /* do we have everything we need? */
93
891
        if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
94
0
                fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
95
0
                    (void *)assert->rp_id, (void *)assert->cdh.ptr);
96
0
                r = FIDO_ERR_INVALID_ARGUMENT;
97
0
                goto fail;
98
0
        }
99
100
891
        if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL ||
101
891
            (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) {
102
3
                fido_log_debug("%s: cbor encode", __func__);
103
3
                r = FIDO_ERR_INTERNAL;
104
3
                goto fail;
105
3
        }
106
107
        /* allowed credentials */
108
888
        if (assert->allow_list.len) {
109
547
                const fido_blob_array_t *cl = &assert->allow_list;
110
547
                if ((argv[2] = cbor_encode_pubkey_list(cl)) == NULL) {
111
47
                        fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
112
47
                        r = FIDO_ERR_INTERNAL;
113
47
                        goto fail;
114
47
                }
115
841
        }
116
117
841
        if (assert->ext.mask)
118
425
                if ((argv[3] = cbor_encode_assert_ext(dev, &assert->ext, ecdh,
119
425
                    pk)) == NULL) {
120
56
                        fido_log_debug("%s: cbor_encode_assert_ext", __func__);
121
56
                        r = FIDO_ERR_INTERNAL;
122
56
                        goto fail;
123
56
                }
124
125
        /* user verification */
126
785
        if (pin != NULL || (uv == FIDO_OPT_TRUE &&
127
439
            fido_dev_supports_permissions(dev))) {
128
439
                if ((r = cbor_add_uv_params(dev, cmd, &assert->cdh, pk, ecdh,
129
439
                    pin, assert->rp_id, &argv[5], &argv[6], ms)) != FIDO_OK) {
130
131
                        fido_log_debug("%s: cbor_add_uv_params", __func__);
131
131
                        goto fail;
132
131
                }
133
308
                uv = FIDO_OPT_OMIT;
134
308
        }
135
136
        /* options */
137
785
        if (assert->up != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT)
138
349
                if ((argv[4] = cbor_encode_assert_opt(assert->up, uv)) == NULL) {
139
4
                        fido_log_debug("%s: cbor_encode_assert_opt", __func__);
140
4
                        r = FIDO_ERR_INTERNAL;
141
4
                        goto fail;
142
4
                }
143
144
        /* frame and transmit */
145
650
        if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
146
650
            fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
147
114
                fido_log_debug("%s: fido_tx", __func__);
148
114
                r = FIDO_ERR_TX;
149
114
                goto fail;
150
114
        }
151
152
536
        r = FIDO_OK;
153
891
fail:
154
891
        cbor_vector_free(argv, nitems(argv));
155
891
        free(f.ptr);
156
157
891
        return (r);
158
536
}
159
160
static int
161
fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms)
162
536
{
163
536
        unsigned char   reply[FIDO_MAXMSG];
164
536
        int             reply_len;
165
536
        int             r;
166
167
536
        fido_assert_reset_rx(assert);
168
169
536
        if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
170
536
            ms)) < 0) {
171
33
                fido_log_debug("%s: fido_rx", __func__);
172
33
                return (FIDO_ERR_RX);
173
33
        }
174
175
        /* start with room for a single assertion */
176
503
        if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL)
177
503
                return (FIDO_ERR_INTERNAL);
178
179
500
        assert->stmt_len = 0;
180
500
        assert->stmt_cnt = 1;
181
182
        /* adjust as needed */
183
500
        if ((r = cbor_parse_reply(reply, (size_t)reply_len, assert,
184
500
            adjust_assert_count)) != FIDO_OK) {
185
104
                fido_log_debug("%s: adjust_assert_count", __func__);
186
104
                return (r);
187
104
        }
188
189
        /* parse the first assertion */
190
396
        if ((r = cbor_parse_reply(reply, (size_t)reply_len,
191
396
            &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
192
47
                fido_log_debug("%s: parse_assert_reply", __func__);
193
47
                return (r);
194
47
        }
195
196
349
        assert->stmt_len++;
197
198
349
        return (FIDO_OK);
199
349
}
200
201
static int
202
fido_get_next_assert_tx(fido_dev_t *dev, int *ms)
203
228
{
204
228
        const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT };
205
206
228
        if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) {
207
5
                fido_log_debug("%s: fido_tx", __func__);
208
5
                return (FIDO_ERR_TX);
209
5
        }
210
211
223
        return (FIDO_OK);
212
223
}
213
214
static int
215
fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms)
216
223
{
217
223
        unsigned char   reply[FIDO_MAXMSG];
218
223
        int             reply_len;
219
223
        int             r;
220
221
223
        if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
222
223
            ms)) < 0) {
223
7
                fido_log_debug("%s: fido_rx", __func__);
224
7
                return (FIDO_ERR_RX);
225
7
        }
226
227
        /* sanity check */
228
216
        if (assert->stmt_len >= assert->stmt_cnt) {
229
0
                fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__,
230
0
                    assert->stmt_len, assert->stmt_cnt);
231
0
                return (FIDO_ERR_INTERNAL);
232
0
        }
233
234
216
        if ((r = cbor_parse_reply(reply, (size_t)reply_len,
235
216
            &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
236
17
                fido_log_debug("%s: parse_assert_reply", __func__);
237
17
                return (r);
238
17
        }
239
240
199
        return (FIDO_OK);
241
199
}
242
243
static int
244
fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert,
245
    const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms)
246
891
{
247
891
        int r;
248
249
891
        if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin,
250
891
            ms)) != FIDO_OK ||
251
891
            (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK)
252
891
                return (r);
253
254
548
        while (assert->stmt_len < assert->stmt_cnt) {
255
228
                if ((r = fido_get_next_assert_tx(dev, ms)) != FIDO_OK ||
256
228
                    (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK)
257
228
                        return (r);
258
199
                assert->stmt_len++;
259
199
        }
260
261
349
        return (FIDO_OK);
262
349
}
263
264
static int
265
decrypt_hmac_secrets(const fido_dev_t *dev, fido_assert_t *assert,
266
    const fido_blob_t *key)
267
12
{
268
28
        for (size_t i = 0; i < assert->stmt_cnt; i++) {
269
17
                fido_assert_stmt *stmt = &assert->stmt[i];
270
17
                if (stmt->authdata_ext.hmac_secret_enc.ptr != NULL) {
271
7
                        if (aes256_cbc_dec(dev, key,
272
7
                            &stmt->authdata_ext.hmac_secret_enc,
273
7
                            &stmt->hmac_secret) < 0) {
274
1
                                fido_log_debug("%s: aes256_cbc_dec %zu",
275
1
                                    __func__, i);
276
1
                                return (-1);
277
1
                        }
278
7
                }
279
17
        }
280
281
12
        return (0);
282
12
}
283
284
int
285
fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
286
2.02k
{
287
2.02k
        fido_blob_t     *ecdh = NULL;
288
2.02k
        es256_pk_t      *pk = NULL;
289
2.02k
        int              ms = dev->timeout_ms;
290
2.02k
        int              r;
291
292
#ifdef USE_WINHELLO
293
        if (dev->flags & FIDO_DEV_WINHELLO)
294
                return (fido_winhello_get_assert(dev, assert, pin, ms));
295
#endif
296
297
2.02k
        if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
298
4
                fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
299
4
                    (void *)assert->rp_id, (void *)assert->cdh.ptr);
300
4
                return (FIDO_ERR_INVALID_ARGUMENT);
301
4
        }
302
303
2.02k
        if (fido_dev_is_fido2(dev) == false) {
304
923
                if (pin != NULL || assert->ext.mask != 0)
305
305
                        return (FIDO_ERR_UNSUPPORTED_OPTION);
306
618
                return (u2f_authenticate(dev, assert, &ms));
307
618
        }
308
309
1.10k
        if (pin != NULL || (assert->uv == FIDO_OPT_TRUE &&
310
514
            fido_dev_supports_permissions(dev)) ||
311
1.10k
            (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) {
312
733
                if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) {
313
210
                        fido_log_debug("%s: fido_do_ecdh", __func__);
314
210
                        goto fail;
315
210
                }
316
891
        }
317
318
891
        r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, &ms);
319
891
        if (r == FIDO_OK && (assert->ext.mask & FIDO_EXT_HMAC_SECRET))
320
12
                if (decrypt_hmac_secrets(dev, assert, ecdh) < 0) {
321
1
                        fido_log_debug("%s: decrypt_hmac_secrets", __func__);
322
1
                        r = FIDO_ERR_INTERNAL;
323
1
                        goto fail;
324
1
                }
325
326
1.10k
fail:
327
1.10k
        es256_pk_free(&pk);
328
1.10k
        fido_blob_free(&ecdh);
329
330
1.10k
        return (r);
331
891
}
332
333
int
334
fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv)
335
650
{
336
650
        fido_log_debug("%s: flags=%02x", __func__, flags);
337
650
        fido_log_debug("%s: up=%d, uv=%d", __func__, up, uv);
338
339
650
        if (up == FIDO_OPT_TRUE &&
340
650
            (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) {
341
34
                fido_log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__);
342
34
                return (-1); /* user not present */
343
34
        }
344
345
616
        if (uv == FIDO_OPT_TRUE &&
346
616
            (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) {
347
40
                fido_log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__);
348
40
                return (-1); /* user not verified */
349
40
        }
350
351
576
        return (0);
352
576
}
353
354
static int
355
check_extensions(int authdata_ext, int ext)
356
402
{
357
        /* XXX: largeBlobKey is not part of extensions map */
358
402
        ext &= ~FIDO_EXT_LARGEBLOB_KEY;
359
402
        if (authdata_ext != ext) {
360
16
                fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
361
16
                    authdata_ext, ext);
362
16
                return (-1);
363
16
        }
364
365
386
        return (0);
366
386
}
367
368
int
369
fido_get_signed_hash(int cose_alg, fido_blob_t *dgst,
370
    const fido_blob_t *clientdata, const fido_blob_t *authdata_cbor)
371
380
{
372
380
        cbor_item_t             *item = NULL;
373
380
        unsigned char           *authdata_ptr = NULL;
374
380
        size_t                   authdata_len;
375
380
        struct cbor_load_result  cbor;
376
380
        const EVP_MD            *md = NULL;
377
380
        EVP_MD_CTX              *ctx = NULL;
378
380
        int                      ok = -1;
379
380
380
        if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
381
380
            &cbor)) == NULL || cbor_isa_bytestring(item) == false ||
382
380
            cbor_bytestring_is_definite(item) == false) {
383
5
                fido_log_debug("%s: authdata", __func__);
384
5
                goto fail;
385
5
        }
386
387
375
        authdata_ptr = cbor_bytestring_handle(item);
388
375
        authdata_len = cbor_bytestring_length(item);
389
390
375
        if (cose_alg != COSE_EDDSA) {
391
263
                if (dgst->len < SHA256_DIGEST_LENGTH ||
392
263
                    (md = EVP_sha256()) == NULL ||
393
263
                    (ctx = EVP_MD_CTX_new()) == NULL ||
394
263
                    EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
395
263
                    EVP_DigestUpdate(ctx, authdata_ptr, authdata_len) != 1 ||
396
263
                    EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 ||
397
263
                    EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) {
398
34
                        fido_log_debug("%s: sha256", __func__);
399
34
                        goto fail;
400
34
                }
401
229
                dgst->len = SHA256_DIGEST_LENGTH;
402
229
        } else {
403
112
                if (SIZE_MAX - authdata_len < clientdata->len ||
404
112
                    dgst->len < authdata_len + clientdata->len) {
405
15
                        fido_log_debug("%s: memcpy", __func__);
406
15
                        goto fail;
407
15
                }
408
97
                memcpy(dgst->ptr, authdata_ptr, authdata_len);
409
97
                memcpy(dgst->ptr + authdata_len, clientdata->ptr,
410
97
                    clientdata->len);
411
97
                dgst->len = authdata_len + clientdata->len;
412
97
        }
413
414
375
        ok = 0;
415
380
fail:
416
380
        if (item != NULL)
417
380
                cbor_decref(&item);
418
419
380
        EVP_MD_CTX_free(ctx);
420
421
380
        return (ok);
422
326
}
423
424
int
425
fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg,
426
    const void *pk)
427
32.4k
{
428
32.4k
        unsigned char            buf[1024]; /* XXX */
429
32.4k
        fido_blob_t              dgst;
430
32.4k
        const fido_assert_stmt  *stmt = NULL;
431
32.4k
        int                      ok = -1;
432
32.4k
        int                      r;
433
434
32.4k
        dgst.ptr = buf;
435
32.4k
        dgst.len = sizeof(buf);
436
437
32.4k
        if (idx >= assert->stmt_len || pk == NULL) {
438
117
                r = FIDO_ERR_INVALID_ARGUMENT;
439
117
                goto out;
440
117
        }
441
442
32.3k
        stmt = &assert->stmt[idx];
443
444
        /* do we have everything we need? */
445
32.3k
        if (assert->cdh.ptr == NULL || assert->rp_id == NULL ||
446
32.3k
            stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) {
447
31.8k
                fido_log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p",
448
31.8k
                    __func__, (void *)assert->cdh.ptr, assert->rp_id,
449
31.8k
                    (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr);
450
31.8k
                r = FIDO_ERR_INVALID_ARGUMENT;
451
31.8k
                goto out;
452
31.8k
        }
453
454
473
        if (fido_check_flags(stmt->authdata.flags, assert->up,
455
473
            assert->uv) < 0) {
456
71
                fido_log_debug("%s: fido_check_flags", __func__);
457
71
                r = FIDO_ERR_INVALID_PARAM;
458
71
                goto out;
459
71
        }
460
461
402
        if (check_extensions(stmt->authdata_ext.mask, assert->ext.mask) < 0) {
462
16
                fido_log_debug("%s: check_extensions", __func__);
463
16
                r = FIDO_ERR_INVALID_PARAM;
464
16
                goto out;
465
16
        }
466
467
386
        if (fido_check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) {
468
82
                fido_log_debug("%s: fido_check_rp_id", __func__);
469
82
                r = FIDO_ERR_INVALID_PARAM;
470
82
                goto out;
471
82
        }
472
473
304
        if (fido_get_signed_hash(cose_alg, &dgst, &assert->cdh,
474
304
            &stmt->authdata_cbor) < 0) {
475
46
                fido_log_debug("%s: fido_get_signed_hash", __func__);
476
46
                r = FIDO_ERR_INTERNAL;
477
46
                goto out;
478
46
        }
479
480
258
        switch (cose_alg) {
481
42
        case COSE_ES256:
482
42
                ok = es256_pk_verify_sig(&dgst, pk, &stmt->sig);
483
42
                break;
484
148
        case COSE_RS256:
485
148
                ok = rs256_pk_verify_sig(&dgst, pk, &stmt->sig);
486
148
                break;
487
68
        case COSE_EDDSA:
488
68
                ok = eddsa_pk_verify_sig(&dgst, pk, &stmt->sig);
489
68
                break;
490
0
        default:
491
0
                fido_log_debug("%s: unsupported cose_alg %d", __func__,
492
0
                    cose_alg);
493
0
                r = FIDO_ERR_UNSUPPORTED_OPTION;
494
0
                goto out;
495
258
        }
496
497
258
        if (ok < 0)
498
258
                r = FIDO_ERR_INVALID_SIG;
499
258
        else
500
258
                r = FIDO_OK;
501
32.4k
out:
502
32.4k
        explicit_bzero(buf, sizeof(buf));
503
504
32.4k
        return (r);
505
258
}
506
507
int
508
fido_assert_set_clientdata(fido_assert_t *assert, const unsigned char *data,
509
    size_t data_len)
510
0
{
511
0
        if (!fido_blob_is_empty(&assert->cdh) ||
512
0
            fido_blob_set(&assert->cd, data, data_len) < 0) {
513
0
                return (FIDO_ERR_INVALID_ARGUMENT);
514
0
        }
515
0
        if (fido_sha256(&assert->cdh, data, data_len) < 0) {
516
0
                fido_blob_reset(&assert->cd);
517
0
                return (FIDO_ERR_INTERNAL);
518
0
        }
519
520
0
        return (FIDO_OK);
521
0
}
522
523
int
524
fido_assert_set_clientdata_hash(fido_assert_t *assert,
525
    const unsigned char *hash, size_t hash_len)
526
36.5k
{
527
36.5k
        if (!fido_blob_is_empty(&assert->cd) ||
528
36.5k
            fido_blob_set(&assert->cdh, hash, hash_len) < 0)
529
439
                return (FIDO_ERR_INVALID_ARGUMENT);
530
531
36.0k
        return (FIDO_OK);
532
36.0k
}
533
534
int
535
fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt,
536
    size_t salt_len)
537
4.05k
{
538
4.05k
        if ((salt_len != 32 && salt_len != 64) ||
539
4.05k
            fido_blob_set(&assert->ext.hmac_salt, salt, salt_len) < 0)
540
3.94k
                return (FIDO_ERR_INVALID_ARGUMENT);
541
542
111
        return (FIDO_OK);
543
111
}
544
545
int
546
fido_assert_set_hmac_secret(fido_assert_t *assert, size_t idx,
547
    const unsigned char *secret, size_t secret_len)
548
0
{
549
0
        if (idx >= assert->stmt_len || (secret_len != 32 && secret_len != 64) ||
550
0
            fido_blob_set(&assert->stmt[idx].hmac_secret, secret,
551
0
            secret_len) < 0)
552
0
                return (FIDO_ERR_INVALID_ARGUMENT);
553
554
0
        return (FIDO_OK);
555
0
}
556
557
int
558
fido_assert_set_rp(fido_assert_t *assert, const char *id)
559
36.5k
{
560
36.5k
        if (assert->rp_id != NULL) {
561
2.02k
                free(assert->rp_id);
562
2.02k
                assert->rp_id = NULL;
563
2.02k
        }
564
565
36.5k
        if (id == NULL)
566
36.5k
                return (FIDO_ERR_INVALID_ARGUMENT);
567
568
36.2k
        if ((assert->rp_id = strdup(id)) == NULL)
569
36.2k
                return (FIDO_ERR_INTERNAL);
570
571
36.1k
        return (FIDO_OK);
572
36.1k
}
573
574
int
575
fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr,
576
    size_t len)
577
63.3k
{
578
63.3k
        fido_blob_t      id;
579
63.3k
        fido_blob_t     *list_ptr;
580
63.3k
        int              r;
581
582
63.3k
        memset(&id, 0, sizeof(id));
583
584
63.3k
        if (assert->allow_list.len == SIZE_MAX) {
585
0
                r = FIDO_ERR_INVALID_ARGUMENT;
586
0
                goto fail;
587
0
        }
588
589
63.3k
        if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr =
590
62.8k
            recallocarray(assert->allow_list.ptr, assert->allow_list.len,
591
62.8k
            assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) {
592
651
                r = FIDO_ERR_INVALID_ARGUMENT;
593
651
                goto fail;
594
651
        }
595
596
62.7k
        list_ptr[assert->allow_list.len++] = id;
597
62.7k
        assert->allow_list.ptr = list_ptr;
598
599
62.7k
        return (FIDO_OK);
600
651
fail:
601
651
        free(id.ptr);
602
603
651
        return (r);
604
605
62.7k
}
606
607
int
608
fido_assert_set_extensions(fido_assert_t *assert, int ext)
609
33.9k
{
610
33.9k
        if (ext == 0)
611
1.18k
                assert->ext.mask = 0;
612
32.7k
        else {
613
32.7k
                if ((ext & FIDO_EXT_ASSERT_MASK) != ext)
614
31.2k
                        return (FIDO_ERR_INVALID_ARGUMENT);
615
1.47k
                assert->ext.mask |= ext;
616
1.47k
        }
617
618
33.9k
        return (FIDO_OK);
619
33.9k
}
620
621
int
622
fido_assert_set_options(fido_assert_t *assert, bool up, bool uv)
623
0
{
624
0
        assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
625
0
        assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
626
627
0
        return (FIDO_OK);
628
0
}
629
630
int
631
fido_assert_set_up(fido_assert_t *assert, fido_opt_t up)
632
15.7k
{
633
15.7k
        assert->up = up;
634
635
15.7k
        return (FIDO_OK);
636
15.7k
}
637
638
int
639
fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv)
640
1.26k
{
641
1.26k
        assert->uv = uv;
642
643
1.26k
        return (FIDO_OK);
644
1.26k
}
645
646
const unsigned char *
647
fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
648
32.5k
{
649
32.5k
        return (assert->cdh.ptr);
650
32.5k
}
651
652
size_t
653
fido_assert_clientdata_hash_len(const fido_assert_t *assert)
654
32.5k
{
655
32.5k
        return (assert->cdh.len);
656
32.5k
}
657
658
fido_assert_t *
659
fido_assert_new(void)
660
34.9k
{
661
34.9k
        return (calloc(1, sizeof(fido_assert_t)));
662
34.9k
}
663
664
void
665
fido_assert_reset_tx(fido_assert_t *assert)
666
34.8k
{
667
34.8k
        free(assert->rp_id);
668
34.8k
        fido_blob_reset(&assert->cd);
669
34.8k
        fido_blob_reset(&assert->cdh);
670
34.8k
        fido_blob_reset(&assert->ext.hmac_salt);
671
34.8k
        fido_free_blob_array(&assert->allow_list);
672
34.8k
        memset(&assert->ext, 0, sizeof(assert->ext));
673
34.8k
        memset(&assert->allow_list, 0, sizeof(assert->allow_list));
674
34.8k
        assert->rp_id = NULL;
675
34.8k
        assert->up = FIDO_OPT_OMIT;
676
34.8k
        assert->uv = FIDO_OPT_OMIT;
677
34.8k
}
678
679
static void fido_assert_reset_extattr(fido_assert_extattr_t *ext)
680
65.2k
{
681
65.2k
        fido_blob_reset(&ext->hmac_secret_enc);
682
65.2k
        fido_blob_reset(&ext->blob);
683
65.2k
        memset(ext, 0, sizeof(*ext));
684
65.2k
}
685
686
void
687
fido_assert_reset_rx(fido_assert_t *assert)
688
35.3k
{
689
99.3k
        for (size_t i = 0; i < assert->stmt_cnt; i++) {
690
63.9k
                free(assert->stmt[i].user.icon);
691
63.9k
                free(assert->stmt[i].user.name);
692
63.9k
                free(assert->stmt[i].user.display_name);
693
63.9k
                fido_blob_reset(&assert->stmt[i].user.id);
694
63.9k
                fido_blob_reset(&assert->stmt[i].id);
695
63.9k
                fido_blob_reset(&assert->stmt[i].hmac_secret);
696
63.9k
                fido_blob_reset(&assert->stmt[i].authdata_cbor);
697
63.9k
                fido_blob_reset(&assert->stmt[i].largeblob_key);
698
63.9k
                fido_blob_reset(&assert->stmt[i].sig);
699
63.9k
                fido_assert_reset_extattr(&assert->stmt[i].authdata_ext);
700
63.9k
                memset(&assert->stmt[i], 0, sizeof(assert->stmt[i]));
701
63.9k
        }
702
35.3k
        free(assert->stmt);
703
35.3k
        assert->stmt = NULL;
704
35.3k
        assert->stmt_len = 0;
705
35.3k
        assert->stmt_cnt = 0;
706
35.3k
}
707
708
void
709
fido_assert_free(fido_assert_t **assert_p)
710
34.8k
{
711
34.8k
        fido_assert_t *assert;
712
713
34.8k
        if (assert_p == NULL || (assert = *assert_p) == NULL)
714
34.8k
                return;
715
34.8k
        fido_assert_reset_tx(assert);
716
34.8k
        fido_assert_reset_rx(assert);
717
34.8k
        free(assert);
718
34.8k
        *assert_p = NULL;
719
34.8k
}
720
721
size_t
722
fido_assert_count(const fido_assert_t *assert)
723
34.9k
{
724
34.9k
        return (assert->stmt_len);
725
34.9k
}
726
727
const char *
728
fido_assert_rp_id(const fido_assert_t *assert)
729
32.5k
{
730
32.5k
        return (assert->rp_id);
731
32.5k
}
732
733
uint8_t
734
fido_assert_flags(const fido_assert_t *assert, size_t idx)
735
32.5k
{
736
32.5k
        if (idx >= assert->stmt_len)
737
2.34k
                return (0);
738
739
30.2k
        return (assert->stmt[idx].authdata.flags);
740
30.2k
}
741
742
uint32_t
743
fido_assert_sigcount(const fido_assert_t *assert, size_t idx)
744
32.5k
{
745
32.5k
        if (idx >= assert->stmt_len)
746
2.34k
                return (0);
747
748
30.2k
        return (assert->stmt[idx].authdata.sigcount);
749
30.2k
}
750
751
const unsigned char *
752
fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx)
753
32.5k
{
754
32.5k
        if (idx >= assert->stmt_len)
755
2.34k
                return (NULL);
756
757
30.2k
        return (assert->stmt[idx].authdata_cbor.ptr);
758
30.2k
}
759
760
size_t
761
fido_assert_authdata_len(const fido_assert_t *assert, size_t idx)
762
32.5k
{
763
32.5k
        if (idx >= assert->stmt_len)
764
2.34k
                return (0);
765
766
30.2k
        return (assert->stmt[idx].authdata_cbor.len);
767
30.2k
}
768
769
const unsigned char *
770
fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx)
771
32.5k
{
772
32.5k
        if (idx >= assert->stmt_len)
773
2.34k
                return (NULL);
774
775
30.2k
        return (assert->stmt[idx].sig.ptr);
776
30.2k
}
777
778
size_t
779
fido_assert_sig_len(const fido_assert_t *assert, size_t idx)
780
32.5k
{
781
32.5k
        if (idx >= assert->stmt_len)
782
2.34k
                return (0);
783
784
30.2k
        return (assert->stmt[idx].sig.len);
785
30.2k
}
786
787
const unsigned char *
788
fido_assert_id_ptr(const fido_assert_t *assert, size_t idx)
789
32.5k
{
790
32.5k
        if (idx >= assert->stmt_len)
791
2.34k
                return (NULL);
792
793
30.2k
        return (assert->stmt[idx].id.ptr);
794
30.2k
}
795
796
size_t
797
fido_assert_id_len(const fido_assert_t *assert, size_t idx)
798
32.5k
{
799
32.5k
        if (idx >= assert->stmt_len)
800
2.34k
                return (0);
801
802
30.2k
        return (assert->stmt[idx].id.len);
803
30.2k
}
804
805
const unsigned char *
806
fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx)
807
32.5k
{
808
32.5k
        if (idx >= assert->stmt_len)
809
2.34k
                return (NULL);
810
811
30.2k
        return (assert->stmt[idx].user.id.ptr);
812
30.2k
}
813
814
size_t
815
fido_assert_user_id_len(const fido_assert_t *assert, size_t idx)
816
32.5k
{
817
32.5k
        if (idx >= assert->stmt_len)
818
2.34k
                return (0);
819
820
30.2k
        return (assert->stmt[idx].user.id.len);
821
30.2k
}
822
823
const char *
824
fido_assert_user_icon(const fido_assert_t *assert, size_t idx)
825
32.5k
{
826
32.5k
        if (idx >= assert->stmt_len)
827
2.34k
                return (NULL);
828
829
30.2k
        return (assert->stmt[idx].user.icon);
830
30.2k
}
831
832
const char *
833
fido_assert_user_name(const fido_assert_t *assert, size_t idx)
834
32.5k
{
835
32.5k
        if (idx >= assert->stmt_len)
836
2.34k
                return (NULL);
837
838
30.2k
        return (assert->stmt[idx].user.name);
839
30.2k
}
840
841
const char *
842
fido_assert_user_display_name(const fido_assert_t *assert, size_t idx)
843
32.5k
{
844
32.5k
        if (idx >= assert->stmt_len)
845
2.34k
                return (NULL);
846
847
30.2k
        return (assert->stmt[idx].user.display_name);
848
30.2k
}
849
850
const unsigned char *
851
fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx)
852
32.5k
{
853
32.5k
        if (idx >= assert->stmt_len)
854
2.34k
                return (NULL);
855
856
30.2k
        return (assert->stmt[idx].hmac_secret.ptr);
857
30.2k
}
858
859
size_t
860
fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx)
861
32.5k
{
862
32.5k
        if (idx >= assert->stmt_len)
863
2.34k
                return (0);
864
865
30.2k
        return (assert->stmt[idx].hmac_secret.len);
866
30.2k
}
867
868
const unsigned char *
869
fido_assert_largeblob_key_ptr(const fido_assert_t *assert, size_t idx)
870
32.5k
{
871
32.5k
        if (idx >= assert->stmt_len)
872
2.34k
                return (NULL);
873
874
30.2k
        return (assert->stmt[idx].largeblob_key.ptr);
875
30.2k
}
876
877
size_t
878
fido_assert_largeblob_key_len(const fido_assert_t *assert, size_t idx)
879
32.5k
{
880
32.5k
        if (idx >= assert->stmt_len)
881
2.34k
                return (0);
882
883
30.2k
        return (assert->stmt[idx].largeblob_key.len);
884
30.2k
}
885
886
const unsigned char *
887
fido_assert_blob_ptr(const fido_assert_t *assert, size_t idx)
888
32.5k
{
889
32.5k
        if (idx >= assert->stmt_len)
890
2.34k
                return (NULL);
891
892
30.2k
        return (assert->stmt[idx].authdata_ext.blob.ptr);
893
30.2k
}
894
895
size_t
896
fido_assert_blob_len(const fido_assert_t *assert, size_t idx)
897
32.5k
{
898
32.5k
        if (idx >= assert->stmt_len)
899
2.34k
                return (0);
900
901
30.2k
        return (assert->stmt[idx].authdata_ext.blob.len);
902
30.2k
}
903
904
static void
905
fido_assert_clean_authdata(fido_assert_stmt *stmt)
906
1.32k
{
907
1.32k
        fido_blob_reset(&stmt->authdata_cbor);
908
1.32k
        fido_assert_reset_extattr(&stmt->authdata_ext);
909
1.32k
        memset(&stmt->authdata, 0, sizeof(stmt->authdata));
910
1.32k
}
911
912
int
913
fido_assert_set_authdata(fido_assert_t *assert, size_t idx,
914
    const unsigned char *ptr, size_t len)
915
65.0k
{
916
65.0k
        cbor_item_t             *item = NULL;
917
65.0k
        fido_assert_stmt        *stmt = NULL;
918
65.0k
        struct cbor_load_result  cbor;
919
65.0k
        int                      r;
920
921
65.0k
        if (idx >= assert->stmt_len || ptr == NULL || len == 0)
922
63.9k
                return (FIDO_ERR_INVALID_ARGUMENT);
923
924
1.17k
        stmt = &assert->stmt[idx];
925
1.17k
        fido_assert_clean_authdata(stmt);
926
927
1.17k
        if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
928
22
                fido_log_debug("%s: cbor_load", __func__);
929
22
                r = FIDO_ERR_INVALID_ARGUMENT;
930
22
                goto fail;
931
22
        }
932
933
1.15k
        if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
934
1.15k
            &stmt->authdata, &stmt->authdata_ext) < 0) {
935
30
                fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
936
30
                r = FIDO_ERR_INVALID_ARGUMENT;
937
30
                goto fail;
938
30
        }
939
940
1.12k
        r = FIDO_OK;
941
1.17k
fail:
942
1.17k
        if (item != NULL)
943
1.17k
                cbor_decref(&item);
944
945
1.17k
        if (r != FIDO_OK)
946
1.17k
                fido_assert_clean_authdata(stmt);
947
948
1.17k
        return (r);
949
1.12k
}
950
951
int
952
fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx,
953
    const unsigned char *ptr, size_t len)
954
63.9k
{
955
63.9k
        cbor_item_t             *item = NULL;
956
63.9k
        fido_assert_stmt        *stmt = NULL;
957
63.9k
        int                      r;
958
959
63.9k
        if (idx >= assert->stmt_len || ptr == NULL || len == 0)
960
63.9k
                return (FIDO_ERR_INVALID_ARGUMENT);
961
962
50
        stmt = &assert->stmt[idx];
963
50
        fido_assert_clean_authdata(stmt);
964
965
50
        if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
966
2
                fido_log_debug("%s: cbor_build_bytestring", __func__);
967
2
                r = FIDO_ERR_INTERNAL;
968
2
                goto fail;
969
2
        }
970
971
48
        if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
972
48
            &stmt->authdata, &stmt->authdata_ext) < 0) {
973
38
                fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
974
38
                r = FIDO_ERR_INVALID_ARGUMENT;
975
38
                goto fail;
976
38
        }
977
978
10
        r = FIDO_OK;
979
50
fail:
980
50
        if (item != NULL)
981
50
                cbor_decref(&item);
982
983
50
        if (r != FIDO_OK)
984
50
                fido_assert_clean_authdata(stmt);
985
986
50
        return (r);
987
10
}
988
989
int
990
fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr,
991
    size_t len)
992
65.0k
{
993
65.0k
        if (idx >= a->stmt_len || ptr == NULL || len == 0)
994
63.9k
                return (FIDO_ERR_INVALID_ARGUMENT);
995
1.11k
        if (fido_blob_set(&a->stmt[idx].sig, ptr, len) < 0)
996
14
                return (FIDO_ERR_INTERNAL);
997
998
1.10k
        return (FIDO_OK);
999
1.10k
}
1000
1001
/* XXX shrinking leaks memory; fortunately that shouldn't happen */
1002
int
1003
fido_assert_set_count(fido_assert_t *assert, size_t n)
1004
33.1k
{
1005
33.1k
        void *new_stmt;
1006
1007
33.1k
#ifdef FIDO_FUZZ
1008
33.1k
        if (n > UINT8_MAX) {
1009
44
                fido_log_debug("%s: n > UINT8_MAX", __func__);
1010
44
                return (FIDO_ERR_INTERNAL);
1011
44
        }
1012
33.1k
#endif
1013
1014
33.1k
        new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n,
1015
33.1k
            sizeof(fido_assert_stmt));
1016
33.1k
        if (new_stmt == NULL)
1017
33.1k
                return (FIDO_ERR_INTERNAL);
1018
1019
33.0k
        assert->stmt = new_stmt;
1020
33.0k
        assert->stmt_cnt = n;
1021
33.0k
        assert->stmt_len = n;
1022
1023
33.0k
        return (FIDO_OK);
1024
33.0k
}