1use self::block::{Block, BLOCK_LEN};
25use crate::{constant_time, cpu, error, hkdf, polyfill};
26use core::ops::RangeFrom;
27
28pub use self::{
29 aes_gcm::{AES_128_GCM, AES_256_GCM},
30 chacha20_poly1305::CHACHA20_POLY1305,
31 nonce::{Nonce, NONCE_LEN},
32};
33
34pub trait NonceSequence {
44 fn advance(&mut self) -> Result<Nonce, error::Unspecified>;
52}
53
54pub trait BoundKey<N: NonceSequence>: core::fmt::Debug {
56 fn new(key: UnboundKey, nonce_sequence: N) -> Self;
58
59 fn algorithm(&self) -> &'static Algorithm;
61}
62
63pub struct OpeningKey<N: NonceSequence> {
69 key: UnboundKey,
70 nonce_sequence: N,
71}
72
73impl<N: NonceSequence> BoundKey<N> for OpeningKey<N> {
74 fn new(key: UnboundKey, nonce_sequence: N) -> Self {
75 Self {
76 key,
77 nonce_sequence,
78 }
79 }
80
81 #[inline]
82 fn algorithm(&self) -> &'static Algorithm {
83 self.key.algorithm
84 }
85}
86
87impl<N: NonceSequence> core::fmt::Debug for OpeningKey<N> {
88 fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
89 f.debug_struct("OpeningKey")
90 .field("algorithm", &self.algorithm())
91 .finish()
92 }
93}
94
95impl<N: NonceSequence> OpeningKey<N> {
96 #[inline]
108 pub fn open_in_place<'in_out, A>(
109 &mut self,
110 aad: Aad<A>,
111 in_out: &'in_out mut [u8],
112 ) -> Result<&'in_out mut [u8], error::Unspecified>
113 where
114 A: AsRef<[u8]>,
115 {
116 self.open_within(aad, in_out, 0..)
117 }
118
119 #[inline]
163 pub fn open_within<'in_out, A>(
164 &mut self,
165 aad: Aad<A>,
166 in_out: &'in_out mut [u8],
167 ciphertext_and_tag: RangeFrom<usize>,
168 ) -> Result<&'in_out mut [u8], error::Unspecified>
169 where
170 A: AsRef<[u8]>,
171 {
172 open_within_(
173 &self.key,
174 self.nonce_sequence.advance()?,
175 aad,
176 in_out,
177 ciphertext_and_tag,
178 )
179 }
180}
181
182#[inline]
183fn open_within_<'in_out, A: AsRef<[u8]>>(
184 key: &UnboundKey,
185 nonce: Nonce,
186 Aad(aad): Aad<A>,
187 in_out: &'in_out mut [u8],
188 ciphertext_and_tag: RangeFrom<usize>,
189) -> Result<&'in_out mut [u8], error::Unspecified> {
190 fn open_within<'in_out>(
191 key: &UnboundKey,
192 nonce: Nonce,
193 aad: Aad<&[u8]>,
194 in_out: &'in_out mut [u8],
195 ciphertext_and_tag: RangeFrom<usize>,
196 ) -> Result<&'in_out mut [u8], error::Unspecified> {
197 let in_prefix_len = ciphertext_and_tag.start;
198 let ciphertext_and_tag_len = in_out
199 .len()
200 .checked_sub(in_prefix_len)
201 .ok_or(error::Unspecified)?;
202 let ciphertext_len = ciphertext_and_tag_len
203 .checked_sub(TAG_LEN)
204 .ok_or(error::Unspecified)?;
205 check_per_nonce_max_bytes(key.algorithm, ciphertext_len)?;
206 let (in_out, received_tag) = in_out.split_at_mut(in_prefix_len + ciphertext_len);
207 let Tag(calculated_tag) = (key.algorithm.open)(
208 &key.inner,
209 nonce,
210 aad,
211 in_prefix_len,
212 in_out,
213 key.cpu_features,
214 );
215 if constant_time::verify_slices_are_equal(calculated_tag.as_ref(), received_tag).is_err() {
216 for b in &mut in_out[..ciphertext_len] {
221 *b = 0;
222 }
223 return Err(error::Unspecified);
224 }
225 Ok(&mut in_out[..ciphertext_len])
227 }
228
229 open_within(
230 key,
231 nonce,
232 Aad::from(aad.as_ref()),
233 in_out,
234 ciphertext_and_tag,
235 )
236}
237
238pub struct SealingKey<N: NonceSequence> {
244 key: UnboundKey,
245 nonce_sequence: N,
246}
247
248impl<N: NonceSequence> BoundKey<N> for SealingKey<N> {
249 fn new(key: UnboundKey, nonce_sequence: N) -> Self {
250 Self {
251 key,
252 nonce_sequence,
253 }
254 }
255
256 #[inline]
257 fn algorithm(&self) -> &'static Algorithm {
258 self.key.algorithm
259 }
260}
261
262impl<N: NonceSequence> core::fmt::Debug for SealingKey<N> {
263 fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
264 f.debug_struct("SealingKey")
265 .field("algorithm", &self.algorithm())
266 .finish()
267 }
268}
269
270impl<N: NonceSequence> SealingKey<N> {
271 #[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
273 #[inline]
274 pub fn seal_in_place<A, InOut>(
275 &mut self,
276 aad: Aad<A>,
277 in_out: &mut InOut,
278 ) -> Result<(), error::Unspecified>
279 where
280 A: AsRef<[u8]>,
281 InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
282 {
283 self.seal_in_place_append_tag(aad, in_out)
284 }
285
286 #[inline]
296 pub fn seal_in_place_append_tag<A, InOut>(
297 &mut self,
298 aad: Aad<A>,
299 in_out: &mut InOut,
300 ) -> Result<(), error::Unspecified>
301 where
302 A: AsRef<[u8]>,
303 InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
304 {
305 self.seal_in_place_separate_tag(aad, in_out.as_mut())
306 .map(|tag| in_out.extend(tag.as_ref()))
307 }
308
309 #[inline]
321 pub fn seal_in_place_separate_tag<A>(
322 &mut self,
323 aad: Aad<A>,
324 in_out: &mut [u8],
325 ) -> Result<Tag, error::Unspecified>
326 where
327 A: AsRef<[u8]>,
328 {
329 seal_in_place_separate_tag_(
330 &self.key,
331 self.nonce_sequence.advance()?,
332 Aad::from(aad.as_ref()),
333 in_out,
334 )
335 }
336}
337
338#[inline]
339fn seal_in_place_separate_tag_(
340 key: &UnboundKey,
341 nonce: Nonce,
342 aad: Aad<&[u8]>,
343 in_out: &mut [u8],
344) -> Result<Tag, error::Unspecified> {
345 check_per_nonce_max_bytes(key.algorithm, in_out.len())?;
346 Ok((key.algorithm.seal)(
347 &key.inner,
348 nonce,
349 aad,
350 in_out,
351 key.cpu_features,
352 ))
353}
354
355pub struct Aad<A: AsRef<[u8]>>(A);
361
362impl<A: AsRef<[u8]>> Aad<A> {
363 #[inline]
365 pub fn from(aad: A) -> Self {
366 Aad(aad)
367 }
368}
369
370impl<A> AsRef<[u8]> for Aad<A>
371where
372 A: AsRef<[u8]>,
373{
374 fn as_ref(&self) -> &[u8] {
375 self.0.as_ref()
376 }
377}
378
379impl Aad<[u8; 0]> {
380 pub fn empty() -> Self {
382 Self::from([])
383 }
384}
385
386pub struct UnboundKey {
388 inner: KeyInner,
389 algorithm: &'static Algorithm,
390 cpu_features: cpu::Features,
391}
392
393impl core::fmt::Debug for UnboundKey {
394 fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
395 f.debug_struct("UnboundKey")
396 .field("algorithm", &self.algorithm)
397 .finish()
398 }
399}
400
401#[allow(clippy::large_enum_variant, variant_size_differences)]
402enum KeyInner {
403 AesGcm(aes_gcm::Key),
404 ChaCha20Poly1305(chacha20_poly1305::Key),
405}
406
407impl UnboundKey {
408 pub fn new(
412 algorithm: &'static Algorithm,
413 key_bytes: &[u8],
414 ) -> Result<Self, error::Unspecified> {
415 let cpu_features = cpu::features();
416 Ok(Self {
417 inner: (algorithm.init)(key_bytes, cpu_features)?,
418 algorithm,
419 cpu_features,
420 })
421 }
422
423 #[inline]
425 pub fn algorithm(&self) -> &'static Algorithm {
426 self.algorithm
427 }
428}
429
430impl From<hkdf::Okm<'_, &'static Algorithm>> for UnboundKey {
431 fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
432 let mut key_bytes = [0; MAX_KEY_LEN];
433 let key_bytes = &mut key_bytes[..okm.len().key_len];
434 let algorithm = *okm.len();
435 okm.fill(key_bytes).unwrap();
436 Self::new(algorithm, key_bytes).unwrap()
437 }
438}
439
440impl hkdf::KeyType for &'static Algorithm {
441 #[inline]
442 fn len(&self) -> usize {
443 self.key_len()
444 }
445}
446
447pub struct LessSafeKey {
452 key: UnboundKey,
453}
454
455impl LessSafeKey {
456 pub fn new(key: UnboundKey) -> Self {
458 Self { key }
459 }
460
461 #[inline]
465 pub fn open_in_place<'in_out, A>(
466 &self,
467 nonce: Nonce,
468 aad: Aad<A>,
469 in_out: &'in_out mut [u8],
470 ) -> Result<&'in_out mut [u8], error::Unspecified>
471 where
472 A: AsRef<[u8]>,
473 {
474 self.open_within(nonce, aad, in_out, 0..)
475 }
476
477 #[inline]
481 pub fn open_within<'in_out, A>(
482 &self,
483 nonce: Nonce,
484 aad: Aad<A>,
485 in_out: &'in_out mut [u8],
486 ciphertext_and_tag: RangeFrom<usize>,
487 ) -> Result<&'in_out mut [u8], error::Unspecified>
488 where
489 A: AsRef<[u8]>,
490 {
491 open_within_(&self.key, nonce, aad, in_out, ciphertext_and_tag)
492 }
493
494 #[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
496 #[inline]
497 pub fn seal_in_place<A, InOut>(
498 &self,
499 nonce: Nonce,
500 aad: Aad<A>,
501 in_out: &mut InOut,
502 ) -> Result<(), error::Unspecified>
503 where
504 A: AsRef<[u8]>,
505 InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
506 {
507 self.seal_in_place_append_tag(nonce, aad, in_out)
508 }
509
510 #[inline]
515 pub fn seal_in_place_append_tag<A, InOut>(
516 &self,
517 nonce: Nonce,
518 aad: Aad<A>,
519 in_out: &mut InOut,
520 ) -> Result<(), error::Unspecified>
521 where
522 A: AsRef<[u8]>,
523 InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
524 {
525 self.seal_in_place_separate_tag(nonce, aad, in_out.as_mut())
526 .map(|tag| in_out.extend(tag.as_ref()))
527 }
528
529 #[inline]
534 pub fn seal_in_place_separate_tag<A>(
535 &self,
536 nonce: Nonce,
537 aad: Aad<A>,
538 in_out: &mut [u8],
539 ) -> Result<Tag, error::Unspecified>
540 where
541 A: AsRef<[u8]>,
542 {
543 seal_in_place_separate_tag_(&self.key, nonce, Aad::from(aad.as_ref()), in_out)
544 }
545
546 #[inline]
548 pub fn algorithm(&self) -> &'static Algorithm {
549 &self.key.algorithm
550 }
551}
552
553impl core::fmt::Debug for LessSafeKey {
554 fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
555 f.debug_struct("LessSafeKey")
556 .field("algorithm", self.algorithm())
557 .finish()
558 }
559}
560
561pub struct Algorithm {
563 init: fn(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>,
564
565 seal: fn(
566 key: &KeyInner,
567 nonce: Nonce,
568 aad: Aad<&[u8]>,
569 in_out: &mut [u8],
570 cpu_features: cpu::Features,
571 ) -> Tag,
572 open: fn(
573 key: &KeyInner,
574 nonce: Nonce,
575 aad: Aad<&[u8]>,
576 in_prefix_len: usize,
577 in_out: &mut [u8],
578 cpu_features: cpu::Features,
579 ) -> Tag,
580
581 key_len: usize,
582 id: AlgorithmID,
583
584 max_input_len: u64,
587}
588
589const fn max_input_len(block_len: usize, overhead_blocks_per_nonce: usize) -> u64 {
590 ((1u64 << 32) - polyfill::u64_from_usize(overhead_blocks_per_nonce))
593 * polyfill::u64_from_usize(block_len)
594}
595
596impl Algorithm {
597 #[inline(always)]
599 pub fn key_len(&self) -> usize {
600 self.key_len
601 }
602
603 #[inline(always)]
607 pub fn tag_len(&self) -> usize {
608 TAG_LEN
609 }
610
611 #[inline(always)]
613 pub fn nonce_len(&self) -> usize {
614 NONCE_LEN
615 }
616}
617
618derive_debug_via_id!(Algorithm);
619
620#[derive(Debug, Eq, PartialEq)]
621enum AlgorithmID {
622 AES_128_GCM,
623 AES_256_GCM,
624 CHACHA20_POLY1305,
625}
626
627impl PartialEq for Algorithm {
628 fn eq(&self, other: &Self) -> bool {
629 self.id == other.id
630 }
631}
632
633impl Eq for Algorithm {}
634
635#[must_use]
637#[repr(C)]
638pub struct Tag([u8; TAG_LEN]);
639
640impl AsRef<[u8]> for Tag {
641 fn as_ref(&self) -> &[u8] {
642 self.0.as_ref()
643 }
644}
645
646const MAX_KEY_LEN: usize = 32;
647
648const TAG_LEN: usize = BLOCK_LEN;
650
651pub const MAX_TAG_LEN: usize = TAG_LEN;
653
654fn check_per_nonce_max_bytes(alg: &Algorithm, in_out_len: usize) -> Result<(), error::Unspecified> {
655 if polyfill::u64_from_usize(in_out_len) > alg.max_input_len {
656 return Err(error::Unspecified);
657 }
658 Ok(())
659}
660
661#[derive(Clone, Copy)]
662enum Direction {
663 Opening { in_prefix_len: usize },
664 Sealing,
665}
666
667mod aes;
668mod aes_gcm;
669mod block;
670mod chacha;
671mod chacha20_poly1305;
672pub mod chacha20_poly1305_openssh;
673mod counter;
674mod gcm;
675mod iv;
676mod nonce;
677mod poly1305;
678pub mod quic;
679mod shift;