Coverage Report

Created: 2025-10-04 18:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/libfido2/src/pin.c
Line
Count
Source
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
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#include <openssl/sha.h>
9
#include "fido.h"
10
#include "fido/es256.h"
11
12
34
#define CTAP21_UV_TOKEN_PERM_MAKECRED   0x01
13
33
#define CTAP21_UV_TOKEN_PERM_ASSERT     0x02
14
48
#define CTAP21_UV_TOKEN_PERM_CRED_MGMT  0x04
15
16
#define CTAP21_UV_TOKEN_PERM_BIO        0x08
16
24
#define CTAP21_UV_TOKEN_PERM_LARGEBLOB  0x10
17
183
#define CTAP21_UV_TOKEN_PERM_CONFIG     0x20
18
19
int
20
fido_sha256(fido_blob_t *digest, const u_char *data, size_t data_len)
21
16.0k
{
22
16.0k
        if ((digest->ptr = calloc(1, SHA256_DIGEST_LENGTH)) == NULL)
23
80
                return (-1);
24
25
15.9k
        digest->len = SHA256_DIGEST_LENGTH;
26
27
15.9k
        if (SHA256(data, data_len, digest->ptr) != digest->ptr) {
28
60
                fido_blob_reset(digest);
29
60
                return (-1);
30
60
        }
31
32
15.9k
        return (0);
33
15.9k
}
34
35
static int
36
pin_sha256_enc(const fido_dev_t *dev, const fido_blob_t *shared,
37
    const fido_blob_t *pin, fido_blob_t **out)
38
16.1k
{
39
16.1k
        fido_blob_t     *ph = NULL;
40
16.1k
        int              r;
41
42
16.1k
        if ((*out = fido_blob_new()) == NULL ||
43
16.1k
            (ph = fido_blob_new()) == NULL) {
44
134
                r = FIDO_ERR_INTERNAL;
45
134
                goto fail;
46
134
        }
47
48
16.0k
        if (fido_sha256(ph, pin->ptr, pin->len) < 0 || ph->len < 16) {
49
140
                fido_log_debug("%s: SHA256", __func__);
50
140
                r = FIDO_ERR_INTERNAL;
51
140
                goto fail;
52
140
        }
53
54
15.9k
        ph->len = 16; /* first 16 bytes */
55
56
15.9k
        if (aes256_cbc_enc(dev, shared, ph, *out) < 0) {
57
457
                fido_log_debug("%s: aes256_cbc_enc", __func__);
58
457
                r = FIDO_ERR_INTERNAL;
59
457
                goto fail;
60
457
        }
61
62
15.4k
        r = FIDO_OK;
63
16.1k
fail:
64
16.1k
        fido_blob_free(&ph);
65
66
16.1k
        return (r);
67
15.4k
}
68
69
static int
70
pad64(const char *pin, fido_blob_t **ppin)
71
498
{
72
498
        size_t  pin_len;
73
498
        size_t  ppin_len;
74
75
498
        pin_len = strlen(pin);
76
498
        if (pin_len < 4 || pin_len > 63) {
77
161
                fido_log_debug("%s: invalid pin length", __func__);
78
161
                return (FIDO_ERR_PIN_POLICY_VIOLATION);
79
161
        }
80
81
337
        if ((*ppin = fido_blob_new()) == NULL)
82
4
                return (FIDO_ERR_INTERNAL);
83
84
333
        ppin_len = (pin_len + 63U) & ~63U;
85
333
        if (ppin_len < pin_len ||
86
333
            ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) {
87
2
                fido_blob_free(ppin);
88
2
                return (FIDO_ERR_INTERNAL);
89
2
        }
90
91
331
        memcpy((*ppin)->ptr, pin, pin_len);
92
331
        (*ppin)->len = ppin_len;
93
94
331
        return (FIDO_OK);
95
333
}
96
97
static int
98
pin_pad64_enc(const fido_dev_t *dev, const fido_blob_t *shared,
99
    const char *pin, fido_blob_t **out)
