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