Coverage Report

Created: 2022-07-22 12:05

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