100
498
{
101
498
        fido_blob_t *ppin = NULL;
102
498
        int          r;
103
104
498
        if ((r = pad64(pin, &ppin)) != FIDO_OK) {
105
167
                fido_log_debug("%s: pad64", __func__);
106
167
                    goto fail;
107
167
        }
108
109
331
        if ((*out = fido_blob_new()) == NULL) {
110
4
                r = FIDO_ERR_INTERNAL;
111
4
                goto fail;
112
4
        }
113
114
327
        if (aes256_cbc_enc(dev, shared, ppin, *out) < 0) {
115
27
                fido_log_debug("%s: aes256_cbc_enc", __func__);
116
27
                r = FIDO_ERR_INTERNAL;
117
27
                goto fail;
118
27
        }
119
120
300
        r = FIDO_OK;
121
498
fail:
122
498
        fido_blob_free(&ppin);
123
124
498
        return (r);
125
300
}
126
127
static cbor_item_t *
128
encode_uv_permission(uint8_t cmd)
129
338
{
130
338
        switch (cmd) {
131
33
        case CTAP_CBOR_ASSERT:
132
33
                return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_ASSERT));
133
14
        case CTAP_CBOR_BIO_ENROLL_PRE:
134
16
        case CTAP_CBOR_BIO_ENROLL:
135
16
                return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_BIO));
136
183
        case CTAP_CBOR_CONFIG:
137
183
                return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_CONFIG));
138
34
        case CTAP_CBOR_MAKECRED:
139
34
                return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_MAKECRED));
140
37
        case CTAP_CBOR_CRED_MGMT_PRE:
141
48
        case CTAP_CBOR_CRED_MGMT:
142
48
                return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_CRED_MGMT));
143
24
        case CTAP_CBOR_LARGEBLOB:
144
24
                return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_LARGEBLOB));
145
0
        default:
146
0
                fido_log_debug("%s: cmd 0x%02x", __func__, cmd);
147
0
                return (NULL);
148
338
        }
149
338
}
150
151
static int
152
ctap20_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh,
153
    const es256_pk_t *pk, int *ms)
154
16.1k
{
155
16.1k
        fido_blob_t      f;
156
16.1k
        fido_blob_t     *p = NULL;
157
16.1k
        fido_blob_t     *phe = NULL;
158
16.1k
        cbor_item_t     *argv[6];
159
16.1k
        int              r;
160
161
16.1k
        memset(&f, 0, sizeof(f));
162
16.1k
        memset(argv, 0, sizeof(argv));
163
164
16.1k
        if (pin == NULL) {
165
33
                fido_log_debug("%s: NULL pin", __func__);
166
33
                r = FIDO_ERR_PIN_REQUIRED;
167
33
                goto fail;
168
33
        }
169
170
16.1k
        if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
171
16.0k
            (const unsigned char *)pin, strlen(pin)) < 0) {
172
475
                fido_log_debug("%s: fido_blob_set", __func__);
173
475
                r = FIDO_ERR_INVALID_ARGUMENT;
174
475
                goto fail;
175
475
        }
176
177
15.6k
        if ((r = pin_sha256_enc(dev, ecdh, p, &phe)) != FIDO_OK) {
178
647
                fido_log_debug("%s: pin_sha256_enc", __func__);
179
647
                goto fail;
180
647
        }
181
182
14.9k
        if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
183
14.9k
            (argv[1] = cbor_build_uint8(5)) == NULL ||
184
14.9k
            (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
185
14.9k
            (argv[5] = fido_blob_encode(phe)) == NULL) {
186
1.49k
                fido_log_debug("%s: cbor encode", __func__);
187
1.49k
                r = FIDO_ERR_INTERNAL;
188
1.49k
                goto fail;
189
1.49k
        }
190
191
13.4k
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
192
13.4k
            &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
193
313
                fido_log_debug("%s: fido_tx", __func__);
194
313
                r = FIDO_ERR_TX;
195
313
                goto fail;
196
313
        }
197
198
13.1k
        r = FIDO_OK;
199
16.1k
fail:
200
16.1k
        cbor_vector_free(argv, nitems(argv));
201
16.1k
        fido_blob_free(&p);
202
16.1k
        fido_blob_free(&phe);
203
16.1k
        free(f.ptr);
204
205
16.1k
        return (r);
206
13.1k
}
207
208
static int
209
ctap21_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh,
210
    const es256_pk_t *pk, uint8_t cmd, const char *rpid, int *ms)
