## Coverage Report

#### Created: 2023-09-23 17:42

    /libfido2/src/es384.c

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