Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 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 "fido.h" |
9 | | |
10 | | static int |
11 | | aes256_cbc(const fido_blob_t *key, const u_char *iv, const fido_blob_t *in, |
12 | | fido_blob_t *out, int encrypt) |
13 | 35.9k | { |
14 | 35.9k | EVP_CIPHER_CTX *ctx = NULL; |
15 | 35.9k | const EVP_CIPHER *cipher; |
16 | 35.9k | int ok = -1; |
17 | | |
18 | 35.9k | memset(out, 0, sizeof(*out)); |
19 | | |
20 | 35.9k | if (key->len != 32) { |
21 | 0 | fido_log_debug("%s: invalid key len %zu", __func__, key->len); |
22 | 0 | goto fail; |
23 | 0 | } |
24 | 35.9k | if (in->len > UINT_MAX || in->len % 16 || in->len == 0) { |
25 | 1.01k | fido_log_debug("%s: invalid input len %zu", __func__, in->len); |
26 | 1.01k | goto fail; |
27 | 1.01k | } |
28 | 34.9k | out->len = in->len; |
29 | 34.9k | if ((out->ptr = calloc(1, out->len)) == NULL) { |
30 | 63 | fido_log_debug("%s: calloc", __func__); |
31 | 63 | goto fail; |
32 | 63 | } |
33 | 34.8k | if ((ctx = EVP_CIPHER_CTX_new()) == NULL || |
34 | 34.8k | (cipher = EVP_aes_256_cbc()) == NULL) { |
35 | 176 | fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__); |
36 | 176 | goto fail; |
37 | 176 | } |
38 | 34.7k | if (EVP_CipherInit(ctx, cipher, key->ptr, iv, encrypt) == 0 || |
39 | 34.7k | EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)out->len) < 0) { |
40 | 218 | fido_log_debug("%s: EVP_Cipher", __func__); |
41 | 218 | goto fail; |
42 | 218 | } |
43 | | |
44 | 34.5k | ok = 0; |
45 | 35.9k | fail: |
46 | 35.9k | if (ctx != NULL) |
47 | 34.8k | EVP_CIPHER_CTX_free(ctx); |
48 | 35.9k | if (ok < 0) |
49 | 1.47k | fido_blob_reset(out); |
50 | | |
51 | 35.9k | return ok; |
52 | 34.5k | } |
53 | | |
54 | | static int |
55 | | aes256_cbc_proto1(const fido_blob_t *key, const fido_blob_t *in, |
56 | | fido_blob_t *out, int encrypt) |
57 | 32.3k | { |
58 | 32.3k | u_char iv[16]; |
59 | | |
60 | 32.3k | memset(&iv, 0, sizeof(iv)); |
61 | | |
62 | 32.3k | return aes256_cbc(key, iv, in, out, encrypt); |
63 | 32.3k | } |
64 | | |
65 | | static int |
66 | | aes256_cbc_fips(const fido_blob_t *secret, const fido_blob_t *in, |
67 | | fido_blob_t *out, int encrypt) |
68 | 4.18k | { |
69 | 4.18k | fido_blob_t key, cin, cout; |
70 | 4.18k | u_char iv[16]; |
71 | | |
72 | 4.18k | memset(out, 0, sizeof(*out)); |
73 | | |
74 | 4.18k | if (secret->len != 64) { |
75 | 0 | fido_log_debug("%s: invalid secret len %zu", __func__, |
76 | 0 | secret->len); |
77 | 0 | return -1; |
78 | 0 | } |
79 | 4.18k | if (in->len < sizeof(iv)) { |
80 | 523 | fido_log_debug("%s: invalid input len %zu", __func__, in->len); |
81 | 523 | return -1; |
82 | 523 | } |
83 | 3.66k | if (encrypt) { |
84 | 2.90k | if (fido_get_random(iv, sizeof(iv)) < 0) { |
85 | 72 | fido_log_debug("%s: fido_get_random", __func__); |
86 | 72 | return -1; |
87 | 72 | } |
88 | 2.83k | cin = *in; |
89 | 2.83k | } else { |
90 | 761 | memcpy(iv, in->ptr, sizeof(iv)); |
91 | 761 | cin.ptr = in->ptr + sizeof(iv); |
92 | 761 | cin.len = in->len - sizeof(iv); |
93 | 761 | } |
94 | 3.59k | key.ptr = secret->ptr + 32; |
95 | 3.59k | key.len = secret->len - 32; |
96 | 3.59k | if (aes256_cbc(&key, iv, &cin, &cout, encrypt) < 0) |
97 | 706 | return -1; |
98 | 2.88k | if (encrypt) { |
99 | 2.69k | if (cout.len > SIZE_MAX - sizeof(iv) || |
100 | 2.69k | (out->ptr = calloc(1, sizeof(iv) + cout.len)) == NULL) { |
101 | 50 | fido_blob_reset(&cout); |
102 | 50 | return -1; |
103 | 50 | } |
104 | 2.64k | out->len = sizeof(iv) + cout.len; |
105 | 2.64k | memcpy(out->ptr, iv, sizeof(iv)); |
106 | 2.64k | memcpy(out->ptr + sizeof(iv), cout.ptr, cout.len); |
107 | 2.64k | fido_blob_reset(&cout); |
108 | 2.64k | } else |
109 | 192 | *out = cout; |
110 | | |
111 | 2.83k | return 0; |
112 | 2.88k | } |
113 | | |
114 | | static int |
115 | | aes256_gcm(const fido_blob_t *key, const fido_blob_t *nonce, |
116 | | const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out, |
117 | | int encrypt) |
118 | 13.8k | { |
119 | 13.8k | EVP_CIPHER_CTX *ctx = NULL; |
120 | 13.8k | const EVP_CIPHER *cipher; |
121 | 13.8k | size_t textlen; |
122 | 13.8k | int ok = -1; |
123 | | |
124 | 13.8k | memset(out, 0, sizeof(*out)); |
125 | | |
126 | 13.8k | if (nonce->len != 12 || key->len != 32 || aad->len > UINT_MAX) { |
127 | 0 | fido_log_debug("%s: invalid params %zu, %zu, %zu", __func__, |
128 | 0 | nonce->len, key->len, aad->len); |
129 | 0 | goto fail; |
130 | 0 | } |
131 | 13.8k | if (in->len > UINT_MAX || in->len > SIZE_MAX - 16) { |
132 | 0 | fido_log_debug("%s: invalid input len %zu", __func__, in->len); |
133 | 0 | goto fail; |
134 | 0 | } |
135 | 13.8k | if (!encrypt && in->len < 16) { |
136 | 0 | fido_log_debug("%s: invalid input len %zu", __func__, in->len); |
137 | 0 | goto fail; |
138 | 0 | } |
139 | | /* add tag to (on encrypt) or trim tag from the output (on decrypt) */ |
140 | 13.8k | out->len = encrypt ? in->len + 16 : in->len - 16; |
141 | 13.8k | if ((out->ptr = calloc(1, out->len)) == NULL) { |
142 | 62 | fido_log_debug("%s: calloc", __func__); |
143 | 62 | goto fail; |
144 | 62 | } |
145 | 13.7k | if ((ctx = EVP_CIPHER_CTX_new()) == NULL || |
146 | 13.7k | (cipher = EVP_aes_256_gcm()) == NULL) { |
147 | 106 | fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__); |
148 | 106 | goto fail; |
149 | 106 | } |
150 | 13.6k | if (EVP_CipherInit(ctx, cipher, key->ptr, nonce->ptr, encrypt) == 0) { |
151 | 49 | fido_log_debug("%s: EVP_CipherInit", __func__); |
152 | 49 | goto fail; |
153 | 49 | } |
154 | | |
155 | 13.6k | if (encrypt) |
156 | 10.6k | textlen = in->len; |
157 | 2.98k | else { |
158 | 2.98k | textlen = in->len - 16; |
159 | | /* point openssl at the mac tag */ |
160 | 2.98k | if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, |
161 | 2.98k | in->ptr + in->len - 16) == 0) { |
162 | 27 | fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__); |
163 | 27 | goto fail; |
164 | 27 | } |
165 | 2.98k | } |
166 | | /* the last EVP_Cipher() will either compute or verify the mac tag */ |
167 | 13.6k | if (EVP_Cipher(ctx, NULL, aad->ptr, (u_int)aad->len) < 0 || |
168 | 13.6k | EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)textlen) < 0 || |
169 | 13.6k | EVP_Cipher(ctx, NULL, NULL, 0) < 0) { |
170 | 1.36k | fido_log_debug("%s: EVP_Cipher", __func__); |
171 | 1.36k | goto fail; |
172 | 1.36k | } |
173 | 12.2k | if (encrypt) { |
174 | | /* append the mac tag */ |
175 | 10.5k | if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, |
176 | 10.5k | out->ptr + out->len - 16) == 0) { |
177 | 23 | fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__); |
178 | 23 | goto fail; |
179 | 23 | } |
180 | 10.5k | } |
181 | | |
182 | 12.2k | ok = 0; |
183 | 13.8k | fail: |
184 | 13.8k | if (ctx != NULL) |
185 | 13.7k | EVP_CIPHER_CTX_free(ctx); |
186 | 13.8k | if (ok < 0) |
187 | 1.63k | fido_blob_reset(out); |
188 | | |
189 | 13.8k | return ok; |
190 | 12.2k | } |
191 | | |
192 | | int |
193 | | aes256_cbc_enc(const fido_dev_t *dev, const fido_blob_t *secret, |
194 | | const fido_blob_t *in, fido_blob_t *out) |
195 | 20.8k | { |
196 | 20.8k | return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret, |
197 | 17.9k | in, out, 1) : aes256_cbc_proto1(secret, in, out, 1); |
198 | 20.8k | } |
199 | | |
200 | | int |
201 | | aes256_cbc_dec(const fido_dev_t *dev, const fido_blob_t *secret, |
202 | | const fido_blob_t *in, fido_blob_t *out) |
203 | 15.6k | { |
204 | 15.6k | return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret, |
205 | 14.4k | in, out, 0) : aes256_cbc_proto1(secret, in, out, 0); |
206 | 15.6k | } |
207 | | |
208 | | int |
209 | | aes256_gcm_enc(const fido_blob_t *key, const fido_blob_t *nonce, |
210 | | const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out) |
211 | 10.7k | { |
212 | 10.7k | return aes256_gcm(key, nonce, aad, in, out, 1); |
213 | 10.7k | } |
214 | | |
215 | | int |
216 | | aes256_gcm_dec(const fido_blob_t *key, const fido_blob_t *nonce, |
217 | | const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out) |
218 | 3.08k | { |
219 | 3.08k | return aes256_gcm(key, nonce, aad, in, out, 0); |
220 | 3.08k | } |