Coverage Report

Created: 2022-04-27 14:33

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