Coverage Report

Created: 2021-07-20 18:14

/libfido2/src/cred.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2018 Yubico AB. All rights reserved.
3
 * Use of this source code is governed by a BSD-style
4
 * license that can be found in the LICENSE file.
5
 */
6
7
#include <openssl/sha.h>
8
#include <openssl/x509.h>
9
10
#include "fido.h"
11
#include "fido/es256.h"
12
13
static int
14
parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
15
1.13k
{
16
1.13k
        fido_cred_t *cred = arg;
17
1.13k
18
1.13k
        if (cbor_isa_uint(key) == false ||
19
1.13k
            cbor_int_get_width(key) != CBOR_INT_8) {
20
20
                fido_log_debug("%s: cbor type", __func__);
21
20
                return (0); /* ignore */
22
20
        }
23
1.11k
24
1.11k
        switch (cbor_get_uint8(key)) {
25
464
        case 1: /* fmt */
26
464
                return (cbor_decode_fmt(val, &cred->fmt));
27
433
        case 2: /* authdata */
28
433
                if (fido_blob_decode(val, &cred->authdata_raw) < 0) {
29
1
                        fido_log_debug("%s: fido_blob_decode", __func__);
30
1
                        return (-1);
31
1
                }
32
432
                return (cbor_decode_cred_authdata(val, cred->type,
33
432
                    &cred->authdata_cbor, &cred->authdata, &cred->attcred,
34
432
                    &cred->authdata_ext));
35
432
        case 3: /* attestation statement */
36
203
                return (cbor_decode_attstmt(val, &cred->attstmt));
37
432
        case 5: /* large blob key */
38
1
                return (fido_blob_decode(val, &cred->largeblob_key));
39
432
        default: /* ignore */
40
12
                fido_log_debug("%s: cbor type", __func__);
41
12
                return (0);
42
1.11k
        }
43
1.11k
}
44
45
static int
46
fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
47
968
{
48
968
        fido_blob_t      f;
49
968
        fido_blob_t     *ecdh = NULL;
50
968
        fido_opt_t       uv = cred->uv;
51
968
        es256_pk_t      *pk = NULL;
52
968
        cbor_item_t     *argv[9];
53
968
        const uint8_t    cmd = CTAP_CBOR_MAKECRED;
54
968
        int              r;
55
968
56
968
        memset(&f, 0, sizeof(f));
57
968
        memset(argv, 0, sizeof(argv));
58
968
59
968
        if (cred->cdh.ptr == NULL || cred->type == 0) {
60
1
                fido_log_debug("%s: cdh=%p, type=%d", __func__,
61
1
                    (void *)cred->cdh.ptr, cred->type);
62
1
                r = FIDO_ERR_INVALID_ARGUMENT;
63
1
                goto fail;
64
1
        }
65
967
66
967
        if ((argv[0] = fido_blob_encode(&cred->cdh)) == NULL ||
67
967
            (argv[1] = cbor_encode_rp_entity(&cred->rp)) == NULL ||
68
967
            (argv[2] = cbor_encode_user_entity(&cred->user)) == NULL ||
69
967
            (argv[3] = cbor_encode_pubkey_param(cred->type)) == NULL) {
70
46
                fido_log_debug("%s: cbor encode", __func__);
71
46
                r = FIDO_ERR_INTERNAL;
72
46
                goto fail;
73
46
        }
74
921
75
921
        /* excluded credentials */
76
921
        if (cred->excl.len)
77
657
                if ((argv[4] = cbor_encode_pubkey_list(&cred->excl)) == NULL) {
78
54
                        fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
79
54
                        r = FIDO_ERR_INTERNAL;
80
54
                        goto fail;
81
54
                }
82
867
83
867
        /* extensions */
84
867
        if (cred->ext.mask)
85
694
                if ((argv[5] = cbor_encode_cred_ext(&cred->ext,
86
694
                    &cred->blob)) == NULL) {
87
10
                        fido_log_debug("%s: cbor_encode_cred_ext", __func__);
88
10
                        r = FIDO_ERR_INTERNAL;
89
10
                        goto fail;
90
10
                }
91
857
92
857
        /* user verification */
93
857
        if (pin != NULL || (uv == FIDO_OPT_TRUE &&
94
697
            fido_dev_supports_permissions(dev))) {
95
697
                if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
96
177
                        fido_log_debug("%s: fido_do_ecdh", __func__);
97
177
                        goto fail;
98
177
                }
99
520
                if ((r = cbor_add_uv_params(dev, cmd, &cred->cdh, pk, ecdh,
100
520
                    pin, cred->rp.id, &argv[7], &argv[8])) != FIDO_OK) {
101
109
                        fido_log_debug("%s: cbor_add_uv_params", __func__);
102
109
                        goto fail;
103
109
                }
104
411
                uv = FIDO_OPT_OMIT;
105
411
        }
106
857
107
857
        /* options */
108
857
        if (cred->rk != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT)
109
228
                if ((argv[6] = cbor_encode_cred_opt(cred->rk, uv)) == NULL) {
110
3
                        fido_log_debug("%s: cbor_encode_cred_opt", __func__);
111
3
                        r = FIDO_ERR_INTERNAL;
112
3
                        goto fail;
113
3
                }
114
568
115
568
        /* framing and transmission */
116
568
        if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
117
568
            fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
118
60
                fido_log_debug("%s: fido_tx", __func__);
119
60
                r = FIDO_ERR_TX;
