/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 | 166k | size_t EVP_AEAD_key_length(const EVP_AEAD *aead) { return aead->key_len; } |
35 | | |
36 | 443k | size_t EVP_AEAD_nonce_length(const EVP_AEAD *aead) { return aead->nonce_len; } |
37 | | |
38 | 12.8k | 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 | 349k | void EVP_AEAD_CTX_zero(EVP_AEAD_CTX *ctx) { |
43 | 349k | OPENSSL_memset(ctx, 0, sizeof(EVP_AEAD_CTX)); |
44 | 349k | } |
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 | 283 | ENGINE *impl) { |
73 | 283 | 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 | 283 | return EVP_AEAD_CTX_init_with_direction(ctx, aead, key, key_len, tag_len, |
79 | 283 | evp_aead_open); |
80 | 283 | } |
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 | 136k | enum evp_aead_direction_t dir) { |
86 | 136k | 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 | 136k | ctx->aead = aead; |
93 | | |
94 | 136k | int ok; |
95 | 136k | if (aead->init) { |
96 | 105k | ok = aead->init(ctx, key, key_len, tag_len); |
97 | 105k | } else { |
98 | 30.7k | ok = aead->init_with_direction(ctx, key, key_len, tag_len, dir); |
99 | 30.7k | } |
100 | | |
101 | 136k | if (!ok) { |
102 | 0 | ctx->aead = nullptr; |
103 | 0 | } |
104 | | |
105 | 136k | return ok; |
106 | 136k | } |
107 | | |
108 | 348k | void EVP_AEAD_CTX_cleanup(EVP_AEAD_CTX *ctx) { |
109 | 348k | if (ctx->aead == nullptr) { |
110 | 212k | return; |
111 | 212k | } |
112 | 136k | ctx->aead->cleanup(ctx); |
113 | 136k | ctx->aead = nullptr; |
114 | 136k | } |
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.89k | size_t out_len) { |
120 | 9.89k | if (!buffers_alias(in, in_len, out, out_len)) { |
121 | 4.66k | return 1; |
122 | 4.66k | } |
123 | | |
124 | 5.22k | return in == out; |
125 | 9.89k | } |
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.26k | size_t ad_len) { |
170 | 2.26k | bool ok = false; |
171 | 2.26k | Cleanup cleanup([&] { |
172 | 2.26k | 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.26k | }); |
180 | | |
181 | | // `out_tag` contains both the encryption of `extra_in` and the tag. |
182 | 2.26k | Span<uint8_t> out_tag_span(out_tag, max_out_tag_len); |
183 | 2.26k | if (out_tag_span.size() < extra_in_len) { |
184 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); |
185 | 0 | return 0; |
186 | 0 | } |
187 | 2.26k | Span<uint8_t> extra_out = out_tag_span.first(extra_in_len); |
188 | 2.26k | Span<uint8_t> tag_only_out = out_tag_span.subspan(extra_in_len); |
189 | | |
190 | 2.26k | CRYPTO_IOVEC iovec[2]; |
191 | 2.26k | iovec[0].in = in; |
192 | 2.26k | iovec[0].out = out; |
193 | 2.26k | iovec[0].len = in_len; |
194 | 2.26k | iovec[1].in = extra_in; |
195 | 2.26k | iovec[1].out = extra_out.data(); |
196 | 2.26k | iovec[1].len = extra_in_len; |
197 | 2.26k | CRYPTO_IVEC aadvec[1]; |
198 | 2.26k | aadvec[0].in = ad; |
199 | 2.26k | aadvec[0].len = ad_len; |
200 | 2.26k | if (!EVP_AEAD_CTX_sealv(ctx, iovec, extra_in_len ? 2 : 1, tag_only_out.data(), |
201 | 2.26k | out_tag_len, tag_only_out.size(), nonce, nonce_len, |
202 | 2.26k | aadvec, 1)) { |
203 | 0 | *out_tag_len = 0; |
204 | 0 | return 0; |
205 | 0 | } |
206 | 2.26k | *out_tag_len += extra_in_len; |
207 | 2.26k | ok = true; |
208 | 2.26k | return 1; |
209 | 2.26k | } |
210 | | |
211 | 9.25k | static bool check_iovec_internal_alias(Span<const CRYPTO_IOVEC> iovecs) { |
212 | 19.1k | for (size_t i = 0; i < iovecs.size(); ++i) { |
213 | | // Same index check. |
214 | 9.89k | if (!check_alias(iovecs[i].in, iovecs[i].len, iovecs[i].out, |
215 | 9.89k | iovecs[i].len)) { |
216 | 0 | return false; |
217 | 0 | } |
218 | 9.89k | #if !defined(NDEBUG) |
219 | | // Unrealistic cases; they'd be harmful but also extremely unlikely anyone |
220 | | // will ever get those wrong. Thus skip them in release builds. |
221 | 10.5k | for (size_t j = i + 1; j < iovecs.size(); ++j) { |
222 | 636 | if (buffers_alias(iovecs[i].in, iovecs[i].len, // |
223 | 636 | iovecs[j].out, iovecs[j].len) || |
224 | 636 | buffers_alias(iovecs[i].out, iovecs[i].len, // |
225 | 636 | iovecs[j].in, iovecs[j].len) || |
226 | 636 | buffers_alias(iovecs[i].out, iovecs[i].len, // |
227 | 636 | iovecs[j].out, iovecs[j].len)) { |
228 | 0 | return false; |
229 | 0 | } |
230 | 636 | } |
231 | 9.89k | #endif |
232 | 9.89k | } |
233 | 9.25k | return true; |
234 | 9.25k | } |
235 | | |
236 | | #if !defined(NDEBUG) |
237 | | static bool check_ivec_buf_alias(Span<const CRYPTO_IVEC> ivecs, |
238 | 19.1k | const uint8_t *buf, size_t buf_len) { |
239 | 19.1k | for (const CRYPTO_IVEC &ivec : ivecs) { |
240 | 19.1k | if (buffers_alias(ivec.in, ivec.len, buf, buf_len)) { |
241 | 0 | return false; |
242 | 0 | } |
243 | 19.1k | } |
244 | 19.1k | return true; |
245 | 19.1k | } |
246 | | |
247 | | static bool check_iovec_out_ivec_alias(Span<const CRYPTO_IOVEC> iovecs, |
248 | 9.25k | Span<const CRYPTO_IVEC> ivecs) { |
249 | 9.89k | for (const CRYPTO_IOVEC &iovec : iovecs) { |
250 | 9.89k | if (!check_ivec_buf_alias(ivecs, iovec.out, iovec.len)) { |
251 | 0 | return false; |
252 | 0 | } |
253 | 9.89k | } |
254 | 9.25k | return true; |
255 | 9.25k | } |
256 | | |
257 | | static bool check_iovec_buf_alias(Span<const CRYPTO_IOVEC> iovecs, |
258 | 9.25k | const uint8_t *buf, size_t buf_len) { |
259 | 9.89k | for (const CRYPTO_IOVEC &iovec : iovecs) { |
260 | 9.89k | if (buffers_alias(iovec.in, iovec.len, buf, buf_len)) { |
261 | 0 | return false; |
262 | 0 | } |
263 | 9.89k | if (buffers_alias(iovec.out, iovec.len, buf, buf_len)) { |
264 | 0 | return false; |
265 | 0 | } |
266 | 9.89k | } |
267 | 9.25k | return true; |
268 | 9.25k | } |
269 | | |
270 | | static bool check_iovec_out_buf_alias(Span<const CRYPTO_IOVEC> iovecs, |
271 | 18.5k | const uint8_t *buf, size_t buf_len) { |
272 | 19.7k | for (const CRYPTO_IOVEC &iovec : iovecs) { |
273 | 19.7k | if (buffers_alias(iovec.out, iovec.len, buf, buf_len)) { |
274 | 0 | return false; |
275 | 0 | } |
276 | 19.7k | } |
277 | 18.5k | return true; |
278 | 18.5k | } |
279 | | #endif |
280 | | |
281 | | static bool check_iovec_alias(Span<const CRYPTO_IOVEC> iovecs, |
282 | | Span<const CRYPTO_IVEC> aadvecs, |
283 | | const uint8_t *out, size_t out_len, |
284 | | const uint8_t *in1, size_t in1_len, |
285 | 9.25k | const uint8_t *in2, size_t in2_len) { |
286 | 9.25k | return |
287 | 9.25k | #if !defined(NDEBUG) |
288 | | // Unrealistic cases; they'd be harmful but also extremely unlikely anyone |
289 | | // will ever get those wrong. Thus skip them in release builds. |
290 | | // |
291 | | // iovec.out <-> aadvec. |
292 | 9.25k | check_iovec_out_ivec_alias(iovecs, aadvecs) && |
293 | | // iovec <-> out. |
294 | 9.25k | check_iovec_buf_alias(iovecs, out, out_len) && |
295 | | // iovec.out <-> in1. |
296 | 9.25k | check_iovec_out_buf_alias(iovecs, in1, in1_len) && |
297 | | // iovec.out <-> in2. |
298 | 9.25k | check_iovec_out_buf_alias(iovecs, in2, in2_len) && |
299 | | // aadvec <-> out. |
300 | 9.25k | check_ivec_buf_alias(aadvecs, out, out_len) && |
301 | | // out <-> in1. |
302 | 9.25k | !buffers_alias(out, out_len, in1, in1_len) && |
303 | | // out <-> in2. |
304 | 9.25k | !buffers_alias(out, out_len, in2, in2_len) && |
305 | 9.25k | #endif |
306 | | // iovec <-> iovec. |
307 | 9.25k | check_iovec_internal_alias(iovecs); |
308 | 9.25k | } |
309 | | |
310 | 3.09k | static void clear_iovec(Span<const CRYPTO_IOVEC> iovecs) { |
311 | 3.09k | for (const CRYPTO_IOVEC &iovec : iovecs) { |
312 | 3.09k | OPENSSL_memset(iovec.out, 0, iovec.len); |
313 | 3.09k | } |
314 | 3.09k | } |
315 | | |
316 | | int EVP_AEAD_CTX_sealv(const EVP_AEAD_CTX *ctx, const CRYPTO_IOVEC *iovec, |
317 | | size_t num_iovec, uint8_t *out_tag, size_t *out_tag_len, |
318 | | size_t max_out_tag_len, const uint8_t *nonce, |
319 | | size_t nonce_len, const CRYPTO_IVEC *aadvec, |
320 | 2.26k | size_t num_aadvec) { |
321 | 2.26k | Span<const CRYPTO_IOVEC> iovecs(iovec, num_iovec); |
322 | 2.26k | Span<const CRYPTO_IVEC> aadvecs(aadvec, num_aadvec); |
323 | | |
324 | 2.26k | bool ok = false; |
325 | 2.26k | Cleanup cleanup([&] { |
326 | 2.26k | if (!ok) { |
327 | | // In the event of an error, clear the output buffer so that a caller |
328 | | // that doesn't check the return value doesn't send raw data. |
329 | 0 | clear_iovec(iovecs); |
330 | 0 | OPENSSL_memset(out_tag, 0, max_out_tag_len); |
331 | 0 | *out_tag_len = 0; |
332 | 0 | } |
333 | 2.26k | }); |
334 | | |
335 | 2.26k | if (!bssl::iovec::IsValid(iovecs) || !bssl::iovec::IsValid(aadvecs)) { |
336 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); |
337 | 0 | return 0; |
338 | 0 | } |
339 | | |
340 | | // Enforce aliasing rules: no output may alias any input, with the one |
341 | | // exception that an iovec member's `in` and `out` pointers may be identical |
342 | | // for in-place operation. |
343 | 2.26k | if (!check_iovec_alias(iovecs, aadvecs, out_tag, max_out_tag_len, nonce, |
344 | 2.26k | nonce_len, nullptr, 0)) { |
345 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_OUTPUT_ALIASES_INPUT); |
346 | 0 | return 0; |
347 | 0 | } |
348 | | |
349 | 2.26k | if (!ctx->aead->sealv) { |
350 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_CTRL_NOT_IMPLEMENTED); |
351 | 0 | return 0; |
352 | 0 | } |
353 | | |
354 | 2.26k | if (ctx->aead->sealv(ctx, iovecs, Span(out_tag, max_out_tag_len), out_tag_len, |
355 | 2.26k | Span(nonce, nonce_len), aadvecs)) { |
356 | 2.26k | ok = true; |
357 | 2.26k | return 1; |
358 | 2.26k | } |
359 | | |
360 | 0 | return 0; |
361 | 2.26k | } |
362 | | |
363 | | int EVP_AEAD_CTX_open(const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, |
364 | | size_t max_out_len, const uint8_t *nonce, |
365 | | size_t nonce_len, const uint8_t *in, size_t in_len, |
366 | 6.99k | const uint8_t *ad, size_t ad_len) { |
367 | 6.99k | bool ok = false; |
368 | 6.99k | Cleanup cleanup([&] { |
369 | 6.99k | if (!ok) { |
370 | | // In the event of an error, clear the output buffer so that a caller |
371 | | // that doesn't check the return value doesn't try and process bad |
372 | | // data. |
373 | 3.09k | OPENSSL_memset(out, 0, max_out_len); |
374 | 3.09k | *out_len = 0; |
375 | 3.09k | } |
376 | 6.99k | }); |
377 | | |
378 | 6.99k | if (ctx->tag_len) { |
379 | | // If the tag length is known, the caller only needs to provide enough |
380 | | // space for in_len - tag_len. |
381 | 3.19k | if (in_len < ctx->tag_len) { |
382 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); |
383 | 0 | return 0; |
384 | 0 | } |
385 | 3.19k | size_t plaintext_len = in_len - ctx->tag_len; |
386 | 3.19k | if (max_out_len < plaintext_len) { |
387 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); |
388 | 0 | return 0; |
389 | 0 | } |
390 | | |
391 | 3.19k | CRYPTO_IOVEC iovec[1]; |
392 | 3.19k | iovec[0].in = in; |
393 | 3.19k | iovec[0].out = out; |
394 | 3.19k | iovec[0].len = plaintext_len; |
395 | 3.19k | CRYPTO_IVEC aadvec[1]; |
396 | 3.19k | aadvec[0].in = ad; |
397 | 3.19k | aadvec[0].len = ad_len; |
398 | 3.19k | if (!EVP_AEAD_CTX_openv_detached(ctx, iovec, 1, nonce, nonce_len, |
399 | 3.19k | in + plaintext_len, ctx->tag_len, aadvec, |
400 | 3.19k | 1)) { |
401 | 2.75k | return 0; |
402 | 2.75k | } |
403 | 436 | *out_len = plaintext_len; |
404 | 436 | ok = true; |
405 | 436 | return 1; |
406 | 3.19k | } |
407 | | |
408 | 3.80k | if (max_out_len < in_len) { |
409 | | // Variable tag length AEADs need to be able to decrypt the entire |
410 | | // plaintext before they can split it up. So the caller has to provide |
411 | | // sufficient max_out_len for temporary data. |
412 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); |
413 | 0 | return 0; |
414 | 0 | } |
415 | 3.80k | CRYPTO_IOVEC iovec[1]; |
416 | 3.80k | iovec[0].in = in; |
417 | 3.80k | iovec[0].out = out; |
418 | 3.80k | iovec[0].len = in_len; |
419 | 3.80k | CRYPTO_IVEC aadvec[1]; |
420 | 3.80k | aadvec[0].in = ad; |
421 | 3.80k | aadvec[0].len = ad_len; |
422 | 3.80k | if (!EVP_AEAD_CTX_openv(ctx, iovec, 1, out_len, nonce, nonce_len, aadvec, |
423 | 3.80k | 1)) { |
424 | 335 | return 0; |
425 | 335 | } |
426 | 3.46k | ok = true; |
427 | 3.46k | return 1; |
428 | 3.80k | } |
429 | | |
430 | | int EVP_AEAD_CTX_open_gather(const EVP_AEAD_CTX *ctx, uint8_t *out, |
431 | | const uint8_t *nonce, size_t nonce_len, |
432 | | const uint8_t *in, size_t in_len, |
433 | | const uint8_t *in_tag, size_t in_tag_len, |
434 | 0 | const uint8_t *ad, size_t ad_len) { |
435 | 0 | bool ok = false; |
436 | 0 | Cleanup cleanup([&] { |
437 | 0 | if (!ok) { |
438 | | // In the event of an error, clear the output buffer so that a caller |
439 | | // that doesn't check the return value doesn't try and process bad |
440 | | // data. |
441 | 0 | OPENSSL_memset(out, 0, in_len); |
442 | 0 | } |
443 | 0 | }); |
444 | |
|
445 | 0 | CRYPTO_IOVEC iovec[1]; |
446 | 0 | iovec[0].in = in; |
447 | 0 | iovec[0].out = out; |
448 | 0 | iovec[0].len = in_len; |
449 | 0 | CRYPTO_IVEC aadvec[1]; |
450 | 0 | aadvec[0].in = ad; |
451 | 0 | aadvec[0].len = ad_len; |
452 | 0 | if (!EVP_AEAD_CTX_openv_detached(ctx, iovec, 1, nonce, nonce_len, in_tag, |
453 | 0 | in_tag_len, aadvec, 1)) { |
454 | 0 | return 0; |
455 | 0 | } |
456 | 0 | ok = true; |
457 | 0 | return 1; |
458 | 0 | } |
459 | | |
460 | | int EVP_AEAD_CTX_openv(const EVP_AEAD_CTX *ctx, const CRYPTO_IOVEC *iovec, |
461 | | size_t num_iovec, size_t *out_total_bytes, |
462 | | const uint8_t *nonce, size_t nonce_len, |
463 | 3.80k | const CRYPTO_IVEC *aadvec, size_t num_aadvec) { |
464 | 3.80k | Span<const CRYPTO_IOVEC> iovecs(iovec, num_iovec); |
465 | 3.80k | Span<const CRYPTO_IVEC> aadvecs(aadvec, num_aadvec); |
466 | | |
467 | 3.80k | bool ok = false; |
468 | 3.80k | Cleanup cleanup([&] { |
469 | 3.80k | if (!ok) { |
470 | | // In the event of an error, clear the output buffer so that a caller |
471 | | // that doesn't check the return value doesn't try and process bad |
472 | | // data. |
473 | 335 | clear_iovec(iovecs); |
474 | 335 | *out_total_bytes = 0; |
475 | 335 | } |
476 | 3.80k | }); |
477 | | |
478 | 3.80k | if (!bssl::iovec::IsValid(iovecs) || !bssl::iovec::IsValid(aadvecs)) { |
479 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); |
480 | 0 | return 0; |
481 | 0 | } |
482 | | |
483 | | // Enforce aliasing rules: no output may alias any input, with the one |
484 | | // exception that an iovec member's `in` and `out` pointers may be identical |
485 | | // for in-place operation. |
486 | 3.80k | if (!check_iovec_alias(iovecs, aadvecs, nullptr, 0, nonce, nonce_len, nullptr, |
487 | 3.80k | 0)) { |
488 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_OUTPUT_ALIASES_INPUT); |
489 | 0 | return 0; |
490 | 0 | } |
491 | | |
492 | 3.80k | if (!ctx->aead->openv) { |
493 | 0 | if (ctx->tag_len && ctx->aead->openv_detached) { |
494 | | // Try with a detached tag. |
495 | 0 | InplaceVector<CRYPTO_IOVEC, CRYPTO_IOVEC_MAX> detached_iovecs; |
496 | 0 | detached_iovecs.CopyFrom(iovecs); |
497 | |
|
498 | 0 | uint8_t tagbuf[EVP_AEAD_MAX_OVERHEAD]; |
499 | 0 | std::optional<Span<const uint8_t>> tag = bssl::iovec::GetAndRemoveSuffix( |
500 | 0 | Span(tagbuf).first(ctx->tag_len), Span(detached_iovecs)); |
501 | |
|
502 | 0 | if (!tag.has_value()) { // I.e. no `ctx->tag_len` bytes available. |
503 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); |
504 | 0 | return 0; |
505 | 0 | } |
506 | | |
507 | 0 | if (ctx->aead->openv_detached(ctx, detached_iovecs, |
508 | 0 | Span(nonce, nonce_len), *tag, aadvecs)) { |
509 | 0 | ok = true; |
510 | 0 | *out_total_bytes = bssl::iovec::TotalLength(Span(detached_iovecs)); |
511 | 0 | return 1; |
512 | 0 | } |
513 | 0 | return 0; |
514 | 0 | } |
515 | | |
516 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_CTRL_NOT_IMPLEMENTED); |
517 | 0 | return 0; |
518 | 0 | } |
519 | | |
520 | 3.80k | if (ctx->aead->openv(ctx, iovecs, out_total_bytes, Span(nonce, nonce_len), |
521 | 3.80k | aadvecs)) { |
522 | 3.46k | ok = true; |
523 | 3.46k | return 1; |
524 | 3.46k | } |
525 | | |
526 | 335 | return 0; |
527 | 3.80k | } |
528 | | |
529 | | int EVP_AEAD_CTX_openv_detached(const EVP_AEAD_CTX *ctx, |
530 | | const CRYPTO_IOVEC *iovec, size_t num_iovec, |
531 | | const uint8_t *nonce, size_t nonce_len, |
532 | | const uint8_t *in_tag, size_t in_tag_len, |
533 | 3.19k | const CRYPTO_IVEC *aadvec, size_t num_aadvec) { |
534 | 3.19k | Span<const CRYPTO_IOVEC> iovecs(iovec, num_iovec); |
535 | 3.19k | Span<const CRYPTO_IVEC> aadvecs(aadvec, num_aadvec); |
536 | | |
537 | 3.19k | bool ok = false; |
538 | 3.19k | Cleanup cleanup([&] { |
539 | 3.19k | if (!ok) { |
540 | | // In the event of an error, clear the output buffer so that a caller |
541 | | // that doesn't check the return value doesn't try and process bad |
542 | | // data. |
543 | 2.75k | clear_iovec(iovecs); |
544 | 2.75k | } |
545 | 3.19k | }); |
546 | | |
547 | 3.19k | if (!bssl::iovec::IsValid(iovecs) || !bssl::iovec::IsValid(aadvecs)) { |
548 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); |
549 | 0 | return 0; |
550 | 0 | } |
551 | 3.19k | if (in_tag_len > EVP_AEAD_MAX_OPEN_OVERHEAD) { |
552 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_TAG_SIZE); |
553 | 0 | return 0; |
554 | 0 | } |
555 | | |
556 | | // Enforce aliasing rules: no output may alias any input, with the one |
557 | | // exception that an iovec member's `in` and `out` pointers may be identical |
558 | | // for in-place operation. |
559 | 3.19k | if (!check_iovec_alias(iovecs, aadvecs, nullptr, 0, nonce, nonce_len, in_tag, |
560 | 3.19k | in_tag_len)) { |
561 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_OUTPUT_ALIASES_INPUT); |
562 | 0 | return 0; |
563 | 0 | } |
564 | | |
565 | 3.19k | if (!ctx->aead->openv_detached) { |
566 | | // AEADs with variable overhead may provide openv instead of openv_detached. |
567 | | // While one might call openv and then, on success, discard the result if |
568 | | // the length was wrong, this requires callers to predict the plaintext |
569 | | // length first. We do not expect callers to do this, especially in the TLS |
570 | | // CBC construction, where this length is sensitive to the Lucky 13 attack. |
571 | 0 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_CTRL_NOT_IMPLEMENTED); |
572 | 0 | return 0; |
573 | 0 | } |
574 | | |
575 | 3.19k | if (ctx->aead->openv_detached(ctx, iovecs, Span(nonce, nonce_len), |
576 | 3.19k | Span(in_tag, in_tag_len), aadvecs)) { |
577 | 436 | ok = true; |
578 | 436 | return 1; |
579 | 436 | } |
580 | | |
581 | 2.75k | return 0; |
582 | 3.19k | } |
583 | | |
584 | 12.8k | const EVP_AEAD *EVP_AEAD_CTX_aead(const EVP_AEAD_CTX *ctx) { return ctx->aead; } |
585 | | |
586 | | int EVP_AEAD_CTX_get_iv(const EVP_AEAD_CTX *ctx, const uint8_t **out_iv, |
587 | 0 | size_t *out_len) { |
588 | 0 | if (ctx->aead->get_iv == nullptr) { |
589 | 0 | OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
590 | 0 | return 0; |
591 | 0 | } |
592 | | |
593 | 0 | return ctx->aead->get_iv(ctx, out_iv, out_len); |
594 | 0 | } |
595 | | |
596 | | int EVP_AEAD_CTX_tag_len(const EVP_AEAD_CTX *ctx, size_t *out_tag_len, |
597 | 9.05k | const size_t in_len, const size_t extra_in_len) { |
598 | 9.05k | size_t tag_len; |
599 | 9.05k | if (ctx->aead->tag_len) { |
600 | 5.22k | if (in_len + extra_in_len < in_len) { |
601 | 0 | OPENSSL_PUT_ERROR(CIPHER, ERR_R_OVERFLOW); |
602 | 0 | *out_tag_len = 0; |
603 | 0 | return 0; |
604 | 0 | } |
605 | 5.22k | tag_len = ctx->aead->tag_len(ctx, in_len + extra_in_len); |
606 | 5.22k | } else { |
607 | 3.82k | tag_len = ctx->tag_len; |
608 | 3.82k | } |
609 | | |
610 | 9.05k | if (extra_in_len + tag_len < extra_in_len) { |
611 | 0 | OPENSSL_PUT_ERROR(CIPHER, ERR_R_OVERFLOW); |
612 | 0 | *out_tag_len = 0; |
613 | 0 | return 0; |
614 | 0 | } |
615 | 9.05k | *out_tag_len = extra_in_len + tag_len; |
616 | 9.05k | return 1; |
617 | 9.05k | } |