211
679
{
212
679
        fido_blob_t      f;
213
679
        fido_blob_t     *p = NULL;
214
679
        fido_blob_t     *phe = NULL;
215
679
        cbor_item_t     *argv[10];
216
679
        uint8_t          subcmd;
217
679
        int              r;
218
219
679
        memset(&f, 0, sizeof(f));
220
679
        memset(argv, 0, sizeof(argv));
221
222
679
        if (pin != NULL) {
223
470
                if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
224
434
                    (const unsigned char *)pin, strlen(pin)) < 0) {
225
69
                        fido_log_debug("%s: fido_blob_set", __func__);
226
69
                        r = FIDO_ERR_INVALID_ARGUMENT;
227
69
                        goto fail;
228
69
                }
229
401
                if ((r = pin_sha256_enc(dev, ecdh, p, &phe)) != FIDO_OK) {
230
77
                        fido_log_debug("%s: pin_sha256_enc", __func__);
231
77
                        goto fail;
232
77
                }
233
324
                subcmd = 9; /* getPinUvAuthTokenUsingPinWithPermissions */
234
324
        } else {
235
209
                if (fido_dev_has_uv(dev) == false) {
236
16
                        fido_log_debug("%s: fido_dev_has_uv", __func__);
237
16
                        r = FIDO_ERR_PIN_REQUIRED;
238
16
                        goto fail;
239
16
                }
240
193
                subcmd = 6; /* getPinUvAuthTokenUsingUvWithPermissions */
241
193
        }
242
243
517
        if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
244
517
            (argv[1] = cbor_build_uint8(subcmd)) == NULL ||
245
517
            (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
246
517
            (phe != NULL && (argv[5] = fido_blob_encode(phe)) == NULL) ||
247
517
            (argv[8] = encode_uv_permission(cmd)) == NULL ||
248
517
            (rpid != NULL && (argv[9] = cbor_build_string(rpid)) == NULL)) {
249
219
                fido_log_debug("%s: cbor encode", __func__);
250
219
                r = FIDO_ERR_INTERNAL;
251
219
                goto fail;
252
219
        }
253
254
298
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
255
298
            &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
256
115
                fido_log_debug("%s:  fido_tx", __func__);
257
115
                r = FIDO_ERR_TX;
258
115
                goto fail;
259
115
        }
260
261
183
        r = FIDO_OK;
262
679
fail:
263
679
        cbor_vector_free(argv, nitems(argv));
264
679
        fido_blob_free(&p);
265
679
        fido_blob_free(&phe);
266
679
        free(f.ptr);
267
268
679
        return (r);
269
183
}
270
271
static int
272
parse_uv_token(const cbor_item_t *key, const cbor_item_t *val, void *arg)
273
14.2k
{
274
14.2k
        fido_blob_t *token = arg;
275
276
14.2k
        if (cbor_isa_uint(key) == false ||
277
14.2k
            cbor_int_get_width(key) != CBOR_INT_8 ||
278
14.2k
            cbor_get_uint8(key) != 2) {
279
2.50k
                fido_log_debug("%s: cbor type", __func__);
280
2.50k
                return (0); /* ignore */
281
2.50k
        }
282
283
11.7k
        return (fido_blob_decode(val, token));
284
14.2k
}
285
286
static int
287
uv_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh, fido_blob_t *token,
288
    int *ms)
289
13.3k
{
290
13.3k
        fido_blob_t     *aes_token = NULL;
291
13.3k
        unsigned char   *msg = NULL;
292
13.3k
        int              msglen;
293
13.3k
        int              r;
294
295
13.3k
        if ((aes_token = fido_blob_new()) == NULL) {
296
89
                r = FIDO_ERR_INTERNAL;
297
89
                goto fail;
298
89
        }
299
300
13.2k
        if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
301
48
                r = FIDO_ERR_INTERNAL;
302
48
                goto fail;
303
48
        }
304
305
13.2k
        if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
306
621
                fido_log_debug("%s: fido_rx", __func__);
307
621
                r = FIDO_ERR_RX;
308
621
                goto fail;
309
621
        }