120
60
                goto fail;
121
60
        }
122
508
123
508
        r = FIDO_OK;
124
968
fail:
125
968
        es256_pk_free(&pk);
126
968
        fido_blob_free(&ecdh);
127
968
        cbor_vector_free(argv, nitems(argv));
128
968
        free(f.ptr);
129
968
130
968
        return (r);
131
508
}
132
133
static int
134
fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int ms)
135
508
{
136
508
        unsigned char   reply[FIDO_MAXMSG];
137
508
        int             reply_len;
138
508
        int             r;
139
508
140
508
        fido_cred_reset_rx(cred);
141
508
142
508
        if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
143
508
            ms)) < 0) {
144
20
                fido_log_debug("%s: fido_rx", __func__);
145
20
                return (FIDO_ERR_RX);
146
20
        }
147
488
148
488
        if ((r = cbor_parse_reply(reply, (size_t)reply_len, cred,
149
488
            parse_makecred_reply)) != FIDO_OK) {
150
337
                fido_log_debug("%s: parse_makecred_reply", __func__);
151
337
                return (r);
152
337
        }
153
151
154
151
        if (cred->fmt == NULL || fido_blob_is_empty(&cred->authdata_cbor) ||
155
151
            fido_blob_is_empty(&cred->attcred.id)) {
156
9
                fido_cred_reset_rx(cred);
157
9
                return (FIDO_ERR_INVALID_CBOR);
158
9
        }
159
142
160
142
        return (FIDO_OK);
161
142
}
162
163
static int
164
fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
165
    int ms)
166
968
{
167
968
        int  r;
168
968
169
968
        if ((r = fido_dev_make_cred_tx(dev, cred, pin)) != FIDO_OK ||
170
968
            (r = fido_dev_make_cred_rx(dev, cred, ms)) != FIDO_OK)
171
968
                return (r);
172
142
173
142
        return (FIDO_OK);
174
142
}
175
176
int
177
fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
178
1.81k
{
179
#ifdef USE_WINHELLO
180
        if (dev->flags & FIDO_DEV_WINHELLO)
181
                return (fido_winhello_make_cred(dev, cred, pin));
182
#endif
183
1.81k
        if (fido_dev_is_fido2(dev) == false) {
184
848
                if (pin != NULL || cred->rk == FIDO_OPT_TRUE ||
185
848
                    cred->ext.mask != 0)
186
542
                        return (FIDO_ERR_UNSUPPORTED_OPTION);
187
306
                return (u2f_register(dev, cred, -1));
188
306
        }
189
968
190
968
        return (fido_dev_make_cred_wait(dev, cred, pin, -1));
191
968
}
192
193
static int
194
check_extensions(const fido_cred_ext_t *authdata_ext,
195
    const fido_cred_ext_t *ext)
196
99
{
197
99
        fido_cred_ext_t  tmp;
198
99
199
99
        /* XXX: largeBlobKey is not part of the extensions map */
200
99
        memcpy(&tmp, ext, sizeof(tmp));
201
99
        tmp.mask &= ~FIDO_EXT_LARGEBLOB_KEY;
202
99
203
99
        return (timingsafe_bcmp(authdata_ext, &tmp, sizeof(*authdata_ext)));
204
99
}
205
206
int
207
fido_check_rp_id(const char *id, const unsigned char *obtained_hash)
208
563
{
209
563
        unsigned char expected_hash[SHA256_DIGEST_LENGTH];
210
563
211
563
        explicit_bzero(expected_hash, sizeof(expected_hash));
212
563
213
563
        if (SHA256((const unsigned char *)id, strlen(id),
214
563
            expected_hash) != expected_hash) {
215
9
                fido_log_debug("%s: sha256", __func__);
216
9
                return (-1);
217
9
        }
218
554
219
554
        return (timingsafe_bcmp(expected_hash, obtained_hash,
220
554
            SHA256_DIGEST_LENGTH));
221
554
}
222
223
static int
224
get_signed_hash_u2f(fido_blob_t *dgst, const unsigned char *rp_id,
225
    size_t rp_id_len, const fido_blob_t *clientdata, const fido_blob_t *id,
226
    const es256_pk_t *pk)
227
25
{
228
25
        const uint8_t           zero = 0;
229
25
        const uint8_t           four = 4; /* uncompressed point */
230
25
        SHA256_CTX              ctx;
231
25
232
25
        if (dgst->len != SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
233
25
            SHA256_Update(&ctx, &zero, sizeof(zero)) == 0 ||
234
25
            SHA256_Update(&ctx, rp_id, rp_id_len) == 0 ||
235
25
            SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
236
25
            SHA256_Update(&ctx, id->ptr, id->len) == 0 ||
237
25
            SHA256_Update(&ctx, &four, sizeof(four)) == 0 ||
238
25
            SHA256_Update(&ctx, pk->x, sizeof(pk->x)) == 0 ||
239
25
            SHA256_Update(&ctx, pk->y, sizeof(pk->y)) == 0 ||
240
25
            SHA256_Final(dgst->ptr, &ctx) == 0) {
241
10
                fido_log_debug("%s: sha256", __func__);
242
10
                return (-1);
243
10
        }
244
15
245
15
        return (0);
246
15
}
247
248
static int
249
verify_sig(const fido_blob_t *dgst, const fido_blob_t *x5c,
250
    const fido_blob_t *sig)
