Line | Count | Source (jump to first uncovered line) |
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 | | */ |
6 | | |
7 | | #include "fido.h" |
8 | | |
9 | | static int |
10 | | aes256_cbc(const fido_blob_t *key, const u_char *iv, const fido_blob_t *in, |
11 | | fido_blob_t *out, int encrypt) |
12 | 5.49k | { |
13 | 5.49k | EVP_CIPHER_CTX *ctx = NULL; |
14 | 5.49k | const EVP_CIPHER *cipher; |
15 | 5.49k | int ok = -1; |
16 | | |
17 | 5.49k | memset(out, 0, sizeof(*out)); |
18 | | |
19 | 5.49k | if (key->len != 32) { |
20 | 0 | fido_log_debug("%s: invalid key len %zu", __func__, key->len); |
21 | 0 | goto fail; |
22 | 0 | } |
23 | 5.49k | if (in->len > UINT_MAX || in->len % 16 || in->len == 0) { |
24 | 92 | fido_log_debug("%s: invalid input len %zu", __func__, in->len); |
25 | 92 | goto fail; |
26 | 92 | } |
27 | 5.40k | out->len = in->len; |
28 | 5.40k | if ((out->ptr = calloc(1, out->len)) == NULL) { |
29 | 15 | fido_log_debug("%s: calloc", __func__); |
30 | 15 | goto fail; |
31 | 15 | } |
32 | 5.38k | if ((ctx = EVP_CIPHER_CTX_new()) == NULL || |
33 | 5.38k | (cipher = EVP_aes_256_cbc()) == NULL) { |
34 | 37 | fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__); |
35 | 37 | goto fail; |
36 | 37 | } |
37 | 5.35k | if (EVP_CipherInit(ctx, cipher, key->ptr, iv, encrypt) == 0 || |
38 | 5.35k | EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)out->len) < 0) { |
39 | 29 | fido_log_debug("%s: EVP_Cipher", __func__); |
40 | 29 | goto fail; |
41 | 29 | } |
42 | | |
43 | 5.32k | ok = 0; |
44 | 5.49k | fail: |
45 | 5.49k | if (ctx != NULL) |
46 | 5.36k | EVP_CIPHER_CTX_free(ctx); |
47 | 5.49k | if (ok < 0) |
48 | 173 | fido_blob_reset(out); |
49 | | |
50 | 5.49k | return ok; |
51 | 5.32k | } |
52 | | |
53 | | static int |
54 | | aes256_cbc_proto1(const fido_blob_t *key, const fido_blob_t *in, |
55 | | fido_blob_t *out, int encrypt) |
56 | 5.17k | { |
57 | 5.17k | u_char iv[16]; |
58 | | |
59 | 5.17k | memset(&iv, 0, sizeof(iv)); |
60 | | |
61 | 5.17k | return aes256_cbc(key, iv, in, out, encrypt); |
62 | 5.17k | } |
63 | | |
64 | | static int |
65 | | aes256_cbc_fips(const fido_blob_t *secret, const fido_blob_t *in, |
66 | | fido_blob_t *out, int encrypt) |
67 | 332 | { |
68 | 332 | fido_blob_t key, cin, cout; |
69 | 332 | u_char iv[16]; |
70 | | |
71 | 332 | memset(out, 0, sizeof(*out)); |
72 | | |
73 | 332 | if (secret->len != 64) { |
74 | 0 | fido_log_debug("%s: invalid secret len %zu", __func__, |
75 | 0 | secret->len); |
76 | 0 | return -1; |
77 | 0 | } |
78 | 332 | if (in->len < sizeof(iv)) { |
79 | 12 | fido_log_debug("%s: invalid input len %zu", __func__, in->len); |
80 | 12 | return -1; |
81 | 12 | } |
82 | 320 | if (encrypt) { |
83 | 291 | if (fido_get_random(iv, sizeof(iv)) < 0) { |
84 | 0 | fido_log_debug("%s: fido_get_random", __func__); |
85 | 0 | return -1; |
86 | 0 | } |
87 | 291 | cin = *in; |
88 | 291 | } else { |
89 | 29 | memcpy(iv, in->ptr, sizeof(iv)); |
90 | 29 | cin.ptr = in->ptr + sizeof(iv); |
91 | 29 | cin.len = in->len - sizeof(iv); |
92 | 29 | } |
93 | 320 | key.ptr = secret->ptr + 32; |
94 | 320 | key.len = secret->len - 32; |
95 | 320 | if (aes256_cbc(&key, iv, &cin, &cout, encrypt) < 0) |
96 | 33 | return -1; |
97 | 287 | if (encrypt) { |
98 | 276 | if (cout.len > SIZE_MAX - sizeof(iv) || |
99 | 276 | (out->ptr = calloc(1, sizeof(iv) + cout.len)) == NULL) { |
100 | 5 | fido_blob_reset(&cout); |
101 | 5 | return -1; |
102 | 5 | } |
103 | 271 | out->len = sizeof(iv) + cout.len; |
104 | 271 | memcpy(out->ptr, iv, sizeof(iv)); |
105 | 271 | memcpy(out->ptr + sizeof(iv), cout.ptr, cout.len); |
106 | 271 | fido_blob_reset(&cout); |
107 | 271 | } else |
108 | 11 | *out = cout; |
109 | | |
110 | 282 | return 0; |
111 | 287 | } |
112 | | |
113 | | static int |
114 | | aes256_gcm(const fido_blob_t *key, const fido_blob_t *nonce, |
115 | | const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out, |
116 | | int encrypt) |
117 | 611 | { |
118 | 611 | EVP_CIPHER_CTX *ctx = NULL; |
119 | 611 | const EVP_CIPHER *cipher; |
120 | 611 | size_t textlen; |
121 | 611 | int ok = -1; |
122 | | |
123 | 611 | memset(out, 0, sizeof(*out)); |
124 | | |
125 | 611 | if (nonce->len != 12 || key->len != 32 || aad->len > UINT_MAX) { |
126 | 0 | fido_log_debug("%s: invalid params %zu, %zu, %zu", __func__, |
127 | 0 | nonce->len, key->len, aad->len); |
128 | 0 | goto fail; |
129 | 0 | } |
130 | 611 | if (in->len > UINT_MAX || in->len > SIZE_MAX - 16 || in->len < 16) { |
131 | 124 | fido_log_debug("%s: invalid input len %zu", __func__, in->len); |
132 | 124 | goto fail; |
133 | 124 | } |
134 | | /* add tag to (on encrypt) or trim tag from the output (on decrypt) */ |
135 | 487 | out->len = encrypt ? in->len + 16 : in->len - 16; |
136 | 487 | if ((out->ptr = calloc(1, out->len)) == NULL) { |
137 | 4 | fido_log_debug("%s: calloc", __func__); |
138 | 4 | goto fail; |
139 | 4 | } |
140 | 483 | if ((ctx = EVP_CIPHER_CTX_new()) == NULL || |
141 | 483 | (cipher = EVP_aes_256_gcm()) == NULL) { |
142 | 8 | fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__); |
143 | 8 | goto fail; |
144 | 8 | } |
145 | 475 | if (EVP_CipherInit(ctx, cipher, key->ptr, nonce->ptr, encrypt) == 0) { |
146 | 2 | fido_log_debug("%s: EVP_CipherInit", __func__); |
147 | 2 | goto fail; |
148 | 2 | } |
149 | | |
150 | 473 | if (encrypt) |
151 | 244 | textlen = in->len; |
152 | 229 | else { |
153 | 229 | textlen = in->len - 16; |
154 | | /* point openssl at the mac tag */ |
155 | 229 | if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, |
156 | 229 | in->ptr + in->len - 16) == 0) { |
157 | 1 | fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__); |
158 | 1 | goto fail; |
159 | 1 | } |
160 | 229 | } |
161 | | /* the last EVP_Cipher() will either compute or verify the mac tag */ |
162 | 472 | if (EVP_Cipher(ctx, NULL, aad->ptr, (u_int)aad->len) < 0 || |
163 | 472 | EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)textlen) < 0 || |
164 | 472 | EVP_Cipher(ctx, NULL, NULL, 0) < 0) { |
165 | 29 | fido_log_debug("%s: EVP_Cipher", __func__); |
166 | 29 | goto fail; |
167 | 29 | } |
168 | 443 | if (encrypt) { |
169 | | /* append the mac tag */ |
170 | 238 | if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, |
171 | 238 | out->ptr + out->len - 16) == 0) { |
172 | 1 | fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__); |
173 | 1 | goto fail; |
174 | 1 | } |
175 | 238 | } |
176 | | |
177 | 442 | ok = 0; |
178 | 611 | fail: |
179 | 611 | if (ctx != NULL) |
180 | 478 | EVP_CIPHER_CTX_free(ctx); |
181 | 611 | if (ok < 0) |
182 | 169 | fido_blob_reset(out); |
183 | | |
184 | 611 | return ok; |
185 | 442 | } |
186 | | |
187 | | int |
188 | | aes256_cbc_enc(const fido_dev_t *dev, const fido_blob_t *secret, |
189 | | const fido_blob_t *in, fido_blob_t *out) |
190 | 3.13k | { |
191 | 3.13k | return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret, |
192 | 2.84k | in, out, 1) : aes256_cbc_proto1(secret, in, out, 1); |
193 | 3.13k | } |
194 | | |
195 | | int |
196 | | aes256_cbc_dec(const fido_dev_t *dev, const fido_blob_t *secret, |
197 | | const fido_blob_t *in, fido_blob_t *out) |
198 | 2.36k | { |
199 | 2.36k | return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret, |
200 | 2.32k | in, out, 0) : aes256_cbc_proto1(secret, in, out, 0); |
201 | 2.36k | } |
202 | | |
203 | | int |
204 | | aes256_gcm_enc(const fido_blob_t *key, const fido_blob_t *nonce, |
205 | | const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out) |
206 | 374 | { |
207 | 374 | return aes256_gcm(key, nonce, aad, in, out, 1); |
208 | 374 | } |
209 | | |
210 | | int |
211 | | aes256_gcm_dec(const fido_blob_t *key, const fido_blob_t *nonce, |
212 | | const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out) |
213 | 237 | { |
214 | 237 | return aes256_gcm(key, nonce, aad, in, out, 0); |
215 | 237 | } |