310
311
12.6k
        if ((r = cbor_parse_reply(msg, (size_t)msglen, aes_token,
312
12.6k
            parse_uv_token)) != FIDO_OK) {
313
761
                fido_log_debug("%s: parse_uv_token", __func__);
314
761
                goto fail;
315
761
        }
316
317
11.8k
        if  (aes256_cbc_dec(dev, ecdh, aes_token, token) < 0) {
318
857
                fido_log_debug("%s: aes256_cbc_dec", __func__);
319
857
                r = FIDO_ERR_RX;
320
857
                goto fail;
321
857
        }
322
323
10.9k
        r = FIDO_OK;
324
13.3k
fail:
325
13.3k
        fido_blob_free(&aes_token);
326
13.3k
        freezero(msg, FIDO_MAXMSG);
327
328
13.3k
        return (r);
329
10.9k
}
330
331
static int
332
uv_token_wait(fido_dev_t *dev, uint8_t cmd, const char *pin,
333
    const fido_blob_t *ecdh, const es256_pk_t *pk, const char *rpid,
334
    fido_blob_t *token, int *ms)
335
16.8k
{
336
16.8k
        int r;
337
338
16.8k
        if (ecdh == NULL || pk == NULL)
339
0
                return (FIDO_ERR_INVALID_ARGUMENT);
340
16.8k
        if (fido_dev_supports_permissions(dev))
341
679
                r = ctap21_uv_token_tx(dev, pin, ecdh, pk, cmd, rpid, ms);
342
16.1k
        else
343
16.1k
                r = ctap20_uv_token_tx(dev, pin, ecdh, pk, ms);
344
16.8k
        if (r != FIDO_OK)
345
3.45k
                return (r);
346
347
13.3k
        return (uv_token_rx(dev, ecdh, token, ms));
348
16.8k
}
349
350
int
351
fido_dev_get_uv_token(fido_dev_t *dev, uint8_t cmd, const char *pin,
352
    const fido_blob_t *ecdh, const es256_pk_t *pk, const char *rpid,
353
    fido_blob_t *token, int *ms)
354
16.8k
{
355
16.8k
        return (uv_token_wait(dev, cmd, pin, ecdh, pk, rpid, token, ms));
356
16.8k
}
357
358
static int
359
fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin,
360
    int *ms)
361
1.30k
{
362
1.30k
        fido_blob_t      f;
363
1.30k
        fido_blob_t     *ppine = NULL;
364
1.30k
        fido_blob_t     *ecdh = NULL;
365
1.30k
        fido_blob_t     *opin = NULL;
366
1.30k
        fido_blob_t     *opinhe = NULL;
367
1.30k
        cbor_item_t     *argv[6];
368
1.30k
        es256_pk_t      *pk = NULL;
369
1.30k
        int r;
370
371
1.30k
        memset(&f, 0, sizeof(f));
372
1.30k
        memset(argv, 0, sizeof(argv));
373
374
1.30k
        if ((opin = fido_blob_new()) == NULL || fido_blob_set(opin,
375
1.29k
            (const unsigned char *)oldpin, strlen(oldpin)) < 0) {
376
273
                fido_log_debug("%s: fido_blob_set", __func__);
377
273
                r = FIDO_ERR_INVALID_ARGUMENT;
378
273
                goto fail;
379
273
        }
380
381
1.03k
        if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
382
816
                fido_log_debug("%s: fido_do_ecdh", __func__);
383
816
                goto fail;
384
816
        }
385
386
        /* pad and encrypt new pin */
387
217
        if ((r = pin_pad64_enc(dev, ecdh, pin, &ppine)) != FIDO_OK) {
388
66
                fido_log_debug("%s: pin_pad64_enc", __func__);
389
66
                goto fail;
390
66
        }
391
392
        /* hash and encrypt old pin */
393
151
        if ((r = pin_sha256_enc(dev, ecdh, opin, &opinhe)) != FIDO_OK) {
394
7
                fido_log_debug("%s: pin_sha256_enc", __func__);
395
7
                goto fail;
396
7
        }
397
398
144
        if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
399
144
            (argv[1] = cbor_build_uint8(4)) == NULL ||
