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 | } |