Coverage Report

Created: 2025-10-04 18:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/libfido2/src/ecdh.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2018-2021 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
static int
21
hkdf_sha256(uint8_t *key, const char *info, const fido_blob_t *secret)
22
{
23
        const EVP_MD *md;
24
        uint8_t salt[32];
25
26
        memset(salt, 0, sizeof(salt));
27
        if ((md = EVP_sha256()) == NULL ||
28
            HKDF(key, SHA256_DIGEST_LENGTH, md, secret->ptr, secret->len, salt,
29
            sizeof(salt), (const uint8_t *)info, strlen(info)) != 1)
30
                return -1;
31
32
        return 0;
33
}
34
#else
35
static int
36
hkdf_sha256(uint8_t *key, char *info, fido_blob_t *secret)
37
5.62k
{
38
5.62k
        const EVP_MD *const_md;
39
5.62k
        EVP_MD *md = NULL;
40
5.62k
        EVP_PKEY_CTX *ctx = NULL;
41
5.62k
        size_t keylen = SHA256_DIGEST_LENGTH;
42
5.62k
        uint8_t salt[32];
43
5.62k
        int ok = -1;
44
45
5.62k
        memset(salt, 0, sizeof(salt));
46
5.62k
        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
5.62k
        if ((const_md = EVP_sha256()) == NULL ||
51
5.62k
            (md = EVP_MD_meth_dup(const_md)) == NULL ||
52
5.62k
            (ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL)) == NULL) {
53
161
                fido_log_debug("%s: init", __func__);
54
161
                goto fail;
55
161
        }
56
5.45k
        if (EVP_PKEY_derive_init(ctx) < 1 ||
57
5.45k
            EVP_PKEY_CTX_set_hkdf_md(ctx, md) < 1 ||
58
5.45k
            EVP_PKEY_CTX_set1_hkdf_salt(ctx, salt, sizeof(salt)) < 1 ||
59
5.45k
            EVP_PKEY_CTX_set1_hkdf_key(ctx, secret->ptr, (int)secret->len) < 1 ||
60
5.45k
            EVP_PKEY_CTX_add1_hkdf_info(ctx, (void *)info, (int)strlen(info)) < 1) {
61
96
                fido_log_debug("%s: EVP_PKEY_CTX", __func__);
62
96
                goto fail;
63
96
        }
64
5.36k
        if (EVP_PKEY_derive(ctx, key, &keylen) < 1) {
65
130
                fido_log_debug("%s: EVP_PKEY_derive", __func__);
66
130
                goto fail;
67
130
        }
68
69
5.23k
        ok = 0;
70
5.62k
fail:
71
5.62k
        if (md != NULL)
72
5.54k
                EVP_MD_meth_free(md);
73
5.62k
        if (ctx != NULL)
74
5.45k
                EVP_PKEY_CTX_free(ctx);
75
76
5.62k
        return ok;
77
5.23k
}
78
#endif /* defined(LIBRESSL_VERSION_NUMBER) */
79
80
static int
81
kdf(uint8_t prot, fido_blob_t *key, /* const */ fido_blob_t *secret)
82
18.7k
{
83
18.7k
        char hmac_info[] = "CTAP2 HMAC key"; /* const */
84
18.7k
        char aes_info[] = "CTAP2 AES key"; /* const */
85
86
18.7k
        switch (prot) {
87
15.7k
        case CTAP_PIN_PROTOCOL1:
88
                /* use sha256 on the resulting secret */
89
15.7k
                key->len = SHA256_DIGEST_LENGTH;
90
15.7k
                if ((key->ptr = calloc(1, key->len)) == NULL ||
91
15.7k
                    SHA256(secret->ptr, secret->len, key->ptr) != key->ptr) {
92
127
                        fido_log_debug("%s: SHA256", __func__);
93
127
                        return -1;
94
127
                }
95
15.5k
                break;
96
15.5k
        case CTAP_PIN_PROTOCOL2:
97
                /* use two instances of hkdf-sha256 on the resulting secret */
98
2.99k
                key->len = 2 * SHA256_DIGEST_LENGTH;
99
2.99k
                if ((key->ptr = calloc(1, key->len)) == NULL ||
100
2.99k
                    hkdf_sha256(key->ptr, hmac_info, secret) < 0 ||
101
2.99k
                    hkdf_sha256(key->ptr + SHA256_DIGEST_LENGTH, aes_info,
102
2.72k
                    secret) < 0) {
103
480
                        fido_log_debug("%s: hkdf", __func__);
104
480
                        return -1;
105
480
                }
106
2.51k
                break;
107
2.51k
        default:
108
0
                fido_log_debug("%s: unknown pin protocol %u", __func__, prot);
109
0
                return -1;
110
18.7k
        }
111
112
18.1k
        return 0;
113
18.7k
}
114
115
static int
116
do_ecdh(const fido_dev_t *dev, const es256_sk_t *sk, const es256_pk_t *pk,
117
    fido_blob_t **ecdh)
