1use crate::{
16 bb, cpu,
17 error::{self, InputTooLongError},
18 hkdf,
19};
20use core::ops::RangeFrom;
21
22use super::{
23 aes, aes_gcm, chacha20_poly1305,
24 nonce::{Nonce, NONCE_LEN},
25 overlapping::{IndexError, Overlapping},
26 Aad, KeyInner, Tag, TAG_LEN,
27};
28
29impl hkdf::KeyType for &'static Algorithm {
30 #[inline]
31 fn len(&self) -> usize {
32 self.key_len()
33 }
34}
35
36pub struct Algorithm {
38 init: fn(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>,
39
40 seal: fn(
41 key: &KeyInner,
42 nonce: Nonce,
43 aad: Aad<&[u8]>,
44 in_out: &mut [u8],
45 cpu_features: cpu::Features,
46 ) -> Result<Tag, error::Unspecified>,
47 open: fn(
48 key: &KeyInner,
49 nonce: Nonce,
50 aad: Aad<&[u8]>,
51 in_out: &mut [u8],
52 src: RangeFrom<usize>,
53 cpu_features: cpu::Features,
54 ) -> Result<Tag, error::Unspecified>,
55
56 key_len: usize,
57 id: AlgorithmID,
58}
59
60impl Algorithm {
61 #[inline(always)]
63 pub fn key_len(&self) -> usize {
64 self.key_len
65 }
66
67 #[inline(always)]
71 pub fn tag_len(&self) -> usize {
72 TAG_LEN
73 }
74
75 #[inline(always)]
77 pub fn nonce_len(&self) -> usize {
78 NONCE_LEN
79 }
80
81 pub(super) fn new_key(
82 &self,
83 key_bytes: &[u8],
84 cpu_features: cpu::Features,
85 ) -> Result<KeyInner, error::Unspecified> {
86 (self.init)(key_bytes, cpu_features)
87 }
88
89 pub(super) fn open_within<'io>(
90 &self,
91 key: &KeyInner,
92 nonce: Nonce,
93 aad: Aad<&[u8]>,
94 received_tag: Tag,
95 in_out: &'io mut [u8],
96 src: RangeFrom<usize>,
97 cpu_features: cpu::Features,
98 ) -> Result<&'io mut [u8], error::Unspecified> {
99 let ciphertext_len = in_out.get(src.clone()).ok_or(error::Unspecified)?.len();
100
101 let Tag(calculated_tag) = (self.open)(key, nonce, aad, in_out, src, cpu_features)?;
102
103 if bb::verify_slices_are_equal(calculated_tag.as_ref(), received_tag.as_ref()).is_err() {
104 for b in &mut in_out[..ciphertext_len] {
109 *b = 0;
110 }
111 return Err(error::Unspecified);
112 }
113
114 Ok(&mut in_out[..ciphertext_len])
116 }
117
118 #[inline]
119 pub(super) fn seal(
120 &self,
121 key: &KeyInner,
122 nonce: Nonce,
123 aad: Aad<&[u8]>,
124 in_out: &mut [u8],
125 cpu_features: cpu::Features,
126 ) -> Result<Tag, error::Unspecified> {
127 (self.seal)(key, nonce, aad, in_out, cpu_features)
128 }
129}
130
131derive_debug_via_id!(Algorithm);
132
133#[derive(Debug, Eq, PartialEq)]
134pub(super) enum AlgorithmID {
135 AES_128_GCM,
136 AES_256_GCM,
137 CHACHA20_POLY1305,
138}
139
140impl PartialEq for Algorithm {
141 fn eq(&self, other: &Self) -> bool {
142 self.id == other.id
143 }
144}
145
146impl Eq for Algorithm {}
147
148pub static AES_128_GCM: Algorithm = Algorithm {
150 key_len: aes::AES_128_KEY_LEN,
151 init: aes_gcm_init_128,
152 seal: aes_gcm_seal,
153 open: aes_gcm_open,
154 id: AlgorithmID::AES_128_GCM,
155};
156
157pub static AES_256_GCM: Algorithm = Algorithm {
159 key_len: aes::AES_256_KEY_LEN,
160 init: aes_gcm_init_256,
161 seal: aes_gcm_seal,
162 open: aes_gcm_open,
163 id: AlgorithmID::AES_256_GCM,
164};
165
166fn aes_gcm_init_128(
167 key: &[u8],
168 cpu_features: cpu::Features,
169) -> Result<KeyInner, error::Unspecified> {
170 let key = key.try_into().map_err(|_| error::Unspecified)?;
171 Ok(KeyInner::AesGcm(aes_gcm::Key::new(
172 aes::KeyBytes::AES_128(key),
173 cpu_features,
174 )?))
175}
176
177fn aes_gcm_init_256(
178 key: &[u8],
179 cpu_features: cpu::Features,
180) -> Result<KeyInner, error::Unspecified> {
181 let key = key.try_into().map_err(|_| error::Unspecified)?;
182 Ok(KeyInner::AesGcm(aes_gcm::Key::new(
183 aes::KeyBytes::AES_256(key),
184 cpu_features,
185 )?))
186}
187
188fn aes_gcm_seal(
189 key: &KeyInner,
190 nonce: Nonce,
191 aad: Aad<&[u8]>,
192 in_out: &mut [u8],
193 _cpu_features: cpu::Features,
194) -> Result<Tag, error::Unspecified> {
195 let key = match key {
196 KeyInner::AesGcm(key) => key,
197 _ => unreachable!(),
198 };
199 aes_gcm::seal(key, nonce, aad, in_out)
200}
201
202pub(super) fn aes_gcm_open(
203 key: &KeyInner,
204 nonce: Nonce,
205 aad: Aad<&[u8]>,
206 in_out: &mut [u8],
207 src: RangeFrom<usize>,
208 _cpu_features: cpu::Features,
209) -> Result<Tag, error::Unspecified> {
210 let key = match key {
211 KeyInner::AesGcm(key) => key,
212 _ => unreachable!(),
213 };
214 aes_gcm::open(key, nonce, aad, in_out, src)
215}
216
217pub static CHACHA20_POLY1305: Algorithm = Algorithm {
223 key_len: chacha20_poly1305::KEY_LEN,
224 init: chacha20_poly1305_init,
225 seal: chacha20_poly1305_seal,
226 open: chacha20_poly1305_open,
227 id: AlgorithmID::CHACHA20_POLY1305,
228};
229
230fn chacha20_poly1305_init(
232 key: &[u8],
233 _cpu_features: cpu::Features,
234) -> Result<KeyInner, error::Unspecified> {
235 let key: [u8; chacha20_poly1305::KEY_LEN] = key.try_into()?;
236 Ok(KeyInner::ChaCha20Poly1305(chacha20_poly1305::Key::new(key)))
237}
238
239fn chacha20_poly1305_seal(
240 key: &KeyInner,
241 nonce: Nonce,
242 aad: Aad<&[u8]>,
243 in_out: &mut [u8],
244 cpu_features: cpu::Features,
245) -> Result<Tag, error::Unspecified> {
246 let key = match key {
247 KeyInner::ChaCha20Poly1305(key) => key,
248 _ => unreachable!(),
249 };
250 chacha20_poly1305::seal(key, nonce, aad, in_out, cpu_features)
251 .map_err(error::erase::<InputTooLongError>)
252}
253
254fn chacha20_poly1305_open(
255 key: &KeyInner,
256 nonce: Nonce,
257 aad: Aad<&[u8]>,
258 in_out: &mut [u8],
259 src: RangeFrom<usize>,
260 cpu_features: cpu::Features,
261) -> Result<Tag, error::Unspecified> {
262 let key = match key {
263 KeyInner::ChaCha20Poly1305(key) => key,
264 _ => unreachable!(),
265 };
266 let in_out = Overlapping::new(in_out, src).map_err(error::erase::<IndexError>)?;
267 chacha20_poly1305::open(key, nonce, aad, in_out, cpu_features)
268 .map_err(error::erase::<InputTooLongError>)
269}