251
23
{
252
23
        BIO             *rawcert = NULL;
253
23
        X509            *cert = NULL;
254
23
        EVP_PKEY        *pkey = NULL;
255
23
        EC_KEY          *ec;
256
23
        int              ok = -1;
257
23
258
23
        /* openssl needs ints */
259
23
        if (dgst->len > INT_MAX || x5c->len > INT_MAX || sig->len > INT_MAX) {
260
0
                fido_log_debug("%s: dgst->len=%zu, x5c->len=%zu, sig->len=%zu",
261
0
                    __func__, dgst->len, x5c->len, sig->len);
262
0
                return (-1);
263
0
        }
264
23
265
23
        /* fetch key from x509 */
266
23
        if ((rawcert = BIO_new_mem_buf(x5c->ptr, (int)x5c->len)) == NULL ||
267
23
            (cert = d2i_X509_bio(rawcert, NULL)) == NULL ||
268
23
            (pkey = X509_get_pubkey(cert)) == NULL ||
269
23
            (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
270
11
                fido_log_debug("%s: x509 key", __func__);
271
11
                goto fail;
272
11
        }
273
12
274
12
        if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
275
12
            (int)sig->len, ec) != 1) {
276
12
                fido_log_debug("%s: ECDSA_verify", __func__);
277
12
                goto fail;
278
12
        }
279
0
280
0
        ok = 0;
281
23
fail:
282
23
        if (rawcert != NULL)
283
23
                BIO_free(rawcert);
284
23
        if (cert != NULL)
285
23
                X509_free(cert);
286
23
        if (pkey != NULL)
287
23
                EVP_PKEY_free(pkey);
288
23
289
23
        return (ok);
290
0
}
291
292
int
293
fido_cred_verify(const fido_cred_t *cred)
294
1.96k
{
295
1.96k
        unsigned char   buf[SHA256_DIGEST_LENGTH];
296
1.96k
        fido_blob_t     dgst;
297
1.96k
        int             r;
298
1.96k
299
1.96k
        dgst.ptr = buf;
300
1.96k
        dgst.len = sizeof(buf);
301
1.96k
302
1.96k
        /* do we have everything we need? */
303
1.96k
        if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
304
1.96k
            cred->attstmt.x5c.ptr == NULL || cred->attstmt.sig.ptr == NULL ||
305
1.96k
            cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
306
1.96k
            cred->rp.id == NULL) {
307
1.89k
                fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, "
308
1.89k
                    "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
309
1.89k
                    (void *)cred->authdata_cbor.ptr,
310
1.89k
                    (void *)cred->attstmt.x5c.ptr,
311
1.89k
                    (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
312
1.89k
                    (void *)cred->attcred.id.ptr, cred->rp.id);
313
1.89k
                r = FIDO_ERR_INVALID_ARGUMENT;
314
1.89k
                goto out;
315
1.89k
        }
316
75
317
75
        if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
318
36
                fido_log_debug("%s: fido_check_rp_id", __func__);
319
36
                r = FIDO_ERR_INVALID_PARAM;
320
36
                goto out;
321
36
        }
322
39
323
39
        if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE,
324
39
            cred->uv) < 0) {
325
2
                fido_log_debug("%s: fido_check_flags", __func__);
326
2
                r = FIDO_ERR_INVALID_PARAM;
327
2
                goto out;
328
2
        }
329
37
330
37
        if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) {
331
1
                fido_log_debug("%s: check_extensions", __func__);
332
1
                r = FIDO_ERR_INVALID_PARAM;
333
1
                goto out;
334
1
        }
335
36
336
36
        if (!strcmp(cred->fmt, "packed")) {
337
12
                if (fido_get_signed_hash(COSE_ES256, &dgst, &cred->cdh,
338
12
                    &cred->authdata_cbor) < 0) {
339
4
                        fido_log_debug("%s: fido_get_signed_hash", __func__);
340
4
                        r = FIDO_ERR_INTERNAL;
341
4
                        goto out;
342
4
                }
343
24
        } else if (!strcmp(cred->fmt, "fido-u2f")) {
344
24
                if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
345
24
                    sizeof(cred->authdata.rp_id_hash), &cred->cdh,
346
24
                    &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
347
9
                        fido_log_debug("%s: get_signed_hash_u2f", __func__);
348
9
                        r = FIDO_ERR_INTERNAL;
349
9
                        goto out;
350
9
                }
351
0
        } else {
352
0
                fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt);
353
0
                r = FIDO_ERR_INVALID_ARGUMENT;
354
0
                goto out;
355
0
        }
356
23
357
23
        if (verify_sig(&dgst, &cred->attstmt.x5c, &cred->attstmt.sig) < 0) {
358
23
                fido_log_debug("%s: verify_sig", __func__);
359
23
                r = FIDO_ERR_INVALID_SIG;
360
23
                goto out;
361
23
        }
362
0
363
0
        r = FIDO_OK;
364
1.96k
out:
365
1.96k
        explicit_bzero(buf, sizeof(buf));
366
1.96k
367
1.96k
        return (r);