400
144
            (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
401
144
            (argv[3] = cbor_encode_change_pin_auth(dev, ecdh, ppine, opinhe)) == NULL ||
402
144
            (argv[4] = fido_blob_encode(ppine)) == NULL ||
403
144
            (argv[5] = fido_blob_encode(opinhe)) == NULL) {
404
62
                fido_log_debug("%s: cbor encode", __func__);
405
62
                r = FIDO_ERR_INTERNAL;
406
62
                goto fail;
407
62
        }
408
409
82
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
410
82
            &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
411
10
                fido_log_debug("%s: fido_tx", __func__);
412
10
                r = FIDO_ERR_TX;
413
10
                goto fail;
414
10
        }
415
416
72
        r = FIDO_OK;
417
1.30k
fail:
418
1.30k
        cbor_vector_free(argv, nitems(argv));
419
1.30k
        es256_pk_free(&pk);
420
1.30k
        fido_blob_free(&ppine);
421
1.30k
        fido_blob_free(&ecdh);
422
1.30k
        fido_blob_free(&opin);
423
1.30k
        fido_blob_free(&opinhe);
424
1.30k
        free(f.ptr);
425
426
1.30k
        return (r);
427
428
72
}
429
430
static int
431
fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin, int *ms)
432
1.55k
{
433
1.55k
        fido_blob_t      f;
434
1.55k
        fido_blob_t     *ppine = NULL;
435
1.55k
        fido_blob_t     *ecdh = NULL;
436
1.55k
        cbor_item_t     *argv[5];
437
1.55k
        es256_pk_t      *pk = NULL;
438
1.55k
        int              r;
439
440
1.55k
        memset(&f, 0, sizeof(f));
441
1.55k
        memset(argv, 0, sizeof(argv));
442
443
1.55k
        if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
444
1.27k
                fido_log_debug("%s: fido_do_ecdh", __func__);
445
1.27k
                goto fail;
446
1.27k
        }
447
448
281
        if ((r = pin_pad64_enc(dev, ecdh, pin, &ppine)) != FIDO_OK) {
449
132
                fido_log_debug("%s: pin_pad64_enc", __func__);
450
132
                goto fail;
451
132
        }
452
453
149
        if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
454
149
            (argv[1] = cbor_build_uint8(3)) == NULL ||
455
149
            (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
456
149
            (argv[3] = cbor_encode_pin_auth(dev, ecdh, ppine)) == NULL ||
457
149
            (argv[4] = fido_blob_encode(ppine)) == NULL) {
458
55
                fido_log_debug("%s: cbor encode", __func__);
459
55
                r = FIDO_ERR_INTERNAL;
460
55
                goto fail;
461
55
        }
462
463
94
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
464
94
            &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
465
17
                fido_log_debug("%s: fido_tx", __func__);
466
17
                r = FIDO_ERR_TX;
467
17
                goto fail;
468
17
        }
469
470
77
        r = FIDO_OK;
471
1.55k
fail:
472
1.55k
        cbor_vector_free(argv, nitems(argv));
473
1.55k
        es256_pk_free(&pk);
474
1.55k
        fido_blob_free(&ppine);
475
1.55k
        fido_blob_free(&ecdh);
476
1.55k
        free(f.ptr);
477
478
1.55k
        return (r);
479
77
}
480
481
static int
482
fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
483
    int *ms)
484
2.86k
{
485
2.86k
        int r;
486
487
2.86k
        if (oldpin != NULL) {
488
1.30k
                if ((r = fido_dev_change_pin_tx(dev, pin, oldpin,
489
1.30k
                    ms)) != FIDO_OK) {
490
1.23k
                        fido_log_debug("%s: fido_dev_change_pin_tx", __func__);
491
1.23k
                        return (r);
492
1.23k
                }
493
1.55k
        } else {
494
1.55k
                if ((r = fido_dev_set_pin_tx(dev, pin, ms)) != FIDO_OK) {
495
1.47k
                        fido_log_debug("%s: fido_dev_set_pin_tx", __func__);
496
1.47k
                        return (r);
497
1.47k
                }
498
1.55k
        }
499
500
149
        if ((r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
501
111
                fido_log_debug("%s: fido_rx_cbor_status", __func__);
502
111
                return (r);
503
111
        }
504
505
38
        if (dev->flags & FIDO_DEV_PIN_UNSET) {
506
25
                dev->flags &= ~FIDO_DEV_PIN_UNSET;
507
25
                dev->flags |= FIDO_DEV_PIN_SET;
508
25
        }
509
510
38
        return (FIDO_OK);
511
149
}
512
513
int
514
fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin)
515
2.86k
{
516
2.86k
        int ms = dev->timeout_ms;
517
518
2.86k
        return (fido_dev_set_pin_wait(dev, pin, oldpin, &ms));
519
2.86k
}
520
521
static int
522
parse_retry_count(const uint8_t keyval, const cbor_item_t *key,
523
    const cbor_item_t *val, void *arg)
