Coverage Report

Created: 2026-04-08 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/libfido2/src/ecdh.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/evp.h>
9
#include <openssl/sha.h>
10
#if defined(LIBRESSL_VERSION_NUMBER)
11
#include <openssl/hkdf.h>
12
#else
13
#include <openssl/kdf.h>
14
#endif
15
16
#include "fido.h"
17
#include "fido/es256.h"
18
19
#if defined(LIBRESSL_VERSION_NUMBER)
20
int
21
hkdf_sha256(uint8_t *key, size_t keylen, const char *info,
22
    const fido_blob_t *secret)
23
{
24
        const EVP_MD *md;
25
        uint8_t salt[32];
26
27
        memset(salt, 0, sizeof(salt));
28
        if ((md = EVP_sha256()) == NULL ||
29
            HKDF(key, keylen, md, secret->ptr, secret->len, salt,
30
            sizeof(salt), (const uint8_t *)info, strlen(info)) != 1)
31
                return -1;
32
33
        return 0;
34
}
35
#else
36
int
37
hkdf_sha256(uint8_t *key, size_t keylen, const char *info,
38
    const fido_blob_t *secret)
39
31.8k
{
40
31.8k
        const EVP_MD *md;
41
31.8k
        EVP_PKEY_CTX *ctx = NULL;
42
31.8k
        uint8_t salt[32];
43
31.8k
        int ok = -1;
44
45
31.8k
        memset(salt, 0, sizeof(salt));
46
31.8k
        if (secret->len > INT_MAX || strlen(info) > INT_MAX) {
47
0
                fido_log_debug("%s: invalid param", __func__);
48
0
                goto fail;
49
0
        }
50
31.8k
        if ((md = EVP_sha256()) == NULL ||
51
31.8k
            (ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL)) == NULL) {
52
255
                fido_log_debug("%s: init", __func__);
53
255
                goto fail;
54
255
        }
55
31.5k
        if (EVP_PKEY_derive_init(ctx) < 1 ||
56
31.5k
            EVP_PKEY_CTX_set_hkdf_md(ctx, md) < 1 ||
57
31.5k
            EVP_PKEY_CTX_set1_hkdf_salt(ctx, salt, sizeof(salt)) < 1 ||
58
31.5k
            EVP_PKEY_CTX_set1_hkdf_key(ctx, secret->ptr, (int)secret->len) < 1 ||
59
31.5k
            EVP_PKEY_CTX_add1_hkdf_info(ctx, (const void *)info,
60
31.4k
            (int)strlen(info)) < 1) {
61
183
                fido_log_debug("%s: EVP_PKEY_CTX", __func__);
62
183
                goto fail;
63
183
        }
64
31.4k
        if (EVP_PKEY_derive(ctx, key, &keylen) < 1) {
65
176
                fido_log_debug("%s: EVP_PKEY_derive", __func__);
66
176
                goto fail;
67
176
        }
68
69
31.2k
        ok = 0;
70
31.8k
fail:
71
31.8k
        if (ctx != NULL)
72
31.5k
                EVP_PKEY_CTX_free(ctx);
73
74
31.8k
        return ok;
75
31.2k
}
76
#endif /* defined(LIBRESSL_VERSION_NUMBER) */
77
78
static int
79
kdf(uint8_t prot, fido_blob_t *key, const fido_blob_t *secret)
80
30.0k
{
81
30.0k
        const char hmac_info[] = "CTAP2 HMAC key";
82
30.0k
        const char aes_info[] = "CTAP2 AES key";
83
84
30.0k
        switch (prot) {
85
14.5k
        case CTAP_PIN_PROTOCOL1:
86
                /* use sha256 on the resulting secret */
87
14.5k
                key->len = SHA256_DIGEST_LENGTH;
88
14.5k
                if ((key->ptr = calloc(1, key->len)) == NULL ||
89
14.5k
                    SHA256(secret->ptr, secret->len, key->ptr) != key->ptr) {
90
73
                        fido_log_debug("%s: SHA256", __func__);
91
73
                        return -1;
92
73
                }
93
14.5k
                break;
94
15.4k
        case CTAP_PIN_PROTOCOL2:
95
                /* use two instances of hkdf-sha256 on the resulting secret */
96
15.4k
                key->len = 2 * SHA256_DIGEST_LENGTH;
97
15.4k
                if ((key->ptr = calloc(1, key->len)) == NULL ||
98
15.4k
                    hkdf_sha256(key->ptr, SHA256_DIGEST_LENGTH, hmac_info, secret) < 0 ||
99
15.4k
                    hkdf_sha256(key->ptr + SHA256_DIGEST_LENGTH, SHA256_DIGEST_LENGTH,
100
15.0k
                    aes_info, secret) < 0) {
101
691
                        fido_log_debug("%s: hkdf", __func__);
102
691
                        return -1;
103
691
                }
104
14.7k
                break;
105
14.7k
        default:
106
0
                fido_log_debug("%s: unknown pin protocol %u", __func__, prot);
107
0
                return -1;
108
30.0k
        }
109
110
29.2k
        return 0;
111
30.0k
}
112
113
static int
114
do_ecdh(const fido_dev_t *dev, const es256_sk_t *sk, const es256_pk_t *pk,
115
    fido_blob_t **ecdh)