368
0
}
369
370
int
371
fido_cred_verify_self(const fido_cred_t *cred)
372
1.96k
{
373
1.96k
        unsigned char   buf[1024]; /* XXX */
374
1.96k
        fido_blob_t     dgst;
375
1.96k
        int             ok = -1;
376
1.96k
        int             r;
377
1.96k
378
1.96k
        dgst.ptr = buf;
379
1.96k
        dgst.len = sizeof(buf);
380
1.96k
381
1.96k
        /* do we have everything we need? */
382
1.96k
        if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
383
1.96k
            cred->attstmt.x5c.ptr != NULL || cred->attstmt.sig.ptr == NULL ||
384
1.96k
            cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
385
1.96k
            cred->rp.id == NULL) {
386
1.88k
                fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, "
387
1.88k
                    "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
388
1.88k
                    (void *)cred->authdata_cbor.ptr,
389
1.88k
                    (void *)cred->attstmt.x5c.ptr,
390
1.88k
                    (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
391
1.88k
                    (void *)cred->attcred.id.ptr, cred->rp.id);
392
1.88k
                r = FIDO_ERR_INVALID_ARGUMENT;
393
1.88k
                goto out;
394
1.88k
        }
395
81
396
81
        if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
397
18
                fido_log_debug("%s: fido_check_rp_id", __func__);
398
18
                r = FIDO_ERR_INVALID_PARAM;
399
18
                goto out;
400
18
        }
401
63
402
63
        if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE,
403
63
            cred->uv) < 0) {
404
1
                fido_log_debug("%s: fido_check_flags", __func__);
405
1
                r = FIDO_ERR_INVALID_PARAM;
406
1
                goto out;
407
1
        }
408
62
409
62
        if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) {
410
2
                fido_log_debug("%s: check_extensions", __func__);
411
2
                r = FIDO_ERR_INVALID_PARAM;
412
2
                goto out;
413
2
        }
414
60
415
60
        if (!strcmp(cred->fmt, "packed")) {
416
59
                if (fido_get_signed_hash(cred->attcred.type, &dgst, &cred->cdh,
417
59
                    &cred->authdata_cbor) < 0) {
418
2
                        fido_log_debug("%s: fido_get_signed_hash", __func__);
419
2
                        r = FIDO_ERR_INTERNAL;
420
2
                        goto out;
421
2
                }
422
1
        } else if (!strcmp(cred->fmt, "fido-u2f")) {
423
1
                if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
424
1
                    sizeof(cred->authdata.rp_id_hash), &cred->cdh,
425
1
                    &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
426
1
                        fido_log_debug("%s: get_signed_hash_u2f", __func__);
427
1
                        r = FIDO_ERR_INTERNAL;
428
1
                        goto out;
429
1
                }
430
0
        } else {
431
0
                fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt);
432
0
                r = FIDO_ERR_INVALID_ARGUMENT;
433
0
                goto out;
434
0
        }
435
57
436
57
        switch (cred->attcred.type) {
437
11
        case COSE_ES256:
438
11
                ok = fido_verify_sig_es256(&dgst, &cred->attcred.pubkey.es256,
439
11
                    &cred->attstmt.sig);
440
11
                break;
441
17
        case COSE_RS256:
442
17
                ok = fido_verify_sig_rs256(&dgst, &cred->attcred.pubkey.rs256,
443
17
                    &cred->attstmt.sig);
444
17
                break;
445
29
        case COSE_EDDSA:
446
29
                ok = fido_verify_sig_eddsa(&dgst, &cred->attcred.pubkey.eddsa,
447
29
                    &cred->attstmt.sig);
448
29
                break;
449
0
        default:
450
0
                fido_log_debug("%s: unsupported cose_alg %d", __func__,
451
0
                    cred->attcred.type);
452
0
                r = FIDO_ERR_UNSUPPORTED_OPTION;
453
0
                goto out;
454
57
        }
455
57
456
57
        if (ok < 0)
457
57
                r = FIDO_ERR_INVALID_SIG;
458
57
        else
459
57
                r = FIDO_OK;
460
57
461
1.96k
out:
462
1.96k
        explicit_bzero(buf, sizeof(buf));
463
1.96k
464
1.96k
        return (r);
465
57
}
466
467
fido_cred_t *
468
fido_cred_new(void)
469
6.59k
{
470
6.59k
        return (calloc(1, sizeof(fido_cred_t)));
471
6.59k
}
472
473
static void
474
fido_cred_clean_authdata(fido_cred_t *cred)
475
35.9k
{
476
35.9k
        fido_blob_reset(&cred->authdata_cbor);
477
35.9k
        fido_blob_reset(&cred->authdata_raw);
478
35.9k
        fido_blob_reset(&cred->attcred.id);
479
35.9k
480
35.9k
        memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext));
481
35.9k
        memset(&cred->authdata, 0, sizeof(cred->authdata));
482
35.9k
        memset(&cred->attcred, 0, sizeof(cred->attcred));
