Coverage Report

Created: 2026-04-08 06:32

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