116
36.3k
{
117
36.3k
        EVP_PKEY *pk_evp = NULL;
118
36.3k
        EVP_PKEY *sk_evp = NULL;
119
36.3k
        EVP_PKEY_CTX *ctx = NULL;
120
36.3k
        fido_blob_t *secret = NULL;
121
36.3k
        int ok = -1;
122
123
36.3k
        *ecdh = NULL;
124
36.3k
        if ((secret = fido_blob_new()) == NULL ||
125
36.3k
            (*ecdh = fido_blob_new()) == NULL)
126
220
                goto fail;
127
36.0k
        if ((pk_evp = es256_pk_to_EVP_PKEY(pk)) == NULL ||
128
36.0k
            (sk_evp = es256_sk_to_EVP_PKEY(sk)) == NULL) {
129
5.59k
                fido_log_debug("%s: es256_to_EVP_PKEY", __func__);
130
5.59k
                goto fail;
131
5.59k
        }
132
30.4k
        if ((ctx = EVP_PKEY_CTX_new(sk_evp, NULL)) == NULL ||
133
30.4k
            EVP_PKEY_derive_init(ctx) <= 0 ||
134
30.4k
            EVP_PKEY_derive_set_peer(ctx, pk_evp) <= 0) {
135
252
                fido_log_debug("%s: EVP_PKEY_derive_init", __func__);
136
252
                goto fail;
137
252
        }
138
30.2k
        if (EVP_PKEY_derive(ctx, NULL, &secret->len) <= 0 ||
139
30.2k
            (secret->ptr = calloc(1, secret->len)) == NULL ||
140
30.2k
            EVP_PKEY_derive(ctx, secret->ptr, &secret->len) <= 0) {
141
203
                fido_log_debug("%s: EVP_PKEY_derive", __func__);
142
203
                goto fail;
143
203
        }
144
30.0k
        if (kdf(fido_dev_get_pin_protocol(dev), *ecdh, secret) < 0) {
145
764
                fido_log_debug("%s: kdf", __func__);
146
764
                goto fail;
147
764
        }
148
149
29.2k
        ok = 0;
150
36.3k
fail:
151
36.3k
        if (pk_evp != NULL)
152
31.0k
                EVP_PKEY_free(pk_evp);
153
36.3k
        if (sk_evp != NULL)
154
30.4k
                EVP_PKEY_free(sk_evp);
155
36.3k
        if (ctx != NULL)
156
30.4k
                EVP_PKEY_CTX_free(ctx);
157
36.3k
        if (ok < 0)
158
7.03k
                fido_blob_free(ecdh);
159
160
36.3k
        fido_blob_free(&secret);
161
162
36.3k
        return ok;
163
29.2k
}
164
165
int
166
fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh, int *ms)
167
117k
{
168
117k
        es256_sk_t *sk = NULL; /* our private key */
169
117k
        es256_pk_t *ak = NULL; /* authenticator's public key */
170
117k
        int r;
171
172
117k
        *pk = NULL;
173
117k
        *ecdh = NULL;
174
117k
        if ((sk = es256_sk_new()) == NULL || (*pk = es256_pk_new()) == NULL) {
175
661
                r = FIDO_ERR_INTERNAL;
176
661
                goto fail;
177
661
        }
178
117k
        if (es256_sk_create(sk) < 0 || es256_derive_pk(sk, *pk) < 0) {
179
7.18k
                fido_log_debug("%s: es256_derive_pk", __func__);
180
7.18k
                r = FIDO_ERR_INTERNAL;
181
7.18k
                goto fail;
182
7.18k
        }
183
109k
        if ((ak = es256_pk_new()) == NULL ||
184
109k
            fido_dev_authkey(dev, ak, ms) != FIDO_OK) {
185
73.6k
                fido_log_debug("%s: fido_dev_authkey", __func__);
186
73.6k
                r = FIDO_ERR_INTERNAL;
187
73.6k
                goto fail;
188
73.6k
        }
189
36.3k
        if (do_ecdh(dev, sk, ak, ecdh) < 0) {
190
7.03k
                fido_log_debug("%s: do_ecdh", __func__);
191
7.03k
                r = FIDO_ERR_INTERNAL;
192
7.03k
                goto fail;
193
7.03k
        }
194
195
29.2k
        r = FIDO_OK;
196
117k
fail:
197
117k
        es256_sk_free(&sk);
198
117k
        es256_pk_free(&ak);
199
200
117k
        if (r != FIDO_OK) {
201
88.4k
                es256_pk_free(pk);
202
88.4k
                fido_blob_free(ecdh);
203
88.4k
        }
204
205
117k
        return r;
206
29.2k
}