483
35.9k
}
484
485
void
486
fido_cred_reset_tx(fido_cred_t *cred)
487
21.0k
{
488
21.0k
        fido_blob_reset(&cred->cd);
489
21.0k
        fido_blob_reset(&cred->cdh);
490
21.0k
        fido_blob_reset(&cred->user.id);
491
21.0k
        fido_blob_reset(&cred->blob);
492
21.0k
493
21.0k
        free(cred->rp.id);
494
21.0k
        free(cred->rp.name);
495
21.0k
        free(cred->user.icon);
496
21.0k
        free(cred->user.name);
497
21.0k
        free(cred->user.display_name);
498
21.0k
        fido_free_blob_array(&cred->excl);
499
21.0k
500
21.0k
        memset(&cred->rp, 0, sizeof(cred->rp));
501
21.0k
        memset(&cred->user, 0, sizeof(cred->user));
502
21.0k
        memset(&cred->excl, 0, sizeof(cred->excl));
503
21.0k
        memset(&cred->ext, 0, sizeof(cred->ext));
504
21.0k
505
21.0k
        cred->type = 0;
506
21.0k
        cred->rk = FIDO_OPT_OMIT;
507
21.0k
        cred->uv = FIDO_OPT_OMIT;
508
21.0k
}
509
510
void
511
fido_cred_reset_rx(fido_cred_t *cred)
512
21.5k
{
513
21.5k
        free(cred->fmt);
514
21.5k
        cred->fmt = NULL;
515
21.5k
        fido_cred_clean_authdata(cred);
516
21.5k
        fido_blob_reset(&cred->attstmt.x5c);
517
21.5k
        fido_blob_reset(&cred->attstmt.sig);
518
21.5k
        fido_blob_reset(&cred->largeblob_key);
519
21.5k
}
520
521
void
522
fido_cred_free(fido_cred_t **cred_p)
523
6.53k
{
524
6.53k
        fido_cred_t *cred;
525
6.53k
526
6.53k
        if (cred_p == NULL || (cred = *cred_p) == NULL)
527
6.53k
                return;
528
6.53k
        fido_cred_reset_tx(cred);
529
6.53k
        fido_cred_reset_rx(cred);
530
6.53k
        free(cred);
531
6.53k
        *cred_p = NULL;
532
6.53k
}
533
534
int
535
fido_cred_set_authdata(fido_cred_t *cred, const unsigned char *ptr, size_t len)
536
3.97k
{
537
3.97k
        cbor_item_t             *item = NULL;
538
3.97k
        struct cbor_load_result  cbor;
539
3.97k
        int                      r = FIDO_ERR_INVALID_ARGUMENT;
540
3.97k
541
3.97k
        fido_cred_clean_authdata(cred);
542
3.97k
543
3.97k
        if (ptr == NULL || len == 0)
544
3.07k
                goto fail;
545
901
546
901
        if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
547
22
                fido_log_debug("%s: cbor_load", __func__);
548
22
                goto fail;
549
22
        }
550
879
551
879
        if (fido_blob_decode(item, &cred->authdata_raw) < 0) {
552
14
                fido_log_debug("%s: fido_blob_decode", __func__);
553
14
                goto fail;
554
14
        }
555
865
556
865
        if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
557
865
            &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
558
376
                fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
559
376
                goto fail;
560
376
        }
561
489
562
489
        r = FIDO_OK;
563
3.97k
fail:
564
3.97k
        if (item != NULL)
565
3.97k
                cbor_decref(&item);
566
3.97k
567
3.97k
        if (r != FIDO_OK)
568
3.97k
                fido_cred_clean_authdata(cred);
569
3.97k
570
3.97k
        return (r);
571
489
572
489
}
573
574
int
575
fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr,
576
    size_t len)
577
3.48k
{
578
3.48k
        cbor_item_t     *item = NULL;
579
3.48k
        int              r = FIDO_ERR_INVALID_ARGUMENT;
580
3.48k
581
3.48k
        fido_cred_clean_authdata(cred);
582
3.48k
583
3.48k
        if (ptr == NULL || len == 0)
584
3.05k
                goto fail;
585
421
586
421
        if (fido_blob_set(&cred->authdata_raw, ptr, len) < 0) {
587
11
                fido_log_debug("%s: fido_blob_set", __func__);
588
11
                r = FIDO_ERR_INTERNAL;
589
11
                goto fail;
590
11
        }
591
410
592
410
        if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
593
11
                fido_log_debug("%s: cbor_build_bytestring", __func__);
594
11
                r = FIDO_ERR_INTERNAL;
595
11
                goto fail;
596
11
        }
597
399
598
399
        if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
599
399
            &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
600
381
                fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
601
381
                goto fail;
602
381
        }
603
18
604
18
        r = FIDO_OK;
605
3.48k
fail:
606
3.48k
        if (item != NULL)
607
3.48k
                cbor_decref(&item);
608
3.48k
609
3.48k
        if (r != FIDO_OK)
610
3.48k
                fido_cred_clean_authdata(cred);
611
3.48k
612
3.48k
        return (r);
613
18
614
18
}
615
616
int
617
fido_cred_set_id(fido_cred_t *cred, const unsigned char *ptr, size_t len)
618
2.57k
{
619
2.57k
        if (fido_blob_set(&cred->attcred.id, ptr, len) < 0)
620
21
                return (FIDO_ERR_INVALID_ARGUMENT);
621
2.55k
622
2.55k
        return (FIDO_OK);
623
2.55k
}
624
625
int
626
fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len)
627
3.96k
{
628
3.96k
        if (fido_blob_set(&cred->attstmt.x5c, ptr, len) < 0)
629
3.74k
                return (FIDO_ERR_INVALID_ARGUMENT);
630
225
631
225
        return (FIDO_OK);
632
225
}
633
634
int
635
fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len)
636
3.96k
{
637
3.96k
        if (fido_blob_set(&cred->attstmt.sig, ptr, len) < 0)
638
3.59k
                return (FIDO_ERR_INVALID_ARGUMENT);
639
373
640
373
        return (FIDO_OK);
641
373
}
642
643
int
644
fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len)
645
76.9k
{
646
76.9k
        fido_blob_t id_blob;
647
76.9k
        fido_blob_t *list_ptr;
648
76.9k
649
76.9k
        memset(&id_blob, 0, sizeof(id_blob));
650
76.9k
651
76.9k
        if (fido_blob_set(&id_blob, id_ptr, id_len) < 0)
652
474
                return (FIDO_ERR_INVALID_ARGUMENT);
653
76.4k
654
76.4k
        if (cred->excl.len == SIZE_MAX) {
655
0
                free(id_blob.ptr);
656
0
                return (FIDO_ERR_INVALID_ARGUMENT);
657
0
        }
658
76.4k
659
76.4k
        if ((list_ptr = recallocarray(cred->excl.ptr, cred->excl.len,
660
76.4k
            cred->excl.len + 1, sizeof(fido_blob_t))) == NULL) {
661
205
                free(id_blob.ptr);
662
205
                return (FIDO_ERR_INTERNAL);
663
205
        }
664
76.2k
665
76.2k
        list_ptr[cred->excl.len++] = id_blob;
666
76.2k
        cred->excl.ptr = list_ptr;
667
76.2k
668
76.2k
        return (FIDO_OK);
669
76.2k
}
670
671
int
672
fido_cred_set_clientdata(fido_cred_t *cred, const unsigned char *data,
673
    size_t data_len)
