Coverage Report

Created: 2026-04-08 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/libfido2/src/aes256.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2021-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 "fido.h"
9
10
static const EVP_CIPHER *
11
aes_cbc_cipher(size_t keysize)
12
33.9k
{
13
33.9k
        switch (keysize) {
14
1.32k
        case 16:
15
1.32k
                return EVP_aes_128_cbc();
16
32.5k
        case 32:
17
32.5k
                return EVP_aes_256_cbc();
18
0
        default:
19
0
                return NULL;
20
33.9k
        }
21
33.9k
}
22
23
static int
24
aes_cbc(const fido_blob_t *key, const u_char *iv, const fido_blob_t *in,
25
    fido_blob_t *out, int encrypt)
26
39.6k
{
27
39.6k
        EVP_CIPHER_CTX *ctx = NULL;
28
39.6k
        const EVP_CIPHER *cipher;
29
39.6k
        int ok = -1;
30
31
39.6k
        memset(out, 0, sizeof(*out));
32
33
39.6k
        if (in->len > UINT_MAX || in->len % 16 || in->len == 0) {
34
5.47k
                fido_log_debug("%s: invalid input len %zu", __func__, in->len);
35
5.47k
                goto fail;
36
5.47k
        }
37
34.1k
        out->len = in->len;
38
34.1k
        if ((out->ptr = calloc(1, out->len)) == NULL) {
39
118
                fido_log_debug("%s: calloc", __func__);
40
118
                goto fail;
41
118
        }
42
34.0k
        if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
43
34.0k
            (cipher = aes_cbc_cipher(key->len)) == NULL) {
44
187
                fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__);
45
187
                goto fail;
46
187
        }
47
33.8k
        if (EVP_CipherInit(ctx, cipher, key->ptr, iv, encrypt) == 0 ||
48
33.8k
            EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)out->len) < 0) {
49
291
                fido_log_debug("%s: EVP_Cipher", __func__);
50
291
                goto fail;
51
291
        }
52
53
33.5k
        ok = 0;
54
39.6k
fail:
55
39.6k
        if (ctx != NULL)
56
33.9k
                EVP_CIPHER_CTX_free(ctx);
57
39.6k
        if (ok < 0)
58
6.06k
                fido_blob_reset(out);
59
60
39.6k
        return ok;
61
33.5k
}
62
63
int
64
aes128_cbc_dec(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out)
65
1.38k
{
66
1.38k
        const uint8_t *iv;
67
1.38k
        fido_blob_t cin;
68
69
1.38k
        if (key->len != 16) {
70
0
                fido_log_debug("%s: invalid key len %zu", __func__, key->len);
71
0
                return -1;
72
0
        }
73
1.38k
        if (in->len < 16) {
74
0
                fido_log_debug("%s: invalid input len %zu", __func__, in->len);
75
0
                return -1;
76
0
        }
77
1.38k
        cin.ptr = in->ptr + 16;
78
1.38k
        cin.len = in->len - 16;
79
1.38k
        iv = in->ptr;
80
1.38k
        return aes_cbc(key, iv, &cin, out, 0);
81
1.38k
}
82
83
static int
84
aes256_cbc(const fido_blob_t *key, const u_char *iv, const fido_blob_t *in,
85
    fido_blob_t *out, int encrypt)
86
38.2k
{
87
38.2k
        if (key->len != 32) {
88
0
                fido_log_debug("%s: invalid key len %zu", __func__, key->len);
89
0
                return -1;
90
0
        }
91
38.2k
        return aes_cbc(key, iv, in, out, encrypt);
92
38.2k
}
93
94
static int
95
aes256_cbc_proto1(const fido_blob_t *key, const fido_blob_t *in,
96
    fido_blob_t *out, int encrypt)
97
21.4k
{
98
21.4k
        u_char iv[16];
99
100
21.4k
        memset(&iv, 0, sizeof(iv));
101
102
21.4k
        return aes256_cbc(key, iv, in, out, encrypt);
103
21.4k
}
104
105
static int
106
aes256_cbc_fips(const fido_blob_t *secret, const fido_blob_t *in,
107
    fido_blob_t *out, int encrypt)
108
17.2k
{
109
17.2k
        fido_blob_t key, cin, cout;
110
17.2k
        u_char iv[16];
111
112
17.2k
        memset(out, 0, sizeof(*out));
113
114
17.2k
        if (secret->len != 64) {
115
0
                fido_log_debug("%s: invalid secret len %zu", __func__,
116
0
                    secret->len);
117
0
                return -1;
118
0
        }
119
17.2k
        if (in->len < sizeof(iv)) {
120
364
                fido_log_debug("%s: invalid input len %zu", __func__, in->len);
121
364
                return -1;
122
364
        }
123
16.8k
        if (encrypt) {
124
11.3k
                if (fido_get_random(iv, sizeof(iv)) < 0) {
125
104
                        fido_log_debug("%s: fido_get_random", __func__);
126
104
                        return -1;
127
104
                }
128
11.2k
                cin = *in;
129
11.2k
        } else {
130
5.54k
                memcpy(iv, in->ptr, sizeof(iv));
131
5.54k
                cin.ptr = in->ptr + sizeof(iv);
132
5.54k
                cin.len = in->len - sizeof(iv);
133
5.54k
        }
134
16.7k
        key.ptr = secret->ptr + 32;
135
16.7k
        key.len = secret->len - 32;
136
16.7k
        if (aes256_cbc(&key, iv, &cin, &cout, encrypt) < 0)
137
5.52k
                return -1;
138
11.2k
        if (encrypt) {
139
10.9k
                if (cout.len > SIZE_MAX - sizeof(iv) ||
140
10.9k
                    (out->ptr = calloc(1, sizeof(iv) + cout.len)) == NULL) {
141
79
                        fido_blob_reset(&cout);
142
79
                        return -1;
143
79
                }
144
10.8k
                out->len = sizeof(iv) + cout.len;
145
10.8k
                memcpy(out->ptr, iv, sizeof(iv));
146
10.8k
                memcpy(out->ptr + sizeof(iv), cout.ptr, cout.len);
147
10.8k
                fido_blob_reset(&cout);
148
10.8k
        } else
149
334
                *out = cout;
150
151
11.1k
        return 0;
152
11.2k
}
153
154
static int
155
aes256_gcm(const fido_blob_t *key, const fido_blob_t *nonce,
156
    const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out,
157
    int encrypt)