118
23.3k
{
119
23.3k
        EVP_PKEY *pk_evp = NULL;
120
23.3k
        EVP_PKEY *sk_evp = NULL;
121
23.3k
        EVP_PKEY_CTX *ctx = NULL;
122
23.3k
        fido_blob_t *secret = NULL;
123
23.3k
        int ok = -1;
124
125
23.3k
        *ecdh = NULL;
126
23.3k
        if ((secret = fido_blob_new()) == NULL ||
127
23.3k
            (*ecdh = fido_blob_new()) == NULL)
128
118
                goto fail;
129
23.2k
        if ((pk_evp = es256_pk_to_EVP_PKEY(pk)) == NULL ||
130
23.2k
            (sk_evp = es256_sk_to_EVP_PKEY(sk)) == NULL) {
131
3.97k
                fido_log_debug("%s: es256_to_EVP_PKEY", __func__);
132
3.97k
                goto fail;
133
3.97k
        }
134
19.2k
        if ((ctx = EVP_PKEY_CTX_new(sk_evp, NULL)) == NULL ||
135
19.2k
            EVP_PKEY_derive_init(ctx) <= 0 ||
136
19.2k
            EVP_PKEY_derive_set_peer(ctx, pk_evp) <= 0) {
137
308
                fido_log_debug("%s: EVP_PKEY_derive_init", __func__);
138
308
                goto fail;
139
308
        }
140
18.9k
        if (EVP_PKEY_derive(ctx, NULL, &secret->len) <= 0 ||
141
18.9k
            (secret->ptr = calloc(1, secret->len)) == NULL ||
142
18.9k
            EVP_PKEY_derive(ctx, secret->ptr, &secret->len) <= 0) {
143
269
                fido_log_debug("%s: EVP_PKEY_derive", __func__);
144
269
                goto fail;
145
269
        }
146
18.7k
        if (kdf(fido_dev_get_pin_protocol(dev), *ecdh, secret) < 0) {
147
607
                fido_log_debug("%s: kdf", __func__);
148
607
                goto fail;
149
607
        }
150
151
18.1k
        ok = 0;
152
23.3k
fail:
153
23.3k
        if (pk_evp != NULL)
154
19.7k
                EVP_PKEY_free(pk_evp);
155
23.3k
        if (sk_evp != NULL)
156
19.2k
                EVP_PKEY_free(sk_evp);
157
23.3k
        if (ctx != NULL)
158
19.2k
                EVP_PKEY_CTX_free(ctx);
159
23.3k
        if (ok < 0)
160
5.27k
                fido_blob_free(ecdh);
161
162
23.3k
        fido_blob_free(&secret);
163
164
23.3k
        return ok;
165
18.1k
}
166
167
int
168
fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh, int *ms)
169
50.0k
{
170
50.0k
        es256_sk_t *sk = NULL; /* our private key */
171
50.0k
        es256_pk_t *ak = NULL; /* authenticator's public key */
172
50.0k
        int r;
173
174
50.0k
        *pk = NULL;
175
50.0k
        *ecdh = NULL;
176
50.0k
        if ((sk = es256_sk_new()) == NULL || (*pk = es256_pk_new()) == NULL) {
177
268
                r = FIDO_ERR_INTERNAL;
178
268
                goto fail;
179
268
        }
180
49.7k
        if (es256_sk_create(sk) < 0 || es256_derive_pk(sk, *pk) < 0) {
181
5.60k
                fido_log_debug("%s: es256_derive_pk", __func__);
182
5.60k
                r = FIDO_ERR_INTERNAL;
183
5.60k
                goto fail;
184
5.60k
        }
185
44.1k
        if ((ak = es256_pk_new()) == NULL ||
186
44.1k
            fido_dev_authkey(dev, ak, ms) != FIDO_OK) {
187
20.8k
                fido_log_debug("%s: fido_dev_authkey", __func__);
188
20.8k
                r = FIDO_ERR_INTERNAL;
189
20.8k
                goto fail;
190
20.8k
        }
191
23.3k
        if (do_ecdh(dev, sk, ak, ecdh) < 0) {
192
5.27k
                fido_log_debug("%s: do_ecdh", __func__);
193
5.27k
                r = FIDO_ERR_INTERNAL;
194
5.27k
                goto fail;
195
5.27k
        }
196
197
18.1k
        r = FIDO_OK;
198
50.0k
fail:
199
50.0k
        es256_sk_free(&sk);
200
50.0k
        es256_pk_free(&ak);
201
202
50.0k
        if (r != FIDO_OK) {
203
31.9k
                es256_pk_free(pk);
204
31.9k
                fido_blob_free(ecdh);
205
31.9k
        }
206
207
50.0k
        return r;
208
18.1k
}