524
2.62k
{
525
2.62k
        int             *retries = arg;
526
2.62k
        uint64_t         n;
527
528
2.62k
        if (cbor_isa_uint(key) == false ||
529
2.62k
            cbor_int_get_width(key) != CBOR_INT_8 ||
530
2.62k
            cbor_get_uint8(key) != keyval) {
531
2.32k
                fido_log_debug("%s: cbor type", __func__);
532
2.32k
                return (0); /* ignore */
533
2.32k
        }
534
535
305
        if (cbor_decode_uint64(val, &n) < 0 || n > INT_MAX) {
536
161
                fido_log_debug("%s: cbor_decode_uint64", __func__);
537
161
                return (-1);
538
161
        }
539
540
144
        *retries = (int)n;
541
542
144
        return (0);
543
305
}
544
545
static int
546
parse_pin_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
547
1.23k
{
548
1.23k
        return (parse_retry_count(3, key, val, arg));
549
1.23k
}
550
551
static int
552
parse_uv_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
553
1.38k
{
554
1.38k
        return (parse_retry_count(5, key, val, arg));
555
1.38k
}
556
557
static int
558
fido_dev_get_retry_count_tx(fido_dev_t *dev, uint8_t subcmd, int *ms)
559
6.15k
{
560
6.15k
        fido_blob_t      f;
561
6.15k
        cbor_item_t     *argv[2];
562
6.15k
        int              r;
563
564
6.15k
        memset(&f, 0, sizeof(f));
565
6.15k
        memset(argv, 0, sizeof(argv));
566
567
6.15k
        if ((argv[0] = cbor_build_uint8(1)) == NULL ||
568
6.15k
            (argv[1] = cbor_build_uint8(subcmd)) == NULL) {
569
77
                r = FIDO_ERR_INTERNAL;
570
77
                goto fail;
571
77
        }
572
573
6.07k
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
574
6.07k
            &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
575
206
                fido_log_debug("%s: fido_tx", __func__);
576
206
                r = FIDO_ERR_TX;
577
206
                goto fail;
578
206
        }
579
580
5.87k
        r = FIDO_OK;
581
6.15k
fail:
582
6.15k
        cbor_vector_free(argv, nitems(argv));
583
6.15k
        free(f.ptr);
584
585
6.15k
        return (r);
586
5.87k
}
587
588
static int
589
fido_dev_get_pin_retry_count_rx(fido_dev_t *dev, int *retries, int *ms)
590
3.00k
{
591
3.00k
        unsigned char   *msg;
592
3.00k
        int              msglen;
593
3.00k
        int              r;
594
595
3.00k
        *retries = 0;
596
597
3.00k
        if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
598
4
                r = FIDO_ERR_INTERNAL;
599
4
                goto fail;
600
4
        }
601
602
2.99k
        if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
603
1.87k
                fido_log_debug("%s: fido_rx", __func__);
604
1.87k
                r = FIDO_ERR_RX;
605
1.87k
                goto fail;
606
1.87k
        }
607
608
1.12k
        if ((r = cbor_parse_reply(msg, (size_t)msglen, retries,
609
1.12k
            parse_pin_retry_count)) != FIDO_OK) {
610
982
                fido_log_debug("%s: parse_pin_retry_count", __func__);
611
982
                goto fail;
612
982
        }
613
614
140
        r = FIDO_OK;
615
3.00k
fail:
616
3.00k
        freezero(msg, FIDO_MAXMSG);
617
618
3.00k
        return (r);
