Coverage Report

Created: 2025-10-04 18:33

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