1#[cfg(aes_ni)]
15use crate::hacl::aesgcm;
16use crate::hacl::chacha20_poly1305;
17
18use libcrux_platform::{aes_ni_support, simd128_support, simd256_support};
19
20#[derive(Debug, PartialEq, Eq, Clone, Copy)]
22pub enum Error {
23 UnsupportedAlgorithm,
24 EncryptionError,
25 InvalidKey,
26 DecryptionFailed,
27 InvalidIv,
28 InvalidTag,
29}
30
31#[derive(Clone, Copy, PartialEq, Debug)]
33#[repr(u32)]
34pub enum Algorithm {
35 Aes128Gcm = 1,
37
38 Aes256Gcm = 2,
40
41 Chacha20Poly1305 = 3,
43}
44
45impl From<u8> for Algorithm {
46 fn from(v: u8) -> Algorithm {
47 match v {
48 0 => Algorithm::Aes128Gcm,
49 1 => Algorithm::Aes256Gcm,
50 2 => Algorithm::Chacha20Poly1305,
51 _ => panic!("Unknown AEAD mode {}", v),
52 }
53 }
54}
55
56impl Algorithm {
57 #[inline]
59 pub const fn key_size(self) -> usize {
60 match self {
61 Algorithm::Aes128Gcm => 16,
62 Algorithm::Aes256Gcm => 32,
63 Algorithm::Chacha20Poly1305 => 32,
64 }
65 }
66
67 #[inline]
69 pub const fn tag_size(self) -> usize {
70 match self {
71 Algorithm::Aes128Gcm => 16,
72 Algorithm::Aes256Gcm => 16,
73 Algorithm::Chacha20Poly1305 => 16,
74 }
75 }
76
77 #[inline]
79 pub const fn nonce_size(self) -> usize {
80 match self {
81 Algorithm::Aes128Gcm => 12,
82 Algorithm::Aes256Gcm => 12,
83 Algorithm::Chacha20Poly1305 => 12,
84 }
85 }
86
87 #[inline]
89 fn supported(self) -> Result<(), Error> {
90 if matches!(self, Algorithm::Aes128Gcm | Algorithm::Aes256Gcm) && !aes_ni_support() {
91 Err(Error::UnsupportedAlgorithm)
92 } else {
93 Ok(())
94 }
95 }
96}
97
98#[derive(Default)]
99pub struct Aes128Key(pub [u8; Algorithm::key_size(Algorithm::Aes128Gcm)]);
100
101#[derive(Default)]
102pub struct Aes256Key(pub [u8; Algorithm::key_size(Algorithm::Aes256Gcm)]);
103
104#[derive(Default)]
105pub struct Chacha20Key(pub [u8; Algorithm::key_size(Algorithm::Chacha20Poly1305)]);
106
107mod keygen {
108 use super::*;
109 use rand::{CryptoRng, RngCore};
110
111 macro_rules! impl_key_gen {
112 ($name:ident) => {
113 impl $name {
114 pub fn generate(rng: &mut (impl RngCore + CryptoRng)) -> Self {
115 let mut k = Self::default();
116 rng.fill_bytes(&mut k.0);
117 k
118 }
119 }
120 };
121 }
122 impl_key_gen!(Aes128Key);
123 impl_key_gen!(Aes256Key);
124 impl_key_gen!(Chacha20Key);
125
126 impl Key {
127 pub fn generate(alg: Algorithm, rng: &mut (impl RngCore + CryptoRng)) -> Self {
128 match alg {
129 Algorithm::Aes128Gcm => Self::Aes128(Aes128Key::generate(rng)),
130 Algorithm::Aes256Gcm => Self::Aes256(Aes256Key::generate(rng)),
131 Algorithm::Chacha20Poly1305 => Self::Chacha20Poly1305(Chacha20Key::generate(rng)),
132 }
133 }
134 }
135
136 impl Iv {
137 pub fn generate(rng: &mut (impl RngCore + CryptoRng)) -> Self {
139 let mut n = Self::default();
140 rng.fill_bytes(&mut n.0);
141 n
142 }
143
144 pub fn new(iv: impl AsRef<[u8]>) -> Result<Self, Error> {
146 Ok(Self(iv.as_ref().try_into().map_err(|_| Error::InvalidIv)?))
147 }
148 }
149}
150
151pub enum Key {
153 Aes128(Aes128Key),
154 Aes256(Aes256Key),
155 Chacha20Poly1305(Chacha20Key),
156}
157
158impl Key {
159 pub fn from_bytes(alg: Algorithm, bytes: Vec<u8>) -> Result<Self, Error> {
161 alg.supported()?;
162
163 Ok(match alg {
164 Algorithm::Aes128Gcm => {
165 Self::Aes128(Aes128Key(bytes.try_into().map_err(|_| Error::InvalidKey)?))
166 }
167 Algorithm::Aes256Gcm => {
168 Self::Aes256(Aes256Key(bytes.try_into().map_err(|_| Error::InvalidKey)?))
169 }
170 Algorithm::Chacha20Poly1305 => Self::Chacha20Poly1305(Chacha20Key(
171 bytes.try_into().map_err(|_| Error::InvalidKey)?,
172 )),
173 })
174 }
175
176 pub fn from_slice(alg: Algorithm, bytes: impl AsRef<[u8]>) -> Result<Self, Error> {
178 alg.supported()?;
179
180 Ok(match alg {
181 Algorithm::Aes128Gcm => Self::Aes128(Aes128Key(
182 bytes.as_ref().try_into().map_err(|_| Error::InvalidKey)?,
183 )),
184 Algorithm::Aes256Gcm => Self::Aes256(Aes256Key(
185 bytes.as_ref().try_into().map_err(|_| Error::InvalidKey)?,
186 )),
187 Algorithm::Chacha20Poly1305 => Self::Chacha20Poly1305(Chacha20Key(
188 bytes.as_ref().try_into().map_err(|_| Error::InvalidKey)?,
189 )),
190 })
191 }
192}
193
194#[derive(Default, PartialEq, Eq, Debug)]
195pub struct Tag([u8; 16]);
196
197impl Tag {
198 pub fn from_slice(t: impl AsRef<[u8]>) -> Result<Self, Error> {
200 Ok(Self(t.as_ref().try_into().map_err(|_| Error::InvalidTag)?))
201 }
202}
203
204impl From<[u8; 16]> for Tag {
205 fn from(value: [u8; 16]) -> Self {
206 Self(value)
207 }
208}
209
210impl AsRef<[u8]> for Tag {
211 fn as_ref(&self) -> &[u8] {
212 &self.0
213 }
214}
215
216impl AsMut<[u8]> for Tag {
217 fn as_mut(&mut self) -> &mut [u8] {
218 &mut self.0
219 }
220}
221
222#[derive(Default)]
223pub struct Iv(pub [u8; 12]);
224
225#[cfg(simd256)]
226fn encrypt_256(key: &Chacha20Key, msg_ctxt: &mut [u8], iv: Iv, aad: &[u8]) -> Tag {
227 chacha20_poly1305::simd256::encrypt(&key.0, msg_ctxt, iv.0, aad).into()
228}
229
230#[cfg(not(simd256))]
233fn encrypt_256(key: &Chacha20Key, msg_ctxt: &mut [u8], iv: Iv, aad: &[u8]) -> Tag {
234 encrypt_128(key, msg_ctxt, iv, aad)
235}
236
237#[cfg(simd128)]
238fn encrypt_128(key: &Chacha20Key, msg_ctxt: &mut [u8], iv: Iv, aad: &[u8]) -> Tag {
239 chacha20_poly1305::simd128::encrypt(&key.0, msg_ctxt, iv.0, aad).into()
240}
241
242#[cfg(not(simd128))]
245fn encrypt_128(key: &Chacha20Key, msg_ctxt: &mut [u8], iv: Iv, aad: &[u8]) -> Tag {
246 encrypt_32(key, msg_ctxt, iv, aad)
247}
248
249fn encrypt_32(key: &Chacha20Key, msg_ctxt: &mut [u8], iv: Iv, aad: &[u8]) -> Tag {
250 chacha20_poly1305::encrypt(&key.0, msg_ctxt, iv.0, aad).into()
251}
252
253#[cfg(simd256)]
254fn decrypt_256(
255 key: &Chacha20Key,
256 ctxt_msg: &mut [u8],
257 iv: Iv,
258 aad: &[u8],
259 tag: &Tag,
260) -> Result<(), Error> {
261 chacha20_poly1305::simd256::decrypt(&key.0, ctxt_msg, iv.0, aad, &tag.0)
262 .map_err(|_| Error::DecryptionFailed)
263}
264
265#[cfg(not(simd256))]
268fn decrypt_256(
269 key: &Chacha20Key,
270 ctxt_msg: &mut [u8],
271 iv: Iv,
272 aad: &[u8],
273 tag: &Tag,
274) -> Result<(), Error> {
275 decrypt_128(key, ctxt_msg, iv, aad, tag)
276}
277
278#[cfg(simd128)]
279fn decrypt_128(
280 key: &Chacha20Key,
281 ctxt_msg: &mut [u8],
282 iv: Iv,
283 aad: &[u8],
284 tag: &Tag,
285) -> Result<(), Error> {
286 chacha20_poly1305::simd128::decrypt(&key.0, ctxt_msg, iv.0, aad, &tag.0)
287 .map_err(|_| Error::DecryptionFailed)
288}
289
290#[cfg(not(simd128))]
293fn decrypt_128(
294 key: &Chacha20Key,
295 ctxt_msg: &mut [u8],
296 iv: Iv,
297 aad: &[u8],
298 tag: &Tag,
299) -> Result<(), Error> {
300 decrypt_32(key, ctxt_msg, iv, aad, tag)
301}
302
303fn decrypt_32(
304 key: &Chacha20Key,
305 ctxt_msg: &mut [u8],
306 iv: Iv,
307 aad: &[u8],
308 tag: &Tag,
309) -> Result<(), Error> {
310 chacha20_poly1305::decrypt(&key.0, ctxt_msg, iv.0, aad, &tag.0)
311 .map_err(|_| Error::DecryptionFailed)
312}
313
314#[cfg(aes_ni)]
315fn aes_encrypt_128(key: &Aes128Key, msg_ctxt: &mut [u8], iv: Iv, aad: &[u8]) -> Result<Tag, Error> {
316 aesgcm::encrypt_128(&key.0, msg_ctxt, iv.0, aad)
317 .map_err(|e| match e {
318 aesgcm::Error::UnsupportedHardware => Error::UnsupportedAlgorithm,
319 _ => Error::EncryptionError,
320 })
321 .map(|t| t.into())
322}
323
324#[cfg(not(aes_ni))]
325fn aes_encrypt_128(_: &Aes128Key, _: &mut [u8], _v: Iv, _: &[u8]) -> Result<Tag, Error> {
326 Err(Error::UnsupportedAlgorithm)
327}
328
329#[cfg(aes_ni)]
330fn aes_encrypt_256(key: &Aes256Key, msg_ctxt: &mut [u8], iv: Iv, aad: &[u8]) -> Result<Tag, Error> {
331 aesgcm::encrypt_256(&key.0, msg_ctxt, iv.0, aad)
332 .map_err(|e| match e {
333 aesgcm::Error::UnsupportedHardware => Error::UnsupportedAlgorithm,
334 _ => Error::EncryptionError,
335 })
336 .map(|t| t.into())
337}
338
339#[cfg(not(aes_ni))]
340fn aes_encrypt_256(_: &Aes256Key, _: &mut [u8], _: Iv, _: &[u8]) -> Result<Tag, Error> {
341 Err(Error::UnsupportedAlgorithm)
342}
343
344#[cfg(aes_ni)]
345fn aes_decrypt_128(
346 key: &Aes128Key,
347 ctxt_msg: &mut [u8],
348 iv: Iv,
349 aad: &[u8],
350 tag: &Tag,
351) -> Result<(), Error> {
352 aesgcm::decrypt_128(&key.0, ctxt_msg, iv.0, aad, &tag.0).map_err(|e| match e {
353 aesgcm::Error::UnsupportedHardware => Error::UnsupportedAlgorithm,
354 _ => Error::EncryptionError,
355 })
356}
357
358#[cfg(not(aes_ni))]
359fn aes_decrypt_128(_: &Aes128Key, _: &mut [u8], _: Iv, _: &[u8], _: &Tag) -> Result<(), Error> {
360 Err(Error::UnsupportedAlgorithm)
361}
362
363#[cfg(aes_ni)]
364fn aes_decrypt_256(
365 key: &Aes256Key,
366 ctxt_msg: &mut [u8],
367 iv: Iv,
368 aad: &[u8],
369 tag: &Tag,
370) -> Result<(), Error> {
371 aesgcm::decrypt_256(&key.0, ctxt_msg, iv.0, aad, &tag.0).map_err(|e| match e {
372 aesgcm::Error::UnsupportedHardware => Error::UnsupportedAlgorithm,
373 _ => Error::EncryptionError,
374 })
375}
376
377#[cfg(not(aes_ni))]
378fn aes_decrypt_256(_: &Aes256Key, _: &mut [u8], _: Iv, _: &[u8], _: &Tag) -> Result<(), Error> {
379 Err(Error::UnsupportedAlgorithm)
380}
381
382pub fn encrypt(key: &Key, msg_ctxt: &mut [u8], iv: Iv, aad: &[u8]) -> Result<Tag, Error> {
386 match key {
387 Key::Aes128(key) => {
388 if aes_ni_support() {
389 aes_encrypt_128(key, msg_ctxt, iv, aad)
390 } else {
391 Err(Error::UnsupportedAlgorithm)
392 }
393 }
394 Key::Aes256(key) => {
395 if aes_ni_support() {
396 aes_encrypt_256(key, msg_ctxt, iv, aad)
397 } else {
398 Err(Error::UnsupportedAlgorithm)
399 }
400 }
401 Key::Chacha20Poly1305(key) => Ok(if simd256_support() {
402 encrypt_256(key, msg_ctxt, iv, aad)
403 } else if simd128_support() {
404 encrypt_128(key, msg_ctxt, iv, aad)
405 } else {
406 encrypt_32(key, msg_ctxt, iv, aad)
407 }),
408 }
409}
410
411pub fn encrypt_detached(
415 key: &Key,
416 msg: impl AsRef<[u8]>,
417 iv: Iv,
418 aad: impl AsRef<[u8]>,
419) -> Result<(Tag, Vec<u8>), Error> {
420 let mut msg_ctxt = msg.as_ref().to_vec();
421 let tag = encrypt(key, &mut msg_ctxt, iv, aad.as_ref())?;
422 Ok((tag, msg_ctxt))
423}
424
425pub fn decrypt(key: &Key, ctxt_msg: &mut [u8], iv: Iv, aad: &[u8], tag: &Tag) -> Result<(), Error> {
430 match key {
431 Key::Aes128(key) => {
432 if aes_ni_support() {
433 aes_decrypt_128(key, ctxt_msg, iv, aad, tag)
434 } else {
435 Err(Error::UnsupportedAlgorithm)
436 }
437 }
438 Key::Aes256(key) => {
439 if aes_ni_support() {
440 aes_decrypt_256(key, ctxt_msg, iv, aad, tag)
441 } else {
442 Err(Error::UnsupportedAlgorithm)
443 }
444 }
445 Key::Chacha20Poly1305(key) => {
446 if simd256_support() {
447 decrypt_256(key, ctxt_msg, iv, aad, tag)
448 } else if simd128_support() {
449 decrypt_128(key, ctxt_msg, iv, aad, tag)
450 } else {
451 decrypt_32(key, ctxt_msg, iv, aad, tag)
452 }
453 }
454 }
455}
456
457pub fn decrypt_detached(
462 key: &Key,
463 ctxt: impl AsRef<[u8]>,
464 iv: Iv,
465 aad: impl AsRef<[u8]>,
466 tag: &Tag,
467) -> Result<Vec<u8>, Error> {
468 let mut ctxt_msg = ctxt.as_ref().to_vec();
469 decrypt(key, &mut ctxt_msg, iv, aad.as_ref(), tag)?;
470 Ok(ctxt_msg)
471}