158
8.30k
{
159
8.30k
        EVP_CIPHER_CTX *ctx = NULL;
160
8.30k
        const EVP_CIPHER *cipher;
161
8.30k
        size_t textlen;
162
8.30k
        int ok = -1;
163
164
8.30k
        memset(out, 0, sizeof(*out));
165
166
8.30k
        if (nonce->len != 12 || key->len != 32 || aad->len > UINT_MAX) {
167
0
                fido_log_debug("%s: invalid params %zu, %zu, %zu", __func__,
168
0
                    nonce->len, key->len, aad->len);
169
0
                goto fail;
170
0
        }
171
8.30k
        if (in->len > UINT_MAX || in->len > SIZE_MAX - 16) {
172
0
                fido_log_debug("%s: invalid input len %zu", __func__, in->len);
173
0
                goto fail;
174
0
        }
175
8.30k
        if (!encrypt && in->len < 16) {
176
0
                fido_log_debug("%s: invalid input len %zu", __func__, in->len);
177
0
                goto fail;
178
0
        }
179
        /* add tag to (on encrypt) or trim tag from the output (on decrypt) */
180
8.30k
        out->len = encrypt ? in->len + 16 : in->len - 16;
181
8.30k
        if ((out->ptr = calloc(1, out->len)) == NULL) {
182
20
                fido_log_debug("%s: calloc", __func__);
183
20
                goto fail;
184
20
        }
185
8.28k
        if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
186
8.28k
            (cipher = EVP_aes_256_gcm()) == NULL) {
187
46
                fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__);
188
46
                goto fail;
189
46
        }
190
8.24k
        if (EVP_CipherInit(ctx, cipher, key->ptr, nonce->ptr, encrypt) == 0) {
191
28
                fido_log_debug("%s: EVP_CipherInit", __func__);
192
28
                goto fail;
193
28
        }
194
195
8.21k
        if (encrypt)
196
5.48k
                textlen = in->len;
197
2.72k
        else {
198
2.72k
                textlen = in->len - 16;
199
                /* point openssl at the mac tag */
200
2.72k
                if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16,
201
2.72k
                    in->ptr + in->len - 16) == 0) {
202
51
                        fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__);
203
51
                        goto fail;
204
51
                }
205
2.72k
        }
206
        /* the last EVP_Cipher() will either compute or verify the mac tag */
207
8.16k
        if (EVP_Cipher(ctx, NULL, aad->ptr, (u_int)aad->len) < 0 ||
208
8.16k
            EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)textlen) < 0 ||
209
8.16k
            EVP_Cipher(ctx, NULL, NULL, 0) < 0) {
210
2.14k
                fido_log_debug("%s: EVP_Cipher", __func__);
211
2.14k
                goto fail;
212
2.14k
        }
213
6.01k
        if (encrypt) {
214
                /* append the mac tag */
215
5.45k
                if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16,
216
5.45k
                    out->ptr + out->len - 16) == 0) {
217
13
                        fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__);
218
13
                        goto fail;
219
13
                }
220
5.45k
        }
221
222
6.00k
        ok = 0;
223
8.30k
fail:
224
8.30k
        if (ctx != NULL)
225
8.27k
                EVP_CIPHER_CTX_free(ctx);
226
8.30k
        if (ok < 0)
227
2.30k
                fido_blob_reset(out);
228
229
8.30k
        return ok;
230
6.00k
}
231
232
int
233
aes256_cbc_enc(const fido_dev_t *dev, const fido_blob_t *secret,
234
    const fido_blob_t *in, fido_blob_t *out)
235
23.6k
{
236
23.6k
        return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret,
237
12.3k
            in, out, 1) : aes256_cbc_proto1(secret, in, out, 1);
238
23.6k
}
239
240
int
241
aes256_cbc_dec(const fido_dev_t *dev, const fido_blob_t *secret,
242
    const fido_blob_t *in, fido_blob_t *out)
243
15.0k
{
244
15.0k
        return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret,
245
9.15k
            in, out, 0) : aes256_cbc_proto1(secret, in, out, 0);
246
15.0k
}
247
248
int
249
aes256_gcm_enc(const fido_blob_t *key, const fido_blob_t *nonce,
250
    const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out)
251
5.53k
{
252
5.53k
        return aes256_gcm(key, nonce, aad, in, out, 1);
253
5.53k
}
254
255
int
256
aes256_gcm_dec(const fido_blob_t *key, const fido_blob_t *nonce,
257
    const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out)
258
2.77k
{
259
2.77k
        return aes256_gcm(key, nonce, aad, in, out, 0);
260
2.77k
}