1use super::{
16 aes::{self, Counter, Overlapping, OverlappingPartialBlock, BLOCK_LEN, ZERO_BLOCK},
17 gcm,
18 overlapping::IndexError,
19 Aad, Nonce, Tag,
20};
21use crate::{
22 cpu,
23 error::{self, InputTooLongError},
24 polyfill::{slice, sliceutil::overwrite_at_start, usize_from_u64_saturated},
25};
26use core::ops::RangeFrom;
27
28#[cfg(any(
29 all(target_arch = "aarch64", target_endian = "little"),
30 all(target_arch = "arm", target_endian = "little"),
31 target_arch = "x86",
32 target_arch = "x86_64"
33))]
34use cpu::GetFeature as _;
35
36mod aarch64;
37mod aeshwclmulmovbe;
38mod vaesclmulavx2;
39
40#[derive(Clone)]
41pub(super) struct Key(DynKey);
42
43impl Key {
44 pub(super) fn new(
45 key: aes::KeyBytes,
46 cpu_features: cpu::Features,
47 ) -> Result<Self, error::Unspecified> {
48 Ok(Self(DynKey::new(key, cpu_features)?))
49 }
50}
51
52#[derive(Clone)]
53enum DynKey {
54 #[cfg(target_arch = "x86_64")]
55 VAesClMulAvx2(Combo<aes::hw::Key, gcm::vclmulavx2::Key>),
56
57 #[cfg(target_arch = "x86_64")]
58 AesHwClMulAvxMovbe(Combo<aes::hw::Key, gcm::clmulavxmovbe::Key>),
59
60 #[cfg(any(
61 all(target_arch = "aarch64", target_endian = "little"),
62 target_arch = "x86",
63 target_arch = "x86_64"
64 ))]
65 AesHwClMul(Combo<aes::hw::Key, gcm::clmul::Key>),
66
67 #[cfg(any(
68 all(target_arch = "aarch64", target_endian = "little"),
69 all(target_arch = "arm", target_endian = "little")
70 ))]
71 Simd(Combo<aes::vp::Key, gcm::neon::Key>),
72
73 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
74 Simd(Combo<aes::vp::Key, gcm::fallback::Key>),
75
76 Fallback(Combo<aes::fallback::Key, gcm::fallback::Key>),
77}
78
79impl DynKey {
80 fn new(key: aes::KeyBytes, cpu: cpu::Features) -> Result<Self, error::Unspecified> {
81 let cpu = cpu.values();
82
83 #[cfg(target_arch = "x86_64")]
84 if let Some((aes, gcm)) = cpu.get_feature() {
85 let aes_key = aes::hw::Key::new(key, aes, cpu.get_feature())?;
87 let gcm_key_value = derive_gcm_key_value(&aes_key);
88 let combo = if let Some(cpu) = cpu.get_feature() {
89 let gcm_key = gcm::vclmulavx2::Key::new(gcm_key_value, cpu);
90 Self::VAesClMulAvx2(Combo { aes_key, gcm_key })
91 } else if let Some(cpu) = cpu.get_feature() {
92 let gcm_key = gcm::clmulavxmovbe::Key::new(gcm_key_value, cpu);
93 Self::AesHwClMulAvxMovbe(Combo { aes_key, gcm_key })
94 } else {
95 let gcm_key = gcm::clmul::Key::new(gcm_key_value, gcm);
96 Self::AesHwClMul(Combo { aes_key, gcm_key })
97 };
98 return Ok(combo);
99 }
100
101 #[cfg(any(
103 all(target_arch = "aarch64", target_endian = "little"),
104 target_arch = "x86"
105 ))]
106 if let (Some(aes), Some(gcm)) = (cpu.get_feature(), cpu.get_feature()) {
107 let aes_key = aes::hw::Key::new(key, aes, cpu.get_feature())?;
108 let gcm_key_value = derive_gcm_key_value(&aes_key);
109 let gcm_key = gcm::clmul::Key::new(gcm_key_value, gcm);
110 return Ok(Self::AesHwClMul(Combo { aes_key, gcm_key }));
111 }
112
113 #[cfg(any(
114 all(target_arch = "aarch64", target_endian = "little"),
115 all(target_arch = "arm", target_endian = "little")
116 ))]
117 if let Some(cpu) = cpu.get_feature() {
118 return Self::new_neon(key, cpu);
119 }
120
121 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
122 if let Some(cpu) = cpu.get_feature() {
123 return Self::new_ssse3(key, cpu);
124 }
125
126 let _ = cpu;
127 Self::new_fallback(key)
128 }
129
130 #[cfg(any(
131 all(target_arch = "aarch64", target_endian = "little"),
132 all(target_arch = "arm", target_endian = "little")
133 ))]
134 #[cfg_attr(target_arch = "aarch64", inline(never))]
135 fn new_neon(key: aes::KeyBytes, cpu: cpu::arm::Neon) -> Result<Self, error::Unspecified> {
136 let aes_key = aes::vp::Key::new(key, cpu)?;
137 let gcm_key_value = derive_gcm_key_value(&aes_key);
138 let gcm_key = gcm::neon::Key::new(gcm_key_value, cpu);
139 Ok(Self::Simd(Combo { aes_key, gcm_key }))
140 }
141
142 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
143 #[inline(never)]
144 fn new_ssse3(
145 key: aes::KeyBytes,
146 cpu: aes::vp::RequiredCpuFeatures,
147 ) -> Result<Self, error::Unspecified> {
148 let aes_key = aes::vp::Key::new(key, cpu)?;
149 let gcm_key_value = derive_gcm_key_value(&aes_key);
150 let gcm_key = gcm::fallback::Key::new(gcm_key_value);
151 Ok(Self::Simd(Combo { aes_key, gcm_key }))
152 }
153
154 #[cfg_attr(
155 any(
156 all(target_arch = "aarch64", target_endian = "little"),
157 all(target_arch = "arm", target_endian = "little"),
158 target_arch = "x86",
159 target_arch = "x86_64",
160 ),
161 inline(never)
162 )]
163 fn new_fallback(key: aes::KeyBytes) -> Result<Self, error::Unspecified> {
164 let aes_key = aes::fallback::Key::new(key)?;
165 let gcm_key_value = derive_gcm_key_value(&aes_key);
166 let gcm_key = gcm::fallback::Key::new(gcm_key_value);
167 Ok(Self::Fallback(Combo { aes_key, gcm_key }))
168 }
169}
170
171fn derive_gcm_key_value(aes_key: &impl aes::EncryptBlock) -> gcm::KeyValue {
172 gcm::KeyValue::new(aes_key.encrypt_block(ZERO_BLOCK))
173}
174
175const CHUNK_BLOCKS: usize = 3 * 1024 / 16;
176
177#[inline(never)]
178pub(super) fn seal(
179 Key(key): &Key,
180 nonce: Nonce,
181 aad: Aad<&[u8]>,
182 in_out: &mut [u8],
183) -> Result<Tag, error::Unspecified> {
184 let mut ctr = Counter::one(nonce);
185 let tag_iv = ctr.increment();
186
187 match key {
188 #[cfg(all(target_arch = "aarch64", target_endian = "little"))]
189 DynKey::AesHwClMul(c) => {
190 seal_whole_partial(c, aad, in_out, ctr, tag_iv, aarch64::seal_whole)
191 }
192
193 #[cfg(target_arch = "x86_64")]
194 DynKey::VAesClMulAvx2(c) => seal_whole_partial(
195 c,
196 aad,
197 in_out,
198 ctr,
199 tag_iv,
200 vaesclmulavx2::seal_whole_vaes_clmul_avx2,
201 ),
202
203 #[cfg(target_arch = "x86_64")]
204 DynKey::AesHwClMulAvxMovbe(Combo { aes_key, gcm_key }) => {
205 aeshwclmulmovbe::seal(aes_key, gcm_key, ctr, tag_iv, aad, in_out)
206 }
207
208 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
209 DynKey::AesHwClMul(c) => seal_strided(c, aad, in_out, ctr, tag_iv),
210
211 #[cfg(any(
212 all(target_arch = "aarch64", target_endian = "little"),
213 all(target_arch = "arm", target_endian = "little"),
214 target_arch = "x86_64",
215 target_arch = "x86"
216 ))]
217 DynKey::Simd(c) => seal_strided(c, aad, in_out, ctr, tag_iv),
218
219 DynKey::Fallback(c) => seal_strided(c, aad, in_out, ctr, tag_iv),
220 }
221}
222
223#[cfg(any(
224 all(target_arch = "aarch64", target_endian = "little"),
225 target_arch = "x86_64"
226))]
227fn seal_whole_partial<A: aes::EncryptBlock, G: gcm::UpdateBlock>(
228 Combo { aes_key, gcm_key }: &Combo<A, G>,
229 aad: Aad<&[u8]>,
230 in_out: &mut [u8],
231 mut ctr: Counter,
232 tag_iv: aes::Iv,
233 seal_whole: impl FnOnce(&A, &mut gcm::Context<G>, &mut Counter, slice::AsChunksMut<u8, BLOCK_LEN>),
234) -> Result<Tag, error::Unspecified> {
235 let mut auth = gcm::Context::new(gcm_key, aad, in_out.len())?;
236 let (whole, remainder) = slice::as_chunks_mut(in_out);
237 seal_whole(aes_key, &mut auth, &mut ctr, whole);
238 let remainder = OverlappingPartialBlock::new(remainder.into())
239 .unwrap_or_else(|InputTooLongError { .. }| unreachable!());
240 seal_finish(aes_key, auth, remainder, ctr, tag_iv)
241}
242
243#[cfg_attr(
244 any(
245 all(target_arch = "aarch64", target_endian = "little"),
246 all(target_arch = "arm", target_endian = "little"),
247 target_arch = "x86",
248 target_arch = "x86_64"
249 ),
250 inline(never)
251)]
252#[cfg_attr(
253 any(
254 all(target_arch = "aarch64", target_endian = "little"),
255 target_arch = "x86_64"
256 ),
257 cold
258)]
259fn seal_strided<
260 A: aes::EncryptBlock + aes::EncryptCtr32,
261 G: gcm::UpdateBlock + gcm::UpdateBlocks,
262>(
263 Combo { aes_key, gcm_key }: &Combo<A, G>,
264 aad: Aad<&[u8]>,
265 in_out: &mut [u8],
266 mut ctr: Counter,
267 tag_iv: aes::Iv,
268) -> Result<Tag, error::Unspecified> {
269 let mut auth = gcm::Context::new(gcm_key, aad, in_out.len())?;
270
271 let (mut whole, remainder) = slice::as_chunks_mut(in_out);
272
273 for mut chunk in whole.chunks_mut::<CHUNK_BLOCKS>() {
274 aes_key.ctr32_encrypt_within(chunk.as_flattened_mut().into(), &mut ctr);
275 auth.update_blocks(chunk.as_ref());
276 }
277
278 let remainder = OverlappingPartialBlock::new(remainder.into())
279 .unwrap_or_else(|InputTooLongError { .. }| unreachable!());
280 seal_finish(aes_key, auth, remainder, ctr, tag_iv)
281}
282
283fn seal_finish<A: aes::EncryptBlock, G: gcm::UpdateBlock>(
284 aes_key: &A,
285 mut auth: gcm::Context<G>,
286 remainder: OverlappingPartialBlock<'_>,
287 ctr: Counter,
288 tag_iv: aes::Iv,
289) -> Result<Tag, error::Unspecified> {
290 let remainder_len = remainder.len();
291 if remainder_len > 0 {
292 let mut input = ZERO_BLOCK;
293 overwrite_at_start(&mut input, remainder.input());
294 let mut output = aes_key.encrypt_iv_xor_block(ctr.into(), input);
295 output[remainder_len..].fill(0);
296 auth.update_block(output);
297 remainder.overwrite_at_start(output);
298 }
299
300 Ok(finish(aes_key, auth, tag_iv))
301}
302
303#[inline(never)]
304pub(super) fn open(
305 Key(key): &Key,
306 nonce: Nonce,
307 aad: Aad<&[u8]>,
308 in_out_slice: &mut [u8],
309 src: RangeFrom<usize>,
310) -> Result<Tag, error::Unspecified> {
311 let mut ctr = Counter::one(nonce);
312 let tag_iv = ctr.increment();
313
314 match key {
315 #[cfg(all(target_arch = "aarch64", target_endian = "little"))]
316 DynKey::AesHwClMul(c) => {
317 open_whole_partial(c, aad, in_out_slice, src, ctr, tag_iv, aarch64::open_whole)
318 }
319
320 #[cfg(target_arch = "x86_64")]
321 DynKey::VAesClMulAvx2(c) => open_whole_partial(
322 c,
323 aad,
324 in_out_slice,
325 src,
326 ctr,
327 tag_iv,
328 vaesclmulavx2::open_whole_vaes_clmul_avx2,
329 ),
330
331 #[cfg(target_arch = "x86_64")]
332 DynKey::AesHwClMulAvxMovbe(Combo { aes_key, gcm_key }) => {
333 aeshwclmulmovbe::open(aes_key, gcm_key, ctr, tag_iv, aad, in_out_slice, src)
334 }
335
336 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
337 DynKey::AesHwClMul(c) => open_strided(c, aad, in_out_slice, src, ctr, tag_iv),
338
339 #[cfg(any(
340 all(target_arch = "aarch64", target_endian = "little"),
341 all(target_arch = "arm", target_endian = "little"),
342 target_arch = "x86_64",
343 target_arch = "x86"
344 ))]
345 DynKey::Simd(c) => open_strided(c, aad, in_out_slice, src, ctr, tag_iv),
346
347 DynKey::Fallback(c) => open_strided(c, aad, in_out_slice, src, ctr, tag_iv),
348 }
349}
350
351#[cfg(any(
352 all(target_arch = "aarch64", target_endian = "little"),
353 target_arch = "x86_64"
354))]
355fn open_whole_partial<A: aes::EncryptBlock, G: gcm::UpdateBlock>(
356 Combo { aes_key, gcm_key }: &Combo<A, G>,
357 aad: Aad<&[u8]>,
358 in_out_slice: &mut [u8],
359 src: RangeFrom<usize>,
360 mut ctr: Counter,
361 tag_iv: aes::Iv,
362 open_whole: impl FnOnce(&A, &mut gcm::Context<G>, Overlapping, &mut Counter),
363) -> Result<Tag, error::Unspecified> {
364 let in_out = Overlapping::new(in_out_slice, src.clone()).map_err(error::erase::<IndexError>)?;
365 let mut auth = gcm::Context::new(gcm_key, aad, in_out.len())?;
366
367 let remainder_len = in_out.len() % BLOCK_LEN;
368
369 let in_out_slice_len = in_out_slice.len();
370 let whole_in_out_slice = &mut in_out_slice[..(in_out_slice_len - remainder_len)];
371 let whole = Overlapping::new(whole_in_out_slice, src.clone())
372 .unwrap_or_else(|IndexError { .. }| unreachable!());
373 let whole_len = whole.len();
374 open_whole(aes_key, &mut auth, whole, &mut ctr);
375
376 let remainder = &mut in_out_slice[whole_len..];
377 let remainder =
378 Overlapping::new(remainder, src).unwrap_or_else(|IndexError { .. }| unreachable!());
379 let remainder = OverlappingPartialBlock::new(remainder)
380 .unwrap_or_else(|InputTooLongError { .. }| unreachable!());
381 open_finish(aes_key, auth, remainder, ctr, tag_iv)
382}
383
384#[cfg_attr(
385 any(
386 all(
387 any(
388 all(target_arch = "aarch64", target_endian = "little"),
389 all(target_arch = "arm", target_endian = "little")
390 ),
391 target_feature = "neon"
392 ),
393 all(
394 any(target_arch = "x86", target_arch = "x86_64"),
395 target_feature = "sse"
396 )
397 ),
398 inline(never)
399)]
400#[cfg_attr(
401 any(
402 all(target_arch = "aarch64", target_endian = "little"),
403 target_arch = "x86_64"
404 ),
405 cold
406)]
407fn open_strided<
408 A: aes::EncryptBlock + aes::EncryptCtr32,
409 G: gcm::UpdateBlock + gcm::UpdateBlocks,
410>(
411 Combo { aes_key, gcm_key }: &Combo<A, G>,
412 aad: Aad<&[u8]>,
413 in_out_slice: &mut [u8],
414 src: RangeFrom<usize>,
415 mut ctr: Counter,
416 tag_iv: aes::Iv,
417) -> Result<Tag, error::Unspecified> {
418 let in_out = Overlapping::new(in_out_slice, src.clone()).map_err(error::erase::<IndexError>)?;
419 let input = in_out.input();
420 let input_len = input.len();
421
422 let mut auth = gcm::Context::new(gcm_key, aad, input_len)?;
423
424 let remainder_len = input_len % BLOCK_LEN;
425 let whole_len = input_len - remainder_len;
426 let in_prefix_len = src.start;
427
428 {
429 let mut chunk_len = CHUNK_BLOCKS * BLOCK_LEN;
430 let mut output = 0;
431 let mut input = in_prefix_len;
432 loop {
433 if whole_len - output < chunk_len {
434 chunk_len = whole_len - output;
435 }
436
437 let ciphertext = &in_out_slice[input..][..chunk_len];
438 let (ciphertext, leftover) = slice::as_chunks(ciphertext);
439 debug_assert_eq!(leftover.len(), 0);
440 if ciphertext.is_empty() {
441 break;
442 }
443 auth.update_blocks(ciphertext);
444
445 let chunk = Overlapping::new(
446 &mut in_out_slice[output..][..(chunk_len + in_prefix_len)],
447 in_prefix_len..,
448 )
449 .map_err(error::erase::<IndexError>)?;
450 aes_key.ctr32_encrypt_within(chunk, &mut ctr);
451 output += chunk_len;
452 input += chunk_len;
453 }
454 }
455
456 let in_out = Overlapping::new(&mut in_out_slice[whole_len..], src)
457 .unwrap_or_else(|IndexError { .. }| unreachable!());
458 let in_out = OverlappingPartialBlock::new(in_out)
459 .unwrap_or_else(|InputTooLongError { .. }| unreachable!());
460
461 open_finish(aes_key, auth, in_out, ctr, tag_iv)
462}
463
464fn open_finish<A: aes::EncryptBlock, G: gcm::UpdateBlock>(
465 aes_key: &A,
466 mut auth: gcm::Context<G>,
467 remainder: OverlappingPartialBlock<'_>,
468 ctr: Counter,
469 tag_iv: aes::Iv,
470) -> Result<Tag, error::Unspecified> {
471 if remainder.len() > 0 {
472 let mut input = ZERO_BLOCK;
473 overwrite_at_start(&mut input, remainder.input());
474 auth.update_block(input);
475 remainder.overwrite_at_start(aes_key.encrypt_iv_xor_block(ctr.into(), input));
476 }
477 Ok(finish(aes_key, auth, tag_iv))
478}
479
480fn finish<A: aes::EncryptBlock, G: gcm::UpdateBlock>(
481 aes_key: &A,
482 gcm_ctx: gcm::Context<G>,
483 tag_iv: aes::Iv,
484) -> Tag {
485 gcm_ctx.pre_finish(|pre_tag| Tag(aes_key.encrypt_iv_xor_block(tag_iv, pre_tag)))
487}
488
489pub(super) const MAX_IN_OUT_LEN: usize = super::max_input_len(BLOCK_LEN, 2);
490
491const _MAX_INPUT_LEN_BOUNDED_BY_NIST: () =
499 assert!(MAX_IN_OUT_LEN == usize_from_u64_saturated(((1u64 << 39) - 256) / 8));
500
501#[derive(Copy, Clone)]
502pub(super) struct Combo<Aes, Gcm> {
503 pub(super) aes_key: Aes,
504 pub(super) gcm_key: Gcm,
505}