1// Copyright 2018-2024 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
15use self::ffi::{Block, BLOCK_LEN, ZERO_BLOCK};
16use super::{aes_gcm, Aad};
17use crate::{
18 bits::{BitLength, FromByteLen as _},
19 error::{self, InputTooLongError},
20 polyfill::{slice::AsChunks, sliceutil::overwrite_at_start, NotSend},
21};
22use cfg_if::cfg_if;
23
24pub(super) use ffi::KeyValue;
25
26cfg_if! {
27 if #[cfg(any(all(target_arch = "aarch64", target_endian = "little"), target_arch = "x86_64"))] {
28 pub(super) use self::ffi::{HTable, Xi};
29 } else {
30 use self::ffi::{HTable, Xi};
31 }
32}
33
34#[macro_use]
35mod ffi;
36
37pub(super) mod clmul;
38pub(super) mod clmulavxmovbe;
39pub(super) mod fallback;
40pub(super) mod neon;
41
42pub(super) struct Context<'key, K> {
43 Xi: Xi,
44 key: &'key K,
45 aad_len: BitLength<u64>,
46 in_out_len: BitLength<u64>,
47 _not_send: NotSend,
48}
49
50impl<'key, K: UpdateBlock> Context<'key, K> {
51 #[inline(always)]
52 pub(crate) fn new(
53 key: &'key K,
54 aad: Aad<&[u8]>,
55 in_out_len: usize,
56 ) -> Result<Self, error::Unspecified> {
57 if in_out_len > aes_gcm::MAX_IN_OUT_LEN {
58 return Err(error::Unspecified);
59 }
60 let in_out_len =
61 BitLength::from_byte_len(in_out_len).map_err(error::erase::<InputTooLongError>)?;
62 let aad_len = BitLength::from_byte_len(aad.as_ref().len())
63 .map_err(error::erase::<InputTooLongError>)?;
64
65 // NIST SP800-38D Section 5.2.1.1 says that the maximum AAD length is
66 // 2**64 - 1 bits, i.e. BitLength<u64>::MAX, so we don't need to do an
67 // explicit check here.
68
69 let mut ctx = Self {
70 Xi: Xi(ZERO_BLOCK),
71 key,
72 aad_len,
73 in_out_len,
74 _not_send: NotSend::VALUE,
75 };
76
77 for ad in aad.0.chunks(BLOCK_LEN) {
78 let mut block = ZERO_BLOCK;
79 overwrite_at_start(&mut block, ad);
80 ctx.update_block(block);
81 }
82
83 Ok(ctx)
84 }
85}
86
87#[cfg(all(
88 target_arch = "aarch64",
89 target_endian = "little",
90 target_pointer_width = "64"
91))]
92impl<K> Context<'_, K> {
93 pub(super) fn in_out_whole_block_bits(&self) -> BitLength<usize> {
94 use crate::polyfill::usize_from_u64;
95 const WHOLE_BLOCK_BITS_MASK: usize = !0b111_1111;
96 #[allow(clippy::assertions_on_constants)]
97 const _WHOLE_BLOCK_BITS_MASK_CORRECT: () =
98 assert!(WHOLE_BLOCK_BITS_MASK == !((BLOCK_LEN * 8) - 1));
99 BitLength::from_bits(usize_from_u64(self.in_out_len.as_bits()) & WHOLE_BLOCK_BITS_MASK)
100 }
101}
102
103#[cfg(all(target_arch = "aarch64", target_endian = "little"))]
104/// Access to `inner` for the integrated AES-GCM implementations only.
105impl Context<'_, clmul::Key> {
106 #[inline]
107 pub(super) fn inner(&mut self) -> (&HTable, &mut Xi) {
108 (&self.key.inner(), &mut self.Xi)
109 }
110}
111
112#[cfg(target_arch = "x86_64")]
113impl Context<'_, clmulavxmovbe::Key> {
114 /// Access to `inner` for the integrated AES-GCM implementations only.
115 #[inline]
116 pub(super) fn inner(&mut self) -> (&HTable, &mut Xi) {
117 (self.key.inner(), &mut self.Xi)
118 }
119}
120
121impl<K: UpdateBlocks> Context<'_, K> {
122 #[inline(always)]
123 pub fn update_blocks(&mut self, input: AsChunks<u8, BLOCK_LEN>) {
124 self.key.update_blocks(&mut self.Xi, input);
125 }
126}
127
128impl<K: UpdateBlock> Context<'_, K> {
129 pub fn update_block(&mut self, a: Block) {
130 self.key.update_block(&mut self.Xi, a);
131 }
132
133 #[inline(always)]
134 pub(super) fn pre_finish<F>(mut self, f: F) -> super::Tag
135 where
136 F: FnOnce(Block) -> super::Tag,
137 {
138 let mut block: [u8; 16] = [0u8; BLOCK_LEN];
139 let (alen: &mut [u8], clen: &mut [u8]) = block.split_at_mut(BLOCK_LEN / 2);
140 alen.copy_from_slice(&BitLength::<u64>::to_be_bytes(self.aad_len));
141 clen.copy_from_slice(&BitLength::<u64>::to_be_bytes(self.in_out_len));
142 self.update_block(block);
143 f(self.Xi.0)
144 }
145}
146
147pub(super) trait UpdateBlock {
148 fn update_block(&self, xi: &mut Xi, a: Block);
149}
150
151pub(super) trait UpdateBlocks {
152 fn update_blocks(&self, xi: &mut Xi, input: AsChunks<u8, BLOCK_LEN>);
153}
154