Coverage Report

Created: 2025-03-01 02:43

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