674
0
{
675
0
        if (!fido_blob_is_empty(&cred->cdh) ||
676
0
            fido_blob_set(&cred->cd, data, data_len) < 0) {
677
0
                return (FIDO_ERR_INVALID_ARGUMENT);
678
0
        }
679
0
        if (fido_sha256(&cred->cdh, data, data_len) < 0) {
680
0
                fido_blob_reset(&cred->cd);
681
0
                return (FIDO_ERR_INTERNAL);
682
0
        }
683
0
684
0
        return (FIDO_OK);
685
0
}
686
687
int
688
fido_cred_set_clientdata_hash(fido_cred_t *cred, const unsigned char *hash,
689
    size_t hash_len)
690
5.60k
{
691
5.60k
        if (!fido_blob_is_empty(&cred->cd) ||
692
5.60k
            fido_blob_set(&cred->cdh, hash, hash_len) < 0)
693
198
                return (FIDO_ERR_INVALID_ARGUMENT);
694
5.40k
695
5.40k
        return (FIDO_OK);
696
5.40k
}
697
698
int
699
fido_cred_set_rp(fido_cred_t *cred, const char *id, const char *name)
700
5.60k
{
701
5.60k
        fido_rp_t *rp = &cred->rp;
702
5.60k
703
5.60k
        if (rp->id != NULL) {
704
1.79k
                free(rp->id);
705
1.79k
                rp->id = NULL;
706
1.79k
        }
707
5.60k
        if (rp->name != NULL) {
708
1.79k
                free(rp->name);
709
1.79k
                rp->name = NULL;
710
1.79k
        }
711
5.60k
712
5.60k
        if (id != NULL && (rp->id = strdup(id)) == NULL)
713
5.60k
                goto fail;
714
5.56k
        if (name != NULL && (rp->name = strdup(name)) == NULL)
715
5.56k
                goto fail;
716
5.54k
717
5.54k
        return (FIDO_OK);
718
53
fail:
719
53
        free(rp->id);
720
53
        free(rp->name);
721
53
        rp->id = NULL;
722
53
        rp->name = NULL;
723
53
724
53
        return (FIDO_ERR_INTERNAL);
725
5.54k
}
726
727
int
728
fido_cred_set_user(fido_cred_t *cred, const unsigned char *user_id,
729
    size_t user_id_len, const char *name, const char *display_name,
730
    const char *icon)
