## 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
    }