1 | // Copyright 2015-2016 Brian Smith. |
2 | // |
3 | // Permission to use, copy, modify, and/or distribute this software for any |
4 | // purpose with or without fee is hereby granted, provided that the above |
5 | // copyright notice and this permission notice appear in all copies. |
6 | // |
7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
10 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
12 | // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
13 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | |
15 | use super::{ |
16 | aes::{self, Counter, Overlapping, OverlappingPartialBlock, BLOCK_LEN, ZERO_BLOCK}, |
17 | gcm, |
18 | overlapping::IndexError, |
19 | Aad, Nonce, Tag, |
20 | }; |
21 | use crate::{ |
22 | cpu, |
23 | error::{self, InputTooLongError}, |
24 | polyfill::{slice, sliceutil::overwrite_at_start, usize_from_u64_saturated}, |
25 | }; |
26 | use core::ops::RangeFrom; |
27 | |
28 | #[cfg (target_arch = "x86_64" )] |
29 | use aes::EncryptCtr32 as _; |
30 | |
31 | #[cfg (any( |
32 | all(target_arch = "aarch64" , target_endian = "little" ), |
33 | all(target_arch = "arm" , target_endian = "little" ), |
34 | target_arch = "x86" , |
35 | target_arch = "x86_64" |
36 | ))] |
37 | use cpu::GetFeature as _; |
38 | |
39 | #[derive (Clone)] |
40 | pub(super) struct Key(DynKey); |
41 | |
42 | impl Key { |
43 | pub(super) fn new( |
44 | key: aes::KeyBytes, |
45 | cpu_features: cpu::Features, |
46 | ) -> Result<Self, error::Unspecified> { |
47 | Ok(Self(DynKey::new(key, cpu_features)?)) |
48 | } |
49 | } |
50 | |
51 | #[derive (Clone)] |
52 | enum DynKey { |
53 | #[cfg (target_arch = "x86_64" )] |
54 | AesHwClMulAvxMovbe(Combo<aes::hw::Key, gcm::clmulavxmovbe::Key>), |
55 | |
56 | #[cfg (any( |
57 | all(target_arch = "aarch64" , target_endian = "little" ), |
58 | target_arch = "x86" , |
59 | target_arch = "x86_64" |
60 | ))] |
61 | AesHwClMul(Combo<aes::hw::Key, gcm::clmul::Key>), |
62 | |
63 | #[cfg (any( |
64 | all(target_arch = "aarch64" , target_endian = "little" ), |
65 | all(target_arch = "arm" , target_endian = "little" ) |
66 | ))] |
67 | Simd(Combo<aes::vp::Key, gcm::neon::Key>), |
68 | |
69 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
70 | Simd(Combo<aes::vp::Key, gcm::fallback::Key>), |
71 | |
72 | Fallback(Combo<aes::fallback::Key, gcm::fallback::Key>), |
73 | } |
74 | |
75 | impl DynKey { |
76 | fn new(key: aes::KeyBytes, cpu: cpu::Features) -> Result<Self, error::Unspecified> { |
77 | let cpu = cpu.values(); |
78 | #[cfg (target_arch = "x86_64" )] |
79 | if let Some((aes, gcm)) = cpu.get_feature() { |
80 | let aes_key = aes::hw::Key::new(key, aes, cpu.get_feature())?; |
81 | let gcm_key_value = derive_gcm_key_value(&aes_key); |
82 | let combo = if let Some(cpu) = cpu.get_feature() { |
83 | let gcm_key = gcm::clmulavxmovbe::Key::new(gcm_key_value, cpu); |
84 | Self::AesHwClMulAvxMovbe(Combo { aes_key, gcm_key }) |
85 | } else { |
86 | let gcm_key = gcm::clmul::Key::new(gcm_key_value, gcm); |
87 | Self::AesHwClMul(Combo { aes_key, gcm_key }) |
88 | }; |
89 | return Ok(combo); |
90 | } |
91 | |
92 | // x86_64 is handled above. |
93 | #[cfg (any( |
94 | all(target_arch = "aarch64" , target_endian = "little" ), |
95 | target_arch = "x86" |
96 | ))] |
97 | if let (Some(aes), Some(gcm)) = (cpu.get_feature(), cpu.get_feature()) { |
98 | let aes_key = aes::hw::Key::new(key, aes, cpu.get_feature())?; |
99 | let gcm_key_value = derive_gcm_key_value(&aes_key); |
100 | let gcm_key = gcm::clmul::Key::new(gcm_key_value, gcm); |
101 | return Ok(Self::AesHwClMul(Combo { aes_key, gcm_key })); |
102 | } |
103 | |
104 | #[cfg (any( |
105 | all(target_arch = "aarch64" , target_endian = "little" ), |
106 | all(target_arch = "arm" , target_endian = "little" ) |
107 | ))] |
108 | if let Some(cpu) = cpu.get_feature() { |
109 | return Self::new_neon(key, cpu); |
110 | } |
111 | |
112 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
113 | if let Some(cpu) = cpu.get_feature() { |
114 | return Self::new_ssse3(key, cpu); |
115 | } |
116 | |
117 | let _ = cpu; |
118 | Self::new_fallback(key) |
119 | } |
120 | |
121 | #[cfg (any( |
122 | all(target_arch = "aarch64" , target_endian = "little" ), |
123 | all(target_arch = "arm" , target_endian = "little" ) |
124 | ))] |
125 | #[cfg_attr (target_arch = "aarch64" , inline(never))] |
126 | fn new_neon(key: aes::KeyBytes, cpu: cpu::arm::Neon) -> Result<Self, error::Unspecified> { |
127 | let aes_key = aes::vp::Key::new(key, cpu)?; |
128 | let gcm_key_value = derive_gcm_key_value(&aes_key); |
129 | let gcm_key = gcm::neon::Key::new(gcm_key_value, cpu); |
130 | Ok(Self::Simd(Combo { aes_key, gcm_key })) |
131 | } |
132 | |
133 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
134 | #[inline (never)] |
135 | fn new_ssse3( |
136 | key: aes::KeyBytes, |
137 | cpu: aes::vp::RequiredCpuFeatures, |
138 | ) -> Result<Self, error::Unspecified> { |
139 | let aes_key = aes::vp::Key::new(key, cpu)?; |
140 | let gcm_key_value = derive_gcm_key_value(&aes_key); |
141 | let gcm_key = gcm::fallback::Key::new(gcm_key_value); |
142 | Ok(Self::Simd(Combo { aes_key, gcm_key })) |
143 | } |
144 | |
145 | #[cfg_attr ( |
146 | any( |
147 | all(target_arch = "aarch64" , target_endian = "little" ), |
148 | all(target_arch = "arm" , target_endian = "little" ), |
149 | target_arch = "x86" , |
150 | target_arch = "x86_64" , |
151 | ), |
152 | inline(never) |
153 | )] |
154 | fn new_fallback(key: aes::KeyBytes) -> Result<Self, error::Unspecified> { |
155 | let aes_key = aes::fallback::Key::new(key)?; |
156 | let gcm_key_value = derive_gcm_key_value(&aes_key); |
157 | let gcm_key = gcm::fallback::Key::new(gcm_key_value); |
158 | Ok(Self::Fallback(Combo { aes_key, gcm_key })) |
159 | } |
160 | } |
161 | |
162 | fn derive_gcm_key_value(aes_key: &impl aes::EncryptBlock) -> gcm::KeyValue { |
163 | gcm::KeyValue::new(aes_key.encrypt_block(ZERO_BLOCK)) |
164 | } |
165 | |
166 | const CHUNK_BLOCKS: usize = 3 * 1024 / 16; |
167 | |
168 | #[inline (never)] |
169 | pub(super) fn seal( |
170 | Key(key: &DynKey): &Key, |
171 | nonce: Nonce, |
172 | aad: Aad<&[u8]>, |
173 | in_out: &mut [u8], |
174 | ) -> Result<Tag, error::Unspecified> { |
175 | let mut ctr = Counter::one(nonce); |
176 | let tag_iv = ctr.increment(); |
177 | |
178 | match key { |
179 | #[cfg (target_arch = "x86_64" )] |
180 | DynKey::AesHwClMulAvxMovbe(Combo { aes_key, gcm_key }) => { |
181 | use crate::c; |
182 | let mut auth = gcm::Context::new(gcm_key, aad, in_out.len())?; |
183 | let (htable, xi) = auth.inner(); |
184 | prefixed_extern! { |
185 | // `HTable` and `Xi` should be 128-bit aligned. TODO: Can we shrink `HTable`? The |
186 | // assembly says it needs just nine values in that array. |
187 | fn aesni_gcm_encrypt( |
188 | input: *const u8, |
189 | output: *mut u8, |
190 | len: c::size_t, |
191 | key: &aes::AES_KEY, |
192 | ivec: &mut Counter, |
193 | Htable: &gcm::HTable, |
194 | Xi: &mut gcm::Xi) -> c::size_t; |
195 | } |
196 | let processed = unsafe { |
197 | aesni_gcm_encrypt( |
198 | in_out.as_ptr(), |
199 | in_out.as_mut_ptr(), |
200 | in_out.len(), |
201 | aes_key.inner_less_safe(), |
202 | &mut ctr, |
203 | htable, |
204 | xi, |
205 | ) |
206 | }; |
207 | |
208 | let ramaining = match in_out.get_mut(processed..) { |
209 | Some(remaining) => remaining, |
210 | None => { |
211 | // This can't happen. If it did, then the assembly already |
212 | // caused a buffer overflow. |
213 | unreachable!() |
214 | } |
215 | }; |
216 | let (mut whole, remainder) = slice::as_chunks_mut(ramaining); |
217 | aes_key.ctr32_encrypt_within(whole.as_flattened_mut().into(), &mut ctr); |
218 | auth.update_blocks(whole.as_ref()); |
219 | let remainder = OverlappingPartialBlock::new(remainder.into()) |
220 | .unwrap_or_else(|InputTooLongError { .. }| unreachable!()); |
221 | seal_finish(aes_key, auth, remainder, ctr, tag_iv) |
222 | } |
223 | |
224 | #[cfg (all(target_arch = "aarch64" , target_endian = "little" ))] |
225 | DynKey::AesHwClMul(Combo { aes_key, gcm_key }) => { |
226 | use crate::bits::BitLength; |
227 | |
228 | let mut auth = gcm::Context::new(gcm_key, aad, in_out.len())?; |
229 | |
230 | let (mut whole, remainder) = slice::as_chunks_mut(in_out); |
231 | let whole_block_bits = auth.in_out_whole_block_bits(); |
232 | let whole_block_bits_u64: BitLength<u64> = whole_block_bits.into(); |
233 | if let Ok(whole_block_bits) = whole_block_bits_u64.try_into() { |
234 | use core::num::NonZeroU64; |
235 | |
236 | let (htable, xi) = auth.inner(); |
237 | prefixed_extern! { |
238 | fn aes_gcm_enc_kernel( |
239 | input: *const [u8; BLOCK_LEN], |
240 | in_bits: BitLength<NonZeroU64>, |
241 | output: *mut [u8; BLOCK_LEN], |
242 | Xi: &mut gcm::Xi, |
243 | ivec: &mut Counter, |
244 | key: &aes::AES_KEY, |
245 | Htable: &gcm::HTable); |
246 | } |
247 | unsafe { |
248 | aes_gcm_enc_kernel( |
249 | whole.as_ptr(), |
250 | whole_block_bits, |
251 | whole.as_mut_ptr(), |
252 | xi, |
253 | &mut ctr, |
254 | aes_key.inner_less_safe(), |
255 | htable, |
256 | ) |
257 | } |
258 | } |
259 | let remainder = OverlappingPartialBlock::new(remainder.into()) |
260 | .unwrap_or_else(|InputTooLongError { .. }| unreachable!()); |
261 | seal_finish(aes_key, auth, remainder, ctr, tag_iv) |
262 | } |
263 | |
264 | #[cfg (any(target_arch = "x86_64" , target_arch = "x86" ))] |
265 | DynKey::AesHwClMul(c) => seal_strided(c, aad, in_out, ctr, tag_iv), |
266 | |
267 | #[cfg (any( |
268 | all(target_arch = "aarch64" , target_endian = "little" ), |
269 | all(target_arch = "arm" , target_endian = "little" ), |
270 | target_arch = "x86_64" , |
271 | target_arch = "x86" |
272 | ))] |
273 | DynKey::Simd(c) => seal_strided(c, aad, in_out, ctr, tag_iv), |
274 | |
275 | DynKey::Fallback(c) => seal_strided(c, aad, in_out, ctr, tag_iv), |
276 | } |
277 | } |
278 | |
279 | #[cfg_attr ( |
280 | any( |
281 | all(target_arch = "aarch64" , target_endian = "little" ), |
282 | all(target_arch = "arm" , target_endian = "little" ), |
283 | target_arch = "x86" , |
284 | target_arch = "x86_64" |
285 | ), |
286 | inline(never) |
287 | )] |
288 | #[cfg_attr ( |
289 | any( |
290 | all(target_arch = "aarch64" , target_endian = "little" ), |
291 | target_arch = "x86_64" |
292 | ), |
293 | cold |
294 | )] |
295 | fn seal_strided< |
296 | A: aes::EncryptBlock + aes::EncryptCtr32, |
297 | G: gcm::UpdateBlock + gcm::UpdateBlocks, |
298 | >( |
299 | Combo { aes_key: &A, gcm_key: &G }: &Combo<A, G>, |
300 | aad: Aad<&[u8]>, |
301 | in_out: &mut [u8], |
302 | mut ctr: Counter, |
303 | tag_iv: aes::Iv, |
304 | ) -> Result<Tag, error::Unspecified> { |
305 | let mut auth: Context<'_, G> = gcm::Context::new(gcm_key, aad, in_out.len())?; |
306 | |
307 | let (mut whole: AsChunksMut<'_, u8, 16>, remainder: &mut [u8]) = slice::as_chunks_mut(slice:in_out); |
308 | |
309 | for mut chunk: AsChunksMut<'_, u8, 16> in whole.chunks_mut::<CHUNK_BLOCKS>() { |
310 | aes_key.ctr32_encrypt_within(in_out:chunk.as_flattened_mut().into(), &mut ctr); |
311 | auth.update_blocks(input:chunk.as_ref()); |
312 | } |
313 | |
314 | let remainder: PartialBlock<'_, u8, 16> = OverlappingPartialBlock::new(remainder.into()) |
315 | .unwrap_or_else(|InputTooLongError { .. }| unreachable!()); |
316 | seal_finish(aes_key, auth, remainder, ctr, tag_iv) |
317 | } |
318 | |
319 | fn seal_finish<A: aes::EncryptBlock, G: gcm::UpdateBlock>( |
320 | aes_key: &A, |
321 | mut auth: gcm::Context<G>, |
322 | remainder: OverlappingPartialBlock<'_>, |
323 | ctr: Counter, |
324 | tag_iv: aes::Iv, |
325 | ) -> Result<Tag, error::Unspecified> { |
326 | let remainder_len: usize = remainder.len(); |
327 | if remainder_len > 0 { |
328 | let mut input: [u8; 16] = ZERO_BLOCK; |
329 | overwrite_at_start(&mut input, b:remainder.input()); |
330 | let mut output: [u8; 16] = aes_key.encrypt_iv_xor_block(iv:ctr.into(), block:input); |
331 | output[remainder_len..].fill(0); |
332 | auth.update_block(output); |
333 | remainder.overwrite_at_start(padded:output); |
334 | } |
335 | |
336 | Ok(finish(aes_key, gcm_ctx:auth, tag_iv)) |
337 | } |
338 | |
339 | #[inline (never)] |
340 | pub(super) fn open( |
341 | Key(key: &DynKey): &Key, |
342 | nonce: Nonce, |
343 | aad: Aad<&[u8]>, |
344 | in_out_slice: &mut [u8], |
345 | src: RangeFrom<usize>, |
346 | ) -> Result<Tag, error::Unspecified> { |
347 | #[cfg (any( |
348 | all(target_arch = "aarch64" , target_endian = "little" ), |
349 | target_arch = "x86_64" |
350 | ))] |
351 | let in_out = Overlapping::new(in_out_slice, src.clone()).map_err(error::erase::<IndexError>)?; |
352 | |
353 | let mut ctr = Counter::one(nonce); |
354 | let tag_iv = ctr.increment(); |
355 | |
356 | match key { |
357 | #[cfg (target_arch = "x86_64" )] |
358 | DynKey::AesHwClMulAvxMovbe(Combo { aes_key, gcm_key }) => { |
359 | use crate::c; |
360 | |
361 | prefixed_extern! { |
362 | // `HTable` and `Xi` should be 128-bit aligned. TODO: Can we shrink `HTable`? The |
363 | // assembly says it needs just nine values in that array. |
364 | fn aesni_gcm_decrypt( |
365 | input: *const u8, |
366 | output: *mut u8, |
367 | len: c::size_t, |
368 | key: &aes::AES_KEY, |
369 | ivec: &mut Counter, |
370 | Htable: &gcm::HTable, |
371 | Xi: &mut gcm::Xi) -> c::size_t; |
372 | } |
373 | |
374 | let mut auth = gcm::Context::new(gcm_key, aad, in_out.len())?; |
375 | let processed = in_out.with_input_output_len(|input, output, len| { |
376 | let (htable, xi) = auth.inner(); |
377 | unsafe { |
378 | aesni_gcm_decrypt( |
379 | input, |
380 | output, |
381 | len, |
382 | aes_key.inner_less_safe(), |
383 | &mut ctr, |
384 | htable, |
385 | xi, |
386 | ) |
387 | } |
388 | }); |
389 | let in_out_slice = in_out_slice.get_mut(processed..).unwrap_or_else(|| { |
390 | // This can't happen. If it did, then the assembly already |
391 | // caused a buffer overflow. |
392 | unreachable!() |
393 | }); |
394 | // Authenticate any remaining whole blocks. |
395 | let in_out = Overlapping::new(in_out_slice, src.clone()).unwrap_or_else( |
396 | |IndexError { .. }| { |
397 | // This can't happen. If it did, then the assembly already |
398 | // overwrote part of the remaining input. |
399 | unreachable!() |
400 | }, |
401 | ); |
402 | let (whole, _) = slice::as_chunks(in_out.input()); |
403 | auth.update_blocks(whole); |
404 | |
405 | let whole_len = whole.as_flattened().len(); |
406 | |
407 | // Decrypt any remaining whole blocks. |
408 | let whole = Overlapping::new(&mut in_out_slice[..(src.start + whole_len)], src.clone()) |
409 | .map_err(error::erase::<IndexError>)?; |
410 | aes_key.ctr32_encrypt_within(whole, &mut ctr); |
411 | |
412 | let in_out_slice = match in_out_slice.get_mut(whole_len..) { |
413 | Some(partial) => partial, |
414 | None => unreachable!(), |
415 | }; |
416 | let in_out = Overlapping::new(in_out_slice, src) |
417 | .unwrap_or_else(|IndexError { .. }| unreachable!()); |
418 | let in_out = OverlappingPartialBlock::new(in_out) |
419 | .unwrap_or_else(|InputTooLongError { .. }| unreachable!()); |
420 | open_finish(aes_key, auth, in_out, ctr, tag_iv) |
421 | } |
422 | |
423 | #[cfg (all(target_arch = "aarch64" , target_endian = "little" ))] |
424 | DynKey::AesHwClMul(Combo { aes_key, gcm_key }) => { |
425 | use crate::bits::BitLength; |
426 | |
427 | let mut auth = gcm::Context::new(gcm_key, aad, in_out.len())?; |
428 | let remainder_len = in_out.len() % BLOCK_LEN; |
429 | let whole_len = in_out.len() - remainder_len; |
430 | in_out.with_input_output_len(|input, output, _len| { |
431 | let whole_block_bits = auth.in_out_whole_block_bits(); |
432 | let whole_block_bits_u64: BitLength<u64> = whole_block_bits.into(); |
433 | if let Ok(whole_block_bits) = whole_block_bits_u64.try_into() { |
434 | use core::num::NonZeroU64; |
435 | |
436 | let (htable, xi) = auth.inner(); |
437 | prefixed_extern! { |
438 | fn aes_gcm_dec_kernel( |
439 | input: *const u8, |
440 | in_bits: BitLength<NonZeroU64>, |
441 | output: *mut u8, |
442 | Xi: &mut gcm::Xi, |
443 | ivec: &mut Counter, |
444 | key: &aes::AES_KEY, |
445 | Htable: &gcm::HTable); |
446 | } |
447 | |
448 | unsafe { |
449 | aes_gcm_dec_kernel( |
450 | input, |
451 | whole_block_bits, |
452 | output, |
453 | xi, |
454 | &mut ctr, |
455 | aes_key.inner_less_safe(), |
456 | htable, |
457 | ) |
458 | } |
459 | } |
460 | }); |
461 | let remainder = &mut in_out_slice[whole_len..]; |
462 | let remainder = |
463 | Overlapping::new(remainder, src).unwrap_or_else(|IndexError { .. }| unreachable!()); |
464 | let remainder = OverlappingPartialBlock::new(remainder) |
465 | .unwrap_or_else(|InputTooLongError { .. }| unreachable!()); |
466 | open_finish(aes_key, auth, remainder, ctr, tag_iv) |
467 | } |
468 | |
469 | #[cfg (any(target_arch = "x86_64" , target_arch = "x86" ))] |
470 | DynKey::AesHwClMul(c) => open_strided(c, aad, in_out_slice, src, ctr, tag_iv), |
471 | |
472 | #[cfg (any( |
473 | all(target_arch = "aarch64" , target_endian = "little" ), |
474 | all(target_arch = "arm" , target_endian = "little" ), |
475 | target_arch = "x86_64" , |
476 | target_arch = "x86" |
477 | ))] |
478 | DynKey::Simd(c) => open_strided(c, aad, in_out_slice, src, ctr, tag_iv), |
479 | |
480 | DynKey::Fallback(c) => open_strided(c, aad, in_out_slice, src, ctr, tag_iv), |
481 | } |
482 | } |
483 | |
484 | #[cfg_attr ( |
485 | any( |
486 | all( |
487 | any( |
488 | all(target_arch = "aarch64" , target_endian = "little" ), |
489 | all(target_arch = "arm" , target_endian = "little" ) |
490 | ), |
491 | target_feature = "neon" |
492 | ), |
493 | all( |
494 | any(target_arch = "x86" , target_arch = "x86_64" ), |
495 | target_feature = "sse" |
496 | ) |
497 | ), |
498 | inline(never) |
499 | )] |
500 | #[cfg_attr ( |
501 | any( |
502 | all(target_arch = "aarch64" , target_endian = "little" ), |
503 | target_arch = "x86_64" |
504 | ), |
505 | cold |
506 | )] |
507 | fn open_strided< |
508 | A: aes::EncryptBlock + aes::EncryptCtr32, |
509 | G: gcm::UpdateBlock + gcm::UpdateBlocks, |
510 | >( |
511 | Combo { aes_key: &A, gcm_key: &G }: &Combo<A, G>, |
512 | aad: Aad<&[u8]>, |
513 | in_out_slice: &mut [u8], |
514 | src: RangeFrom<usize>, |
515 | mut ctr: Counter, |
516 | tag_iv: aes::Iv, |
517 | ) -> Result<Tag, error::Unspecified> { |
518 | let in_out = Overlapping::new(in_out_slice, src.clone()).map_err(error::erase::<IndexError>)?; |
519 | let input = in_out.input(); |
520 | let input_len = input.len(); |
521 | |
522 | let mut auth = gcm::Context::new(gcm_key, aad, input_len)?; |
523 | |
524 | let remainder_len = input_len % BLOCK_LEN; |
525 | let whole_len = input_len - remainder_len; |
526 | let in_prefix_len = src.start; |
527 | |
528 | { |
529 | let mut chunk_len = CHUNK_BLOCKS * BLOCK_LEN; |
530 | let mut output = 0; |
531 | let mut input = in_prefix_len; |
532 | loop { |
533 | if whole_len - output < chunk_len { |
534 | chunk_len = whole_len - output; |
535 | } |
536 | |
537 | let ciphertext = &in_out_slice[input..][..chunk_len]; |
538 | let (ciphertext, leftover) = slice::as_chunks(ciphertext); |
539 | debug_assert_eq!(leftover.len(), 0); |
540 | if ciphertext.is_empty() { |
541 | break; |
542 | } |
543 | auth.update_blocks(ciphertext); |
544 | |
545 | let chunk = Overlapping::new( |
546 | &mut in_out_slice[output..][..(chunk_len + in_prefix_len)], |
547 | in_prefix_len.., |
548 | ) |
549 | .map_err(error::erase::<IndexError>)?; |
550 | aes_key.ctr32_encrypt_within(chunk, &mut ctr); |
551 | output += chunk_len; |
552 | input += chunk_len; |
553 | } |
554 | } |
555 | |
556 | let in_out = Overlapping::new(&mut in_out_slice[whole_len..], src) |
557 | .unwrap_or_else(|IndexError { .. }| unreachable!()); |
558 | let in_out = OverlappingPartialBlock::new(in_out) |
559 | .unwrap_or_else(|InputTooLongError { .. }| unreachable!()); |
560 | |
561 | open_finish(aes_key, auth, in_out, ctr, tag_iv) |
562 | } |
563 | |
564 | fn open_finish<A: aes::EncryptBlock, G: gcm::UpdateBlock>( |
565 | aes_key: &A, |
566 | mut auth: gcm::Context<G>, |
567 | remainder: OverlappingPartialBlock<'_>, |
568 | ctr: Counter, |
569 | tag_iv: aes::Iv, |
570 | ) -> Result<Tag, error::Unspecified> { |
571 | if remainder.len() > 0 { |
572 | let mut input: [u8; 16] = ZERO_BLOCK; |
573 | overwrite_at_start(&mut input, b:remainder.input()); |
574 | auth.update_block(input); |
575 | remainder.overwrite_at_start(padded:aes_key.encrypt_iv_xor_block(iv:ctr.into(), block:input)); |
576 | } |
577 | Ok(finish(aes_key, gcm_ctx:auth, tag_iv)) |
578 | } |
579 | |
580 | fn finish<A: aes::EncryptBlock, G: gcm::UpdateBlock>( |
581 | aes_key: &A, |
582 | gcm_ctx: gcm::Context<G>, |
583 | tag_iv: aes::Iv, |
584 | ) -> Tag { |
585 | // Finalize the tag and return it. |
586 | gcm_ctx.pre_finish(|pre_tag: [u8; BLOCK_LEN]| Tag(aes_key.encrypt_iv_xor_block(tag_iv, block:pre_tag))) |
587 | } |
588 | |
589 | pub(super) const MAX_IN_OUT_LEN: usize = super::max_input_len(BLOCK_LEN, overhead_blocks_per_nonce:2); |
590 | |
591 | // [NIST SP800-38D] Section 5.2.1.1. Note that [RFC 5116 Section 5.1] and |
592 | // [RFC 5116 Section 5.2] have an off-by-one error in `P_MAX`. |
593 | // |
594 | // [NIST SP800-38D]: |
595 | // http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf |
596 | // [RFC 5116 Section 5.1]: https://tools.ietf.org/html/rfc5116#section-5.1 |
597 | // [RFC 5116 Section 5.2]: https://tools.ietf.org/html/rfc5116#section-5.2 |
598 | const _MAX_INPUT_LEN_BOUNDED_BY_NIST: () = |
599 | assert!(MAX_IN_OUT_LEN == usize_from_u64_saturated(((1u64 << 39) - 256) / 8)); |
600 | |
601 | #[derive (Copy, Clone)] |
602 | pub(super) struct Combo<Aes, Gcm> { |
603 | pub(super) aes_key: Aes, |
604 | pub(super) gcm_key: Gcm, |
605 | } |
606 | |