/src/boringssl/crypto/fipsmodule/cipher/aead.cc.inc
Line | Count | Source |
1 | | // Copyright 2014 The BoringSSL Authors |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include <openssl/aead.h> |
16 | | |
17 | | #include <optional> |
18 | | |
19 | | #include <assert.h> |
20 | | #include <string.h> |
21 | | |
22 | | #include <openssl/cipher.h> |
23 | | #include <openssl/err.h> |
24 | | #include <openssl/mem.h> |
25 | | #include <openssl/span.h> |
26 | | |
27 | | #include "../../internal.h" |
28 | | #include "../../mem_internal.h" |
29 | | #include "internal.h" |
30 | | |
31 | | |
32 | | using namespace bssl; |
33 | | |
34 | 152k | size_t EVP_AEAD_key_length(const EVP_AEAD *aead) { return aead->key_len; } |
35 | | |
36 | 398k | size_t EVP_AEAD_nonce_length(const EVP_AEAD *aead) { return aead->nonce_len; } |
37 | | |
38 | 8.44k | size_t EVP_AEAD_max_overhead(const EVP_AEAD *aead) { return aead->overhead; } |
39 | | |
40 | 0 | size_t EVP_AEAD_max_tag_len(const EVP_AEAD *aead) { return aead->max_tag_len; } |
41 | | |
42 | 490k | void EVP_AEAD_CTX_zero(EVP_AEAD_CTX *ctx) { |
43 | 490k | OPENSSL_memset(ctx, 0, sizeof(EVP_AEAD_CTX)); |
44 | 490k | } |
45 | | |
46 | | EVP_AEAD_CTX *EVP_AEAD_CTX_new(const EVP_AEAD *aead, const uint8_t *key, |
47 | 0 | size_t key_len, size_t tag_len) { |
48 | 0 | EVP_AEAD_CTX *ctx = New<EVP_AEAD_CTX>(); |
49 | 0 | if (!ctx) { |
50 | 0 | return nullptr; |
51 | 0 | } |
52 | 0 | EVP_AEAD_CTX_zero(ctx); |
53 | |
|
54 | 0 | if (EVP_AEAD_CTX_init(ctx, aead, key, key_len, tag_len, nullptr)) { |
55 | 0 | return ctx; |
56 | 0 | } |
57 | | |
58 | 0 | EVP_AEAD_CTX_free(ctx); |
59 | 0 | return nullptr; |
60 | 0 | } |
61 | | |
62 | 0 | void EVP_AEAD_CTX_free(EVP_AEAD_CTX *ctx) { |
63 | 0 | if (ctx == nullptr) { |
64 | 0 | return; |
65 | 0 | } |
66 | 0 | EVP_AEAD_CTX_cleanup(ctx); |
67 | 0 | Delete(ctx); |
68 | 0 | } |
69 | | |
70 | | int EVP_AEAD_CTX_init(EVP_AEAD_CTX *ctx, const EVP_AEAD *aead, |
71 | | const uint8_t *key, size_t key_len, size_t tag_len, |
72 | 306 | ENGINE *impl) { |
73 | 306 | if (!aead->init) { |
74 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_NO_DIRECTION_SET); |
75 | 0 | ctx->aead = nullptr; |
76 | 0 | return 0; |
77 | 0 | } |
78 | 306 | return EVP_AEAD_CTX_init_with_direction(ctx, aead, key, key_len, tag_len, |
79 | 306 | evp_aead_open); |
80 | 306 | } |
81 | | |
82 | | int EVP_AEAD_CTX_init_with_direction(EVP_AEAD_CTX *ctx, const EVP_AEAD *aead, |
83 | | const uint8_t *key, size_t key_len, |
84 | | size_t tag_len, |
85 | 119k | enum evp_aead_direction_t dir) { |
86 | 119k | if (key_len != aead->key_len) { |
87 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_KEY_SIZE); |
88 | 0 | ctx->aead = nullptr; |
89 | 0 | return 0; |
90 | 0 | } |
91 | | |
92 | 119k | ctx->aead = aead; |
93 | | |
94 | 119k | int ok; |
95 | 119k | if (aead->init) { |
96 | 86.7k | ok = aead->init(ctx, key, key_len, tag_len); |
97 | 86.7k | } else { |
98 | 33.1k | ok = aead->init_with_direction(ctx, key, key_len, tag_len, dir); |
99 | 33.1k | } |
100 | | |
101 | 119k | if (!ok) { |
102 | 0 | ctx->aead = nullptr; |
103 | 0 | } |
104 | | |
105 | 119k | return ok; |
106 | 119k | } |
107 | | |
108 | 489k | void EVP_AEAD_CTX_cleanup(EVP_AEAD_CTX *ctx) { |
109 | 489k | if (ctx->aead == nullptr) { |
110 | 370k | return; |
111 | 370k | } |
112 | 119k | ctx->aead->cleanup(ctx); |
113 | 119k | ctx->aead = nullptr; |
114 | 119k | } |
115 | | |
116 | | // check_alias returns 1 if |out| is compatible with |in| and 0 otherwise. If |
117 | | // |in| and |out| alias, we require that |in| == |out|. |
118 | | static int check_alias(const uint8_t *in, size_t in_len, const uint8_t *out, |
119 | 9.98k | size_t out_len) { |
120 | 9.98k | if (!buffers_alias(in, in_len, out, out_len)) { |
121 | 4.03k | return 1; |
122 | 4.03k | } |
123 | | |
124 | 5.94k | return in == out; |
125 | 9.98k | } |
126 | | |
127 | | int EVP_AEAD_CTX_seal(const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, |
128 | | size_t max_out_len, const uint8_t *nonce, |
129 | | size_t nonce_len, const uint8_t *in, size_t in_len, |
130 | 0 | const uint8_t *ad, size_t ad_len) { |
131 | 0 | bool ok = false; |
132 | 0 | Cleanup cleanup([&] { |
133 | 0 | if (!ok) { |
134 | | // In the event of an error, clear the output buffer so that a caller |
135 | | // that doesn't check the return value doesn't send raw data. |
136 | 0 | OPENSSL_memset(out, 0, max_out_len); |
137 | 0 | *out_len = 0; |
138 | 0 | } |
139 | 0 | }); |
140 | |
|
141 | 0 | if (max_out_len < in_len) { |
142 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); |
143 | 0 | return 0; |
144 | 0 | } |
145 | | |
146 | 0 | CRYPTO_IOVEC iovec[1]; |
147 | 0 | iovec[0].in = in; |
148 | 0 | iovec[0].out = out; |
149 | 0 | iovec[0].len = in_len; |
150 | 0 | CRYPTO_IVEC aadvec[1]; |
151 | 0 | aadvec[0].in = ad; |
152 | 0 | aadvec[0].len = ad_len; |
153 | 0 | if (!EVP_AEAD_CTX_sealv(ctx, iovec, 1, out + in_len, out_len, |
154 | 0 | max_out_len - in_len, nonce, nonce_len, aadvec, 1)) { |
155 | 0 | *out_len = 0; |
156 | 0 | return 0; |
157 | 0 | } |
158 | 0 | *out_len += in_len; |
159 | 0 | ok = true; |
160 | 0 | return 1; |
161 | 0 | } |
162 | | |
163 | | int EVP_AEAD_CTX_seal_scatter(const EVP_AEAD_CTX *ctx, uint8_t *out, |
164 | | uint8_t *out_tag, size_t *out_tag_len, |
165 | | size_t max_out_tag_len, const uint8_t *nonce, |
166 | | size_t nonce_len, const uint8_t *in, |
167 | | size_t in_len, const uint8_t *extra_in, |
168 | | size_t extra_in_len, const uint8_t *ad, |
169 | 2.06k | size_t ad_len) { |
170 | 2.06k | bool ok = false; |
171 | 2.06k | Cleanup cleanup([&] { |
172 | 2.06k | if (!ok) { |
173 | | // In the event of an error, clear the output buffer so that a caller |
174 | | // that doesn't check the return value doesn't send raw data. |
175 | 0 | OPENSSL_memset(out, 0, in_len); |
176 | 0 | OPENSSL_memset(out_tag, 0, max_out_tag_len); |
177 | 0 | *out_tag_len = 0; |
178 | 0 | } |
179 | 2.06k | }); |
180 | | |
181 | 2.06k | CRYPTO_IOVEC iovec[2]; |
182 | 2.06k | iovec[0].in = in; |
183 | 2.06k | iovec[0].out = out; |
184 | 2.06k | iovec[0].len = in_len; |
185 | 2.06k | iovec[1].in = extra_in; |
186 | 2.06k | iovec[1].out = out_tag; |
187 | 2.06k | iovec[1].len = extra_in_len; |
188 | 2.06k | CRYPTO_IVEC aadvec[1]; |
189 | 2.06k | aadvec[0].in = ad; |
190 | 2.06k | aadvec[0].len = ad_len; |
191 | 2.06k | if (!EVP_AEAD_CTX_sealv( |
192 | 2.06k | ctx, iovec, extra_in_len ? 2 : 1, out_tag + extra_in_len, out_tag_len, |
193 | 2.06k | max_out_tag_len - extra_in_len, nonce, nonce_len, aadvec, 1)) { |
194 | 0 | *out_tag_len = 0; |
195 | 0 | return 0; |
196 | 0 | } |
197 | 2.06k | *out_tag_len += extra_in_len; |
198 | 2.06k | ok = true; |
199 | 2.06k | return 1; |
200 | 2.06k | } |
201 | | |
202 | 9.38k | static bool check_iovec_internal_alias(Span<const CRYPTO_IOVEC> iovecs) { |
203 | 19.3k | for (size_t i = 0; i < iovecs.size(); ++i) { |
204 | | // Same index check. |
205 | 9.98k | if (!check_alias(iovecs[i].in, iovecs[i].len, iovecs[i].out, |
206 | 9.98k | iovecs[i].len)) { |
207 | 0 | return false; |
208 | 0 | } |
209 | 9.98k | #if !defined(NDEBUG) |
210 | | // Unrealistic cases; they'd be harmful but also extremely unlikely anyone |
211 | | // will ever get those wrong. Thus skip them in release builds. |
212 | 10.5k | for (size_t j = i + 1; j < iovecs.size(); ++j) { |
213 | 599 | if (buffers_alias(iovecs[i].in, iovecs[i].len, // |
214 | 599 | iovecs[j].out, iovecs[j].len) || |
215 | 599 | buffers_alias(iovecs[i].out, iovecs[i].len, // |
216 | 599 | iovecs[j].in, iovecs[j].len) || |
217 | 599 | buffers_alias(iovecs[i].out, iovecs[i].len, // |
218 | 599 | iovecs[j].out, iovecs[j].len)) { |
219 | 0 | return false; |
220 | 0 | } |
221 | 599 | } |
222 | 9.98k | #endif |
223 | 9.98k | } |
224 | 9.38k | return true; |
225 | 9.38k | } |
226 | | |
227 | | #if !defined(NDEBUG) |
228 | | static bool check_ivec_buf_alias(Span<const CRYPTO_IVEC> ivecs, |
229 | 19.3k | const uint8_t *buf, size_t buf_len) { |
230 | 19.3k | for (const CRYPTO_IVEC &ivec : ivecs) { |
231 | 19.3k | if (buffers_alias(ivec.in, ivec.len, buf, buf_len)) { |
232 | 0 | return false; |
233 | 0 | } |
234 | 19.3k | } |
235 | 19.3k | return true; |
236 | 19.3k | } |
237 | | |
238 | | static bool check_iovec_out_ivec_alias(Span<const CRYPTO_IOVEC> iovecs, |
239 | 9.38k | Span<const CRYPTO_IVEC> ivecs) { |
240 | 9.98k | for (const CRYPTO_IOVEC &iovec : iovecs) { |
241 | 9.98k | if (!check_ivec_buf_alias(ivecs, iovec.out, iovec.len)) { |
242 | 0 | return false; |
243 | 0 | } |
244 | 9.98k | } |
245 | 9.38k | return true; |
246 | 9.38k | } |
247 | | |
248 | | static bool check_iovec_buf_alias(Span<const CRYPTO_IOVEC> iovecs, |
249 | 9.38k | const uint8_t *buf, size_t buf_len) { |
250 | 9.98k | for (const CRYPTO_IOVEC &iovec : iovecs) { |
251 | 9.98k | if (buffers_alias(iovec.in, iovec.len, buf, buf_len)) { |
252 | 0 | return false; |
253 | 0 | } |
254 | 9.98k | if (buffers_alias(iovec.out, iovec.len, buf, buf_len)) { |
255 | 0 | return false; |
256 | 0 | } |
257 | 9.98k | } |
258 | 9.38k | return true; |
259 | 9.38k | } |
260 | | |
261 | | static bool check_iovec_out_buf_alias(Span<const CRYPTO_IOVEC> iovecs, |
262 | 18.7k | const uint8_t *buf, size_t buf_len) { |
263 | 19.9k | for (const CRYPTO_IOVEC &iovec : iovecs) { |
264 | 19.9k | if (buffers_alias(iovec.out, iovec.len, buf, buf_len)) { |
265 | 0 | return false; |
266 | 0 | } |
267 | 19.9k | } |
268 | 18.7k | return true; |
269 | 18.7k | } |
270 | | #endif |
271 | | |
272 | | static bool check_iovec_alias(Span<const CRYPTO_IOVEC> iovecs, |
273 | | Span<const CRYPTO_IVEC> aadvecs, |
274 | | const uint8_t *out, size_t out_len, |
275 | | const uint8_t *in1, size_t in1_len, |
276 | 9.38k | const uint8_t *in2, size_t in2_len) { |
277 | 9.38k | return |
278 | 9.38k | #if !defined(NDEBUG) |
279 | | // Unrealistic cases; they'd be harmful but also extremely unlikely anyone |
280 | | // will ever get those wrong. Thus skip them in release builds. |
281 | | // |
282 | | // iovec.out <-> aadvec. |
283 | 9.38k | check_iovec_out_ivec_alias(iovecs, aadvecs) && |
284 | | // iovec <-> out. |
285 | 9.38k | check_iovec_buf_alias(iovecs, out, out_len) && |
286 | | // iovec.out <-> in1. |
287 | 9.38k | check_iovec_out_buf_alias(iovecs, in1, in1_len) && |
288 | | // iovec.out <-> in2. |
289 | 9.38k | check_iovec_out_buf_alias(iovecs, in2, in2_len) && |
290 | | // aadvec <-> out. |
291 | 9.38k | check_ivec_buf_alias(aadvecs, out, out_len) && |
292 | | // out <-> in1. |
293 | 9.38k | !buffers_alias(out, out_len, in1, in1_len) && |
294 | | // out <-> in2. |
295 | 9.38k | !buffers_alias(out, out_len, in2, in2_len) && |
296 | 9.38k | #endif |
297 | | // iovec <-> iovec. |
298 | 9.38k | check_iovec_internal_alias(iovecs); |
299 | 9.38k | } |
300 | | |
301 | 2.94k | static void clear_iovec(Span<const CRYPTO_IOVEC> iovecs) { |
302 | 2.94k | for (const CRYPTO_IOVEC &iovec : iovecs) { |
303 | 2.94k | OPENSSL_memset(iovec.out, 0, iovec.len); |
304 | 2.94k | } |
305 | 2.94k | } |
306 | | |
307 | | int EVP_AEAD_CTX_sealv(const EVP_AEAD_CTX *ctx, const CRYPTO_IOVEC *iovec, |
308 | | size_t num_iovec, uint8_t *out_tag, size_t *out_tag_len, |
309 | | size_t max_out_tag_len, const uint8_t *nonce, |
310 | | size_t nonce_len, const CRYPTO_IVEC *aadvec, |
311 | 2.06k | size_t num_aadvec) { |
312 | 2.06k | Span<const CRYPTO_IOVEC> iovecs(iovec, num_iovec); |
313 | 2.06k | Span<const CRYPTO_IVEC> aadvecs(aadvec, num_aadvec); |
314 | | |
315 | 2.06k | bool ok = false; |
316 | 2.06k | Cleanup cleanup([&] { |
317 | 2.06k | if (!ok) { |
318 | | // In the event of an error, clear the output buffer so that a caller |
319 | | // that doesn't check the return value doesn't send raw data. |
320 | 0 | clear_iovec(iovecs); |
321 | 0 | OPENSSL_memset(out_tag, 0, max_out_tag_len); |
322 | 0 | *out_tag_len = 0; |
323 | 0 | } |
324 | 2.06k | }); |
325 | | |
326 | 2.06k | if (!bssl::iovec::IsValid(iovecs) || !bssl::iovec::IsValid(aadvecs)) { |
327 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); |
328 | 0 | return 0; |
329 | 0 | } |
330 | | |
331 | | // Enforce aliasing rules: no output may alias any input, with the one |
332 | | // exception that an iovec member's |in| and |out| pointers may be identical |
333 | | // for in-place operation. |
334 | 2.06k | if (!check_iovec_alias(iovecs, aadvecs, out_tag, max_out_tag_len, nonce, |
335 | 2.06k | nonce_len, nullptr, 0)) { |
336 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_OUTPUT_ALIASES_INPUT); |
337 | 0 | return 0; |
338 | 0 | } |
339 | | |
340 | 2.06k | if (!ctx->aead->sealv) { |
341 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_CTRL_NOT_IMPLEMENTED); |
342 | 0 | return 0; |
343 | 0 | } |
344 | | |
345 | 2.06k | if (ctx->aead->sealv(ctx, iovecs, Span(out_tag, max_out_tag_len), out_tag_len, |
346 | 2.06k | Span(nonce, nonce_len), aadvecs)) { |
347 | 2.06k | ok = true; |
348 | 2.06k | return 1; |
349 | 2.06k | } |
350 | | |
351 | 0 | return 0; |
352 | 2.06k | } |
353 | | |
354 | | int EVP_AEAD_CTX_open(const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, |
355 | | size_t max_out_len, const uint8_t *nonce, |
356 | | size_t nonce_len, const uint8_t *in, size_t in_len, |
357 | 7.31k | const uint8_t *ad, size_t ad_len) { |
358 | 7.31k | bool ok = false; |
359 | 7.31k | Cleanup cleanup([&] { |
360 | 7.31k | if (!ok) { |
361 | | // In the event of an error, clear the output buffer so that a caller |
362 | | // that doesn't check the return value doesn't try and process bad |
363 | | // data. |
364 | 2.94k | OPENSSL_memset(out, 0, max_out_len); |
365 | 2.94k | *out_len = 0; |
366 | 2.94k | } |
367 | 7.31k | }); |
368 | | |
369 | 7.31k | if (ctx->tag_len) { |
370 | | // If the tag length is known, the caller only needs to provide enough |
371 | | // space for in_len - tag_len. |
372 | 3.28k | if (in_len < ctx->tag_len) { |
373 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); |
374 | 0 | return 0; |
375 | 0 | } |
376 | 3.28k | size_t plaintext_len = in_len - ctx->tag_len; |
377 | 3.28k | if (max_out_len < plaintext_len) { |
378 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); |
379 | 0 | return 0; |
380 | 0 | } |
381 | | |
382 | 3.28k | CRYPTO_IOVEC iovec[1]; |
383 | 3.28k | iovec[0].in = in; |
384 | 3.28k | iovec[0].out = out; |
385 | 3.28k | iovec[0].len = plaintext_len; |
386 | 3.28k | CRYPTO_IVEC aadvec[1]; |
387 | 3.28k | aadvec[0].in = ad; |
388 | 3.28k | aadvec[0].len = ad_len; |
389 | 3.28k | if (!EVP_AEAD_CTX_openv_detached(ctx, iovec, 1, nonce, nonce_len, |
390 | 3.28k | in + plaintext_len, ctx->tag_len, aadvec, |
391 | 3.28k | 1)) { |
392 | 2.63k | return 0; |
393 | 2.63k | } |
394 | 650 | *out_len = plaintext_len; |
395 | 650 | ok = true; |
396 | 650 | return 1; |
397 | 3.28k | } |
398 | | |
399 | 4.02k | if (max_out_len < in_len) { |
400 | | // Variable tag length AEADs need to be able to decrypt the entire |
401 | | // plaintext before they can split it up. So the caller has to provide |
402 | | // sufficient max_out_len for temporary data. |
403 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); |
404 | 0 | return 0; |
405 | 0 | } |
406 | 4.02k | CRYPTO_IOVEC iovec[1]; |
407 | 4.02k | iovec[0].in = in; |
408 | 4.02k | iovec[0].out = out; |
409 | 4.02k | iovec[0].len = in_len; |
410 | 4.02k | CRYPTO_IVEC aadvec[1]; |
411 | 4.02k | aadvec[0].in = ad; |
412 | 4.02k | aadvec[0].len = ad_len; |
413 | 4.02k | if (!EVP_AEAD_CTX_openv(ctx, iovec, 1, out_len, nonce, nonce_len, aadvec, |
414 | 4.02k | 1)) { |
415 | 304 | return 0; |
416 | 304 | } |
417 | 3.72k | ok = true; |
418 | 3.72k | return 1; |
419 | 4.02k | } |
420 | | |
421 | | int EVP_AEAD_CTX_open_gather(const EVP_AEAD_CTX *ctx, uint8_t *out, |
422 | | const uint8_t *nonce, size_t nonce_len, |
423 | | const uint8_t *in, size_t in_len, |
424 | | const uint8_t *in_tag, size_t in_tag_len, |
425 | 0 | const uint8_t *ad, size_t ad_len) { |
426 | 0 | bool ok = false; |
427 | 0 | Cleanup cleanup([&] { |
428 | 0 | if (!ok) { |
429 | | // In the event of an error, clear the output buffer so that a caller |
430 | | // that doesn't check the return value doesn't try and process bad |
431 | | // data. |
432 | 0 | OPENSSL_memset(out, 0, in_len); |
433 | 0 | } |
434 | 0 | }); |
435 | |
|
436 | 0 | CRYPTO_IOVEC iovec[1]; |
437 | 0 | iovec[0].in = in; |
438 | 0 | iovec[0].out = out; |
439 | 0 | iovec[0].len = in_len; |
440 | 0 | CRYPTO_IVEC aadvec[1]; |
441 | 0 | aadvec[0].in = ad; |
442 | 0 | aadvec[0].len = ad_len; |
443 | 0 | if (!EVP_AEAD_CTX_openv_detached(ctx, iovec, 1, nonce, nonce_len, in_tag, |
444 | 0 | in_tag_len, aadvec, 1)) { |
445 | 0 | return 0; |
446 | 0 | } |
447 | 0 | ok = true; |
448 | 0 | return 1; |
449 | 0 | } |
450 | | |
451 | | int EVP_AEAD_CTX_openv(const EVP_AEAD_CTX *ctx, const CRYPTO_IOVEC *iovec, |
452 | | size_t num_iovec, size_t *out_total_bytes, |
453 | | const uint8_t *nonce, size_t nonce_len, |
454 | 4.02k | const CRYPTO_IVEC *aadvec, size_t num_aadvec) { |
455 | 4.02k | Span<const CRYPTO_IOVEC> iovecs(iovec, num_iovec); |
456 | 4.02k | Span<const CRYPTO_IVEC> aadvecs(aadvec, num_aadvec); |
457 | | |
458 | 4.02k | bool ok = false; |
459 | 4.02k | Cleanup cleanup([&] { |
460 | 4.02k | if (!ok) { |
461 | | // In the event of an error, clear the output buffer so that a caller |
462 | | // that doesn't check the return value doesn't try and process bad |
463 | | // data. |
464 | 304 | clear_iovec(iovecs); |
465 | 304 | *out_total_bytes = 0; |
466 | 304 | } |
467 | 4.02k | }); |
468 | | |
469 | 4.02k | if (!bssl::iovec::IsValid(iovecs) || !bssl::iovec::IsValid(aadvecs)) { |
470 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); |
471 | 0 | return 0; |
472 | 0 | } |
473 | | |
474 | | // Enforce aliasing rules: no output may alias any input, with the one |
475 | | // exception that an iovec member's |in| and |out| pointers may be identical |
476 | | // for in-place operation. |
477 | 4.02k | if (!check_iovec_alias(iovecs, aadvecs, nullptr, 0, nonce, nonce_len, nullptr, |
478 | 4.02k | 0)) { |
479 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_OUTPUT_ALIASES_INPUT); |
480 | 0 | return 0; |
481 | 0 | } |
482 | | |
483 | 4.02k | if (!ctx->aead->openv) { |
484 | 0 | if (ctx->tag_len && ctx->aead->openv_detached) { |
485 | | // Try with a detached tag. |
486 | 0 | InplaceVector<CRYPTO_IOVEC, CRYPTO_IOVEC_MAX> detached_iovecs; |
487 | 0 | detached_iovecs.CopyFrom(iovecs); |
488 | |
|
489 | 0 | uint8_t tagbuf[EVP_AEAD_MAX_OVERHEAD]; |
490 | 0 | std::optional<Span<const uint8_t>> tag = bssl::iovec::GetAndRemoveSuffix( |
491 | 0 | Span(tagbuf).first(ctx->tag_len), Span(detached_iovecs)); |
492 | |
|
493 | 0 | if (!tag.has_value()) { // I.e. no |ctx->tag_len| bytes available. |
494 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); |
495 | 0 | return 0; |
496 | 0 | } |
497 | | |
498 | 0 | if (ctx->aead->openv_detached(ctx, detached_iovecs, |
499 | 0 | Span(nonce, nonce_len), *tag, aadvecs)) { |
500 | 0 | ok = true; |
501 | 0 | *out_total_bytes = bssl::iovec::TotalLength(Span(detached_iovecs)); |
502 | 0 | return 1; |
503 | 0 | } |
504 | 0 | return 0; |
505 | 0 | } |
506 | | |
507 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_CTRL_NOT_IMPLEMENTED); |
508 | 0 | return 0; |
509 | 0 | } |
510 | | |
511 | 4.02k | if (ctx->aead->openv(ctx, iovecs, out_total_bytes, Span(nonce, nonce_len), |
512 | 4.02k | aadvecs)) { |
513 | 3.72k | ok = true; |
514 | 3.72k | return 1; |
515 | 3.72k | } |
516 | | |
517 | 304 | return 0; |
518 | 4.02k | } |
519 | | |
520 | | int EVP_AEAD_CTX_openv_detached(const EVP_AEAD_CTX *ctx, |
521 | | const CRYPTO_IOVEC *iovec, size_t num_iovec, |
522 | | const uint8_t *nonce, size_t nonce_len, |
523 | | const uint8_t *in_tag, size_t in_tag_len, |
524 | 3.28k | const CRYPTO_IVEC *aadvec, size_t num_aadvec) { |
525 | 3.28k | Span<const CRYPTO_IOVEC> iovecs(iovec, num_iovec); |
526 | 3.28k | Span<const CRYPTO_IVEC> aadvecs(aadvec, num_aadvec); |
527 | | |
528 | 3.28k | bool ok = false; |
529 | 3.28k | Cleanup cleanup([&] { |
530 | 3.28k | if (!ok) { |
531 | | // In the event of an error, clear the output buffer so that a caller |
532 | | // that doesn't check the return value doesn't try and process bad |
533 | | // data. |
534 | 2.63k | clear_iovec(iovecs); |
535 | 2.63k | } |
536 | 3.28k | }); |
537 | | |
538 | 3.28k | if (!bssl::iovec::IsValid(iovecs) || !bssl::iovec::IsValid(aadvecs)) { |
539 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); |
540 | 0 | return 0; |
541 | 0 | } |
542 | 3.28k | if (in_tag_len > EVP_AEAD_MAX_OPEN_OVERHEAD) { |
543 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_TAG_SIZE); |
544 | 0 | return 0; |
545 | 0 | } |
546 | | |
547 | | // Enforce aliasing rules: no output may alias any input, with the one |
548 | | // exception that an iovec member's |in| and |out| pointers may be identical |
549 | | // for in-place operation. |
550 | 3.28k | if (!check_iovec_alias(iovecs, aadvecs, nullptr, 0, nonce, nonce_len, in_tag, |
551 | 3.28k | in_tag_len)) { |
552 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_OUTPUT_ALIASES_INPUT); |
553 | 0 | return 0; |
554 | 0 | } |
555 | | |
556 | 3.28k | if (!ctx->aead->openv_detached) { |
557 | | // AEADs with variable overhead may provide openv instead of openv_detached. |
558 | | // While one might call openv and then, on success, discard the result if |
559 | | // the length was wrong, this requires callers to predict the plaintext |
560 | | // length first. We do not expect callers to do this, especially in the TLS |
561 | | // CBC construction, where this length is sensitive to the Lucky 13 attack. |
562 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_CTRL_NOT_IMPLEMENTED); |
563 | 0 | return 0; |
564 | 0 | } |
565 | | |
566 | 3.28k | if (ctx->aead->openv_detached(ctx, iovecs, Span(nonce, nonce_len), |
567 | 3.28k | Span(in_tag, in_tag_len), aadvecs)) { |
568 | 650 | ok = true; |
569 | 650 | return 1; |
570 | 650 | } |
571 | | |
572 | 2.63k | return 0; |
573 | 3.28k | } |
574 | | |
575 | 8.44k | const EVP_AEAD *EVP_AEAD_CTX_aead(const EVP_AEAD_CTX *ctx) { return ctx->aead; } |
576 | | |
577 | | int EVP_AEAD_CTX_get_iv(const EVP_AEAD_CTX *ctx, const uint8_t **out_iv, |
578 | 0 | size_t *out_len) { |
579 | 0 | if (ctx->aead->get_iv == nullptr) { |
580 | 0 | OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
581 | 0 | return 0; |
582 | 0 | } |
583 | | |
584 | 0 | return ctx->aead->get_iv(ctx, out_iv, out_len); |
585 | 0 | } |
586 | | |
587 | | int EVP_AEAD_CTX_tag_len(const EVP_AEAD_CTX *ctx, size_t *out_tag_len, |
588 | 8.27k | const size_t in_len, const size_t extra_in_len) { |
589 | 8.27k | size_t tag_len; |
590 | 8.27k | if (ctx->aead->tag_len) { |
591 | 4.87k | if (in_len + extra_in_len < in_len) { |
592 | 0 | OPENSSL_PUT_ERROR(CIPHER, ERR_R_OVERFLOW); |
593 | 0 | *out_tag_len = 0; |
594 | 0 | return 0; |
595 | 0 | } |
596 | 4.87k | tag_len = ctx->aead->tag_len(ctx, in_len + extra_in_len); |
597 | 4.87k | } else { |
598 | 3.40k | tag_len = ctx->tag_len; |
599 | 3.40k | } |
600 | | |
601 | 8.27k | if (extra_in_len + tag_len < extra_in_len) { |
602 | 0 | OPENSSL_PUT_ERROR(CIPHER, ERR_R_OVERFLOW); |
603 | 0 | *out_tag_len = 0; |
604 | 0 | return 0; |
605 | 0 | } |
606 | 8.27k | *out_tag_len = extra_in_len + tag_len; |
607 | 8.27k | return 1; |
608 | 8.27k | } |