731
4.20k
{
732
4.20k
        fido_user_t *up = &cred->user;
733
4.20k
734
4.20k
        if (up->id.ptr != NULL) {
735
1.78k
                free(up->id.ptr);
736
1.78k
                up->id.ptr = NULL;
737
1.78k
                up->id.len = 0;
738
1.78k
        }
739
4.20k
        if (up->name != NULL) {
740
1.78k
                free(up->name);
741
1.78k
                up->name = NULL;
742
1.78k
        }
743
4.20k
        if (up->display_name != NULL) {
744
1.78k
                free(up->display_name);
745
1.78k
                up->display_name = NULL;
746
1.78k
        }
747
4.20k
        if (up->icon != NULL) {
748
1.78k
                free(up->icon);
749
1.78k
                up->icon = NULL;
750
1.78k
        }
751
4.20k
752
4.20k
        if (user_id != NULL && fido_blob_set(&up->id, user_id, user_id_len) < 0)
753
18
                goto fail;
754
4.18k
        if (name != NULL && (up->name = strdup(name)) == NULL)
755
4.18k
                goto fail;
756
4.17k
        if (display_name != NULL &&
757
4.17k
            (up->display_name = strdup(display_name)) == NULL)
758
4.17k
                goto fail;
759
4.16k
        if (icon != NULL && (up->icon = strdup(icon)) == NULL)
760
4.16k
                goto fail;
761
4.14k
762
4.14k
        return (FIDO_OK);
763
60
fail:
764
60
        free(up->id.ptr);
765
60
        free(up->name);
766
60
        free(up->display_name);
767
60
        free(up->icon);
768
60
769
60
        up->id.ptr = NULL;
770
60
        up->id.len = 0;
771
60
        up->name = NULL;
772
60
        up->display_name = NULL;
773
60
        up->icon = NULL;
774
60
775
60
        return (FIDO_ERR_INTERNAL);
776
4.14k
}
777
778
int
779
fido_cred_set_extensions(fido_cred_t *cred, int ext)
780
2.98k
{
781
2.98k
        if (ext == 0)
782
124
                cred->ext.mask = 0;
783
2.85k
        else {
784
2.85k
                if ((ext & FIDO_EXT_CRED_MASK) != ext)
785
1.81k
                        return (FIDO_ERR_INVALID_ARGUMENT);
786
1.04k
                cred->ext.mask |= ext;
787
1.04k
        }
788
2.98k
789
2.98k
        return (FIDO_OK);
790
2.98k
}
791
792
int
793
fido_cred_set_options(fido_cred_t *cred, bool rk, bool uv)
794
0
{
795
0
        cred->rk = rk ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
796
0
        cred->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
797
0
798
0
        return (FIDO_OK);
799
0
}
800
801
int
802
fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk)
803
1.29k
{
804
1.29k
        cred->rk = rk;
805
1.29k
806
1.29k
        return (FIDO_OK);
807
1.29k
}
808
809
int
810
fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv)
811
1.17k
{
812
1.17k
        cred->uv = uv;
813
1.17k
814
1.17k
        return (FIDO_OK);
815
1.17k
}
816
817
int
818
fido_cred_set_prot(fido_cred_t *cred, int prot)
819
4.28k
{
820
4.28k
        if (prot == 0) {
821
1.94k
                cred->ext.mask &= ~FIDO_EXT_CRED_PROTECT;
822
1.94k
                cred->ext.prot = 0;
823
2.34k
        } else {
824
2.34k
                if (prot != FIDO_CRED_PROT_UV_OPTIONAL &&
825
2.34k
                    prot != FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID &&
826
2.34k
                    prot != FIDO_CRED_PROT_UV_REQUIRED)
827
2.34k
                        return (FIDO_ERR_INVALID_ARGUMENT);
828
2.28k
829
2.28k
                cred->ext.mask |= FIDO_EXT_CRED_PROTECT;
830
2.28k
                cred->ext.prot = prot;
831
2.28k
        }
832
4.28k
833
4.28k
        return (FIDO_OK);
834
4.28k
}
835
836
int
837
fido_cred_set_blob(fido_cred_t *cred, const unsigned char *ptr, size_t len)
838
426
{
839
426
        if (ptr == NULL || len == 0)
840
0
                return (FIDO_ERR_INVALID_ARGUMENT);
841
426
        if (fido_blob_set(&cred->blob, ptr, len) < 0)
842
2
                return (FIDO_ERR_INTERNAL);
843
424
844
424
        cred->ext.mask |= FIDO_EXT_CRED_BLOB;
845
424
846
424
        return (FIDO_OK);
847
424
}
848
849
int
850
fido_cred_set_fmt(fido_cred_t *cred, const char *fmt)
851
485
{
852
485
        free(cred->fmt);
853
485
        cred->fmt = NULL;
854
485
855
485
        if (fmt == NULL)
856
485
                return (FIDO_ERR_INVALID_ARGUMENT);
857
485
858
485
        if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f") &&
859
485
            strcmp(fmt, "none"))
860
0
                return (FIDO_ERR_INVALID_ARGUMENT);
861
485
862
485
        if ((cred->fmt = strdup(fmt)) == NULL)
863
485
                return (FIDO_ERR_INTERNAL);
864
476
865
476
        return (FIDO_OK);