619
140
}
620
621
static int
622
fido_dev_get_pin_retry_count_wait(fido_dev_t *dev, int *retries, int *ms)
623
3.13k
{
624
3.13k
        int r;
625
626
3.13k
        if ((r = fido_dev_get_retry_count_tx(dev, 1, ms)) != FIDO_OK ||
627
3.13k
            (r = fido_dev_get_pin_retry_count_rx(dev, retries, ms)) != FIDO_OK)
628
2.99k
                return (r);
629
630
140
        return (FIDO_OK);
631
3.13k
}
632
633
int
634
fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
635
3.13k
{
636
3.13k
        int ms = dev->timeout_ms;
637
638
3.13k
        return (fido_dev_get_pin_retry_count_wait(dev, retries, &ms));
639
3.13k
}
640
641
static int
642
fido_dev_get_uv_retry_count_rx(fido_dev_t *dev, int *retries, int *ms)
643
2.87k
{
644
2.87k
        unsigned char   *msg;
645
2.87k
        int              msglen;
646
2.87k
        int              r;
647
648
2.87k
        *retries = 0;
649
650
2.87k
        if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
651
8
                r = FIDO_ERR_INTERNAL;
652
8
                goto fail;
653
8
        }
654
655
2.86k
        if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
656
1.93k
                fido_log_debug("%s: fido_rx", __func__);
657
1.93k
                r = FIDO_ERR_RX;
658
1.93k
                goto fail;
659
1.93k
        }
660
661
929
        if ((r = cbor_parse_reply(msg, (size_t)msglen, retries,
662
929
            parse_uv_retry_count)) != FIDO_OK) {
663
877
                fido_log_debug("%s: parse_uv_retry_count", __func__);
664
877
                goto fail;
665
877
        }
666
667
52
        r = FIDO_OK;
668
2.87k
fail:
669
2.87k
        freezero(msg, FIDO_MAXMSG);
670
671
2.87k
        return (r);
672
52
}
673
674
static int
675
fido_dev_get_uv_retry_count_wait(fido_dev_t *dev, int *retries, int *ms)
676
3.01k
{
677
3.01k
        int r;
678
679
3.01k
        if ((r = fido_dev_get_retry_count_tx(dev, 7, ms)) != FIDO_OK ||
680
3.01k
            (r = fido_dev_get_uv_retry_count_rx(dev, retries, ms)) != FIDO_OK)
681
2.96k
                return (r);
682
683
52
        return (FIDO_OK);
684
3.01k
}
685
686
int
687
fido_dev_get_uv_retry_count(fido_dev_t *dev, int *retries)
688
3.01k
{
689
3.01k
        int ms = dev->timeout_ms;
690
691
3.01k
        return (fido_dev_get_uv_retry_count_wait(dev, retries, &ms));
692
3.01k
}
693
694
int
695
cbor_add_uv_params(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *hmac_data,
696
    const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin,
697
    const char *rpid, cbor_item_t **auth, cbor_item_t **opt, int *ms)
698
14.2k
{
699
14.2k
        fido_blob_t     *token = NULL;
700
14.2k
        int              r;
701
702
14.2k
        if ((token = fido_blob_new()) == NULL) {
703
52
                r = FIDO_ERR_INTERNAL;
704
52
                goto fail;
705
52
        }
706
707
14.1k
        if ((r = fido_dev_get_uv_token(dev, cmd, pin, ecdh, pk, rpid,
708
14.1k
            token, ms)) != FIDO_OK) {
709
4.59k
                fido_log_debug("%s: fido_dev_get_uv_token", __func__);
710
4.59k
                goto fail;
711
4.59k
        }
712
713
9.56k
        if ((*auth = cbor_encode_pin_auth(dev, token, hmac_data)) == NULL ||
714
9.56k
            (*opt = cbor_encode_pin_opt(dev)) == NULL) {
715
108
                fido_log_debug("%s: cbor encode", __func__);
716
108
                r = FIDO_ERR_INTERNAL;
717
108
                goto fail;
718
108
        }
719
720
9.45k
        r = FIDO_OK;
721
14.2k
fail:
722
14.2k
        fido_blob_free(&token);
723
724
14.2k
        return (r);
725
9.45k
}