## Coverage Report

#### Created: 2025-03-01 02:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /libfido2/src/es384.c

    Line
    Count
    Source
    1
    /*
    2
     * Copyright (c) 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/bn.h>
    9
    #include <openssl/ecdsa.h>
    10
    #include <openssl/obj_mac.h>
    11
    12
    #include "fido.h"
    13
    #include "fido/es384.h"
    14
    15
    #if OPENSSL_VERSION_NUMBER >= 0x30000000
    16
    190
    #define get0_EC_KEY(x)  EVP_PKEY_get0_EC_KEY((x))
    17
    #else
    18
    #define get0_EC_KEY(x)  EVP_PKEY_get0((x))
    19
    #endif
    20
    21
    static int
    22
    decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
    23
    1.36k
    {
    24
    1.36k
            if (cbor_isa_bytestring(item) == false ||
    25
    1.36k
                cbor_bytestring_is_definite(item) == false ||
    26
    1.36k
                cbor_bytestring_length(item) != xy_len) {
    27
    32
                    fido_log_debug("%s: cbor type", __func__);
    28
    32
                    return (-1);
    29
    32
            }
    30
    31
    1.33k
            memcpy(xy, cbor_bytestring_handle(item), xy_len);
    32
    33
    1.33k
            return (0);
    34
    1.36k
    }
    35
    36
    static int
    37
    decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg)
    38
    3.79k
    {
    39
    3.79k
            es384_pk_t *k = arg;
    40
    41
    3.79k
            if (cbor_isa_negint(key) == false ||
    42
    3.79k
                cbor_int_get_width(key) != CBOR_INT_8)
    43
    1.61k
                    return (0); /* ignore */
    44
    45
    2.18k
            switch (cbor_get_uint8(key)) {
    46
    711
            case 1: /* x coordinate */
    47
    711
                    return (decode_coord(val, &k->x, sizeof(k->x)));
    48
    657
            case 2: /* y coordinate */
    49
    657
                    return (decode_coord(val, &k->y, sizeof(k->y)));
    50
    2.18k
            }
    51
    52
    816
            return (0); /* ignore */
    53
    2.18k
    }
    54
    55
    int
    56
    es384_pk_decode(const cbor_item_t *item, es384_pk_t *k)
    57
    770
    {
    58
    770
            if (cbor_isa_map(item) == false ||
    59
    770
                cbor_map_is_definite(item) == false ||
    60
    770
                cbor_map_iter(item, k, decode_pubkey_point) < 0) {
    61
    39
                    fido_log_debug("%s: cbor type", __func__);
    62
    39
                    return (-1);
    63
    39
            }
    64
    65
    731
            return (0);
    66
    770
    }
    67
    68
    es384_pk_t *
    69
    es384_pk_new(void)
    70
    2.30k
    {
    71
    2.30k
            return (calloc(1, sizeof(es384_pk_t)));
    72
    2.30k
    }
    73
    74
    void
    75
    es384_pk_free(es384_pk_t **pkp)
    76
    12.9k
    {
    77
    12.9k
            es384_pk_t *pk;
    78
    79
    12.9k
            if (pkp == NULL || (pk = *pkp) == NULL)
    80
    10.9k
                    return;
    81
    82
    2.01k
            freezero(pk, sizeof(*pk));
    83
    2.01k
            *pkp = NULL;
    84
    2.01k
    }
    85
    86
    int
    87
    es384_pk_from_ptr(es384_pk_t *pk, const void *ptr, size_t len)
    88
    1.82k
    {
    89
    1.82k
            const uint8_t   *p = ptr;
    90
    1.82k
            EVP_PKEY        *pkey;
    91
    92
    1.82k
            if (len < sizeof(*pk))
    93
    899
                    return (FIDO_ERR_INVALID_ARGUMENT);
    94
    95
    926
            if (len == sizeof(*pk) + 1 && *p == 0x04)
    96
    7
                    memcpy(pk, ++p, sizeof(*pk)); /* uncompressed format */
    97
    919
            else
    98
    919
                    memcpy(pk, ptr, sizeof(*pk)); /* libfido2 x||y format */
    99
    100
    926
            if ((pkey = es384_pk_to_EVP_PKEY(pk)) == NULL) {
    101
    390
                    fido_log_debug("%s: es384_pk_to_EVP_PKEY", __func__);
    102
    390
                    explicit_bzero(pk, sizeof(*pk));
    103
    390
                    return (FIDO_ERR_INVALID_ARGUMENT);
    104
    390
            }
    105
    106
    536
            EVP_PKEY_free(pkey);
    107
    108
    536
            return (FIDO_OK);
    109
    926
    }
    110
    111
    EVP_PKEY *
    112
    es384_pk_to_EVP_PKEY(const es384_pk_t *k)
    113
    4.52k
    {
    114
    4.52k
            BN_CTX          *bnctx = NULL;
    115
    4.52k
            EC_KEY          *ec = NULL;
    116
    4.52k
            EC_POINT        *q = NULL;
    117
    4.52k
            EVP_PKEY        *pkey = NULL;
    118
    4.52k
            BIGNUM          *x = NULL;
    119
    4.52k
            BIGNUM          *y = NULL;
    120
    4.52k
            const EC_GROUP  *g = NULL;
    121
    4.52k
            int              ok = -1;
    122
    123
    4.52k
            if ((bnctx = BN_CTX_new()) == NULL)
    124
    34
                    goto fail;
    125
    126
    4.49k
            BN_CTX_start(bnctx);
    127
    128
    4.49k
            if ((x = BN_CTX_get(bnctx)) == NULL ||
    129
    4.49k
                (y = BN_CTX_get(bnctx)) == NULL)
    130
    110
                    goto fail;
    131
    132
    4.38k
            if (BN_bin2bn(k->x, sizeof(k->x), x) == NULL ||
    133
    4.38k
                BN_bin2bn(k->y, sizeof(k->y), y) == NULL) {
    134
    188
                    fido_log_debug("%s: BN_bin2bn", __func__);
    135
    188
                    goto fail;
    136
    188
            }
    137
    138
    4.19k
            if ((ec = EC_KEY_new_by_curve_name(NID_secp384r1)) == NULL ||
    139
    4.19k
                (g = EC_KEY_get0_group(ec)) == NULL) {
    140
    83
                    fido_log_debug("%s: EC_KEY init", __func__);
    141
    83
                    goto fail;
    142
    83
            }
    143
    144
    4.11k
            if ((q = EC_POINT_new(g)) == NULL ||
    145
    4.11k
                EC_POINT_set_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 ||
    146
    4.11k
                EC_KEY_set_public_key(ec, q) == 0) {
    147
    1.62k
                    fido_log_debug("%s: EC_KEY_set_public_key", __func__);
    148
    1.62k
                    goto fail;
    149
    1.62k
            }
    150
    151
    2.49k
            if ((pkey = EVP_PKEY_new()) == NULL ||
    152
    2.49k
                EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) {
    153
    36
                    fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__);
    154
    36
                    goto fail;
    155
    36
            }
    156
    157
    2.45k
            ec = NULL; /* at this point, ec belongs to evp */
    158
    159
    2.45k
            ok = 0;
    160
    4.52k
    fail:
    161
    4.52k
            if (bnctx != NULL) {
    162
    4.49k
                    BN_CTX_end(bnctx);
    163
    4.49k
                    BN_CTX_free(bnctx);
    164
    4.49k
            }
    165
    166
    4.52k
            if (ec != NULL)
    167
    1.68k
                    EC_KEY_free(ec);
    168
    4.52k
            if (q != NULL)
    169
    4.09k
                    EC_POINT_free(q);
    170
    171
    4.52k
            if (ok < 0 && pkey != NULL) {
    172
    14
                    EVP_PKEY_free(pkey);
    173
    14
                    pkey = NULL;
    174
    14
            }
    175
    176
    4.52k
            return (pkey);
    177
    2.45k
    }
    178
    179
    int
    180
    es384_pk_from_EC_KEY(es384_pk_t *pk, const EC_KEY *ec)
    181
    173
    {
    182
    173
            BN_CTX          *bnctx = NULL;
    183
    173
            BIGNUM          *x = NULL;
    184
    173
            BIGNUM          *y = NULL;
    185
    173
            const EC_POINT  *q = NULL;
    186
    173
            EC_GROUP        *g = NULL;
    187
    173
            size_t           dx;
    188
    173
            size_t           dy;
    189
    173
            int              ok = FIDO_ERR_INTERNAL;
    190
    173
            int              nx;
    191
    173
            int              ny;
    192
    193
    173
            if ((q = EC_KEY_get0_public_key(ec)) == NULL ||
    194
    173
                (g = EC_GROUP_new_by_curve_name(NID_secp384r1)) == NULL ||
    195
    173
                (bnctx = BN_CTX_new()) == NULL)
    196
    7
                    goto fail;
    197
    198
    166
            BN_CTX_start(bnctx);
    199
    200
    166
            if ((x = BN_CTX_get(bnctx)) == NULL ||
    201
    166
                (y = BN_CTX_get(bnctx)) == NULL)
    202
    8
                    goto fail;
    203
    204
    158
            if (EC_POINT_is_on_curve(g, q, bnctx) != 1) {
    205
    0
                    fido_log_debug("%s: EC_POINT_is_on_curve", __func__);
    206
    0
                    ok = FIDO_ERR_INVALID_ARGUMENT;
    207
    0
                    goto fail;
    208
    0
            }
    209
    210
    158
            if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 ||
    211
    158
                (nx = BN_num_bytes(x)) < 0 || (size_t)nx > sizeof(pk->x) ||
    212
    158
                (ny = BN_num_bytes(y)) < 0 || (size_t)ny > sizeof(pk->y)) {
    213
    4
                    fido_log_debug("%s: EC_POINT_get_affine_coordinates_GFp",
    214
    4
                        __func__);
    215
    4
                    goto fail;
    216
    4
            }
    217
    218
    154
            dx = sizeof(pk->x) - (size_t)nx;
    219
    154
            dy = sizeof(pk->y) - (size_t)ny;
    220
    221
    154
            if ((nx = BN_bn2bin(x, pk->x + dx)) < 0 || (size_t)nx > sizeof(pk->x) ||
    222
    154
                (ny = BN_bn2bin(y, pk->y + dy)) < 0 || (size_t)ny > sizeof(pk->y)) {
    223
    9
                    fido_log_debug("%s: BN_bn2bin", __func__);
    224
    9
                    goto fail;
    225
    9
            }
    226
    227
    145
            ok = FIDO_OK;
    228
    173
    fail:
    229
    173
            EC_GROUP_free(g);
    230
    231
    173
            if (bnctx != NULL) {
    232
    166
                    BN_CTX_end(bnctx);
    233
    166
                    BN_CTX_free(bnctx);
    234
    166
            }
    235
    236
    173
            return (ok);
    237
    145
    }
    238
    239
    int
    240
    es384_pk_from_EVP_PKEY(es384_pk_t *pk, const EVP_PKEY *pkey)
    241
    190
    {
    242
    190
            const EC_KEY *ec;
    243
    244
    190
            if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC ||
    245
    190
                (ec = get0_EC_KEY(pkey)) == NULL)
    246
    17
                    return (FIDO_ERR_INVALID_ARGUMENT);
    247
    248
    173
            return (es384_pk_from_EC_KEY(pk, ec));
    249
    190
    }
    250
    251
    int
    252
    es384_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,
    253
        const fido_blob_t *sig)
    254
    1.47k
    {
    255
    1.47k
            EVP_PKEY_CTX    *pctx = NULL;
    256
    1.47k
            int              ok = -1;
    257
    258
    1.47k
            if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) {
    259
    0
                    fido_log_debug("%s: EVP_PKEY_base_id", __func__);
    260
    0
                    goto fail;
    261
    0
            }
    262
    263
    1.47k
            if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL ||
    264
    1.47k
                EVP_PKEY_verify_init(pctx) != 1 ||
    265
    1.47k
                EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr,
    266
    1.47k
                dgst->len) != 1) {
    267
    1.47k
                    fido_log_debug("%s: EVP_PKEY_verify", __func__);
    268
    1.47k
                    goto fail;
    269
    1.47k
            }
    270
    271
    0
            ok = 0;
    272
    1.47k
    fail:
    273
    1.47k
            EVP_PKEY_CTX_free(pctx);
    274
    275
    1.47k
            return (ok);
    276
    0
    }
    277
    278
    int
    279
    es384_pk_verify_sig(const fido_blob_t *dgst, const es384_pk_t *pk,
    280
        const fido_blob_t *sig)
    281
    1.77k
    {
    282
    1.77k
            EVP_PKEY        *pkey;
    283
    1.77k
            int              ok = -1;
    284
    285
    1.77k
            if ((pkey = es384_pk_to_EVP_PKEY(pk)) == NULL ||
    286
    1.77k
                es384_verify_sig(dgst, pkey, sig) < 0) {
    287
    1.77k
                    fido_log_debug("%s: es384_verify_sig", __func__);
    288
    1.77k
                    goto fail;
    289
    1.77k
            }
    290
    291
    0
            ok = 0;
    292
    1.77k
    fail:
    293
    1.77k
            EVP_PKEY_free(pkey);
    294
    295
    1.77k
            return (ok);
    296
    0
    }