866
476
}
867
868
int
869
fido_cred_set_type(fido_cred_t *cred, int cose_alg)
870
5.60k
{
871
5.60k
        if ((cose_alg != COSE_ES256 && cose_alg != COSE_RS256 &&
872
5.60k
            cose_alg != COSE_EDDSA) || cred->type != 0)
873
1.81k
                return (FIDO_ERR_INVALID_ARGUMENT);
874
3.78k
875
3.78k
        cred->type = cose_alg;
876
3.78k
877
3.78k
        return (FIDO_OK);
878
3.78k
}
879
880
int
881
fido_cred_type(const fido_cred_t *cred)
882
3.19k
{
883
3.19k
        return (cred->type);
884
3.19k
}
885
886
uint8_t
887
fido_cred_flags(const fido_cred_t *cred)
888
1.96k
{
889
1.96k
        return (cred->authdata.flags);
890
1.96k
}
891
892
uint32_t
893
fido_cred_sigcount(const fido_cred_t *cred)
894
1.96k
{
895
1.96k
        return (cred->authdata.sigcount);
896
1.96k
}
897
898
const unsigned char *
899
fido_cred_clientdata_hash_ptr(const fido_cred_t *cred)
900
1.99k
{
901
1.99k
        return (cred->cdh.ptr);
902
1.99k
}
903
904
size_t
905
fido_cred_clientdata_hash_len(const fido_cred_t *cred)
906
1.99k
{
907
1.99k
        return (cred->cdh.len);
908
1.99k
}
909
910
const unsigned char *
911
fido_cred_x5c_ptr(const fido_cred_t *cred)
912
1.99k
{
913
1.99k
        return (cred->attstmt.x5c.ptr);
914
1.99k
}
915
916
size_t
917
fido_cred_x5c_len(const fido_cred_t *cred)
918
1.99k
{
919
1.99k
        return (cred->attstmt.x5c.len);
920
1.99k
}
921
922
const unsigned char *
923
fido_cred_sig_ptr(const fido_cred_t *cred)
924
1.99k
{
925
1.99k
        return (cred->attstmt.sig.ptr);
926
1.99k
}
927
928
size_t
929
fido_cred_sig_len(const fido_cred_t *cred)
930
1.99k
{
931
1.99k
        return (cred->attstmt.sig.len);
932
1.99k
}
933
934
const unsigned char *
935
fido_cred_authdata_ptr(const fido_cred_t *cred)
936
1.99k
{
937
1.99k
        return (cred->authdata_cbor.ptr);
938
1.99k
}
939
940
size_t
941
fido_cred_authdata_len(const fido_cred_t *cred)
942
1.99k
{
943
1.99k
        return (cred->authdata_cbor.len);
944
1.99k
}
945
946
const unsigned char *
947
fido_cred_authdata_raw_ptr(const fido_cred_t *cred)
948
1.99k
{
949
1.99k
        return (cred->authdata_raw.ptr);
950
1.99k
}
951
952
size_t
953
fido_cred_authdata_raw_len(const fido_cred_t *cred)
954
1.99k
{
955
1.99k
        return (cred->authdata_raw.len);
956
1.99k
}
957
958
const unsigned char *
959
fido_cred_pubkey_ptr(const fido_cred_t *cred)
960
3.19k
{
961
3.19k
        const void *ptr;
962
3.19k
963
3.19k
        switch (cred->attcred.type) {
964
715
        case COSE_ES256:
965
715
                ptr = &cred->attcred.pubkey.es256;
966
715
                break;
967
26
        case COSE_RS256:
968
26
                ptr = &cred->attcred.pubkey.rs256;
969
26
                break;
970
111
        case COSE_EDDSA:
971
111
                ptr = &cred->attcred.pubkey.eddsa;
972
111
                break;
973
2.34k
        default:
974
2.34k
                ptr = NULL;
975
2.34k
                break;
976
3.19k
        }
977
3.19k
978
3.19k
        return (ptr);
979
3.19k
}
980
981
size_t
982
fido_cred_pubkey_len(const fido_cred_t *cred)
983
3.19k
{
984
3.19k
        size_t len;
985
3.19k
986
3.19k
        switch (cred->attcred.type) {
987
715
        case COSE_ES256:
988
715
                len = sizeof(cred->attcred.pubkey.es256);
989
715
                break;
990
26
        case COSE_RS256:
991
26
                len = sizeof(cred->attcred.pubkey.rs256);
992
26
                break;
993
111
        case COSE_EDDSA:
994
111
                len = sizeof(cred->attcred.pubkey.eddsa);
995
111
                break;
996
2.34k
        default:
997
2.34k
                len = 0;
998
2.34k
                break;
999
3.19k
        }
1000
3.19k
1001
3.19k
        return (len);
1002
3.19k
}
1003
1004
const unsigned char *
1005
fido_cred_id_ptr(const fido_cred_t *cred)
1006
5.19k
{
1007
5.19k
        return (cred->attcred.id.ptr);
1008
5.19k
}
1009
1010
size_t
1011
fido_cred_id_len(const fido_cred_t *cred)
1012
5.19k
{
1013
5.19k
        return (cred->attcred.id.len);
1014
5.19k
}
1015
1016
const unsigned char *
1017
fido_cred_aaguid_ptr(const fido_cred_t *cred)
1018
1.96k
{
1019
1.96k
        return (cred->attcred.aaguid);
1020
1.96k
}
1021
1022
size_t
1023
fido_cred_aaguid_len(const fido_cred_t *cred)
1024
1.96k
{
1025
1.96k
        return (sizeof(cred->attcred.aaguid));
1026
1.96k
}
1027
1028
int
1029
fido_cred_prot(const fido_cred_t *cred)
1030
3.22k
{
1031
3.22k
        return (cred->ext.prot);
1032
3.22k
}
1033
1034
const char *
1035
fido_cred_fmt(const fido_cred_t *cred)
1036
1.99k
{
1037
1.99k
        return (cred->fmt);
1038
1.99k
}
1039
1040
const char *
1041
fido_cred_rp_id(const fido_cred_t *cred)
1042
1.99k
{
1043
1.99k
        return (cred->rp.id);
1044
1.99k
}
1045
1046
const char *
1047
fido_cred_rp_name(const fido_cred_t *cred)
1048
1.99k
{
1049
1.99k
        return (cred->rp.name);
1050
1.99k
}
1051
1052
const char *
1053
fido_cred_user_name(const fido_cred_t *cred)
1054
3.19k
{
1055
3.19k
        return (cred->user.name);
1056
3.19k
}
1057
1058
const char *
1059
fido_cred_display_name(const fido_cred_t *cred)
1060
3.19k
{
1061
3.19k
        return (cred->user.display_name);
1062
3.19k
}
1063
1064
const unsigned char *
1065
fido_cred_user_id_ptr(const fido_cred_t *cred)
1066
3.19k
{
1067
3.19k
        return (cred->user.id.ptr);
1068
3.19k
}
1069
1070
size_t
1071
fido_cred_user_id_len(const fido_cred_t *cred)
1072
3.19k
{
1073
3.19k
        return (cred->user.id.len);
1074
3.19k
}
1075
1076
const unsigned char *
1077
fido_cred_largeblob_key_ptr(const fido_cred_t *cred)
1078
1.96k
{
1079
1.96k
        return (cred->largeblob_key.ptr);
1080
1.96k
}
1081
1082
size_t
1083
fido_cred_largeblob_key_len(const fido_cred_t *cred)
1084
1.96k
{
1085
1.96k
        return (cred->largeblob_key.len);
1086
1.96k
}