// Copyright 2015-2016 Brian Smith. // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. //! Authenticated Encryption with Associated Data (AEAD). //! //! See [Authenticated encryption: relations among notions and analysis of the //! generic composition paradigm][AEAD] for an introduction to the concept of //! AEADs. //! //! [AEAD]: http://www-cse.ucsd.edu/~mihir/papers/oem.html //! [`crypto.cipher.AEAD`]: https://golang.org/pkg/crypto/cipher/#AEAD use self::block::{Block, BLOCK_LEN}; use crate::{constant_time, cpu, error, hkdf, polyfill}; use core::ops::RangeFrom; pub use self::{ aes_gcm::{AES_128_GCM, AES_256_GCM}, chacha20_poly1305::CHACHA20_POLY1305, nonce::{Nonce, NONCE_LEN}, }; /// A sequences of unique nonces. /// /// A given `NonceSequence` must never return the same `Nonce` twice from /// `advance()`. /// /// A simple counter is a reasonable (but probably not ideal) `NonceSequence`. /// /// Intentionally not `Clone` or `Copy` since cloning would allow duplication /// of the sequence. pub trait NonceSequence { /// Returns the next nonce in the sequence. /// /// This may fail if "too many" nonces have been requested, where how many /// is too many is up to the implementation of `NonceSequence`. An /// implementation may that enforce a maximum number of records are /// sent/received under a key this way. Once `advance()` fails, it must /// fail for all subsequent calls. fn advance(&mut self) -> Result; } /// An AEAD key bound to a nonce sequence. pub trait BoundKey: core::fmt::Debug { /// Constructs a new key from the given `UnboundKey` and `NonceSequence`. fn new(key: UnboundKey, nonce_sequence: N) -> Self; /// The key's AEAD algorithm. fn algorithm(&self) -> &'static Algorithm; } /// An AEAD key for authenticating and decrypting ("opening"), bound to a nonce /// sequence. /// /// Intentionally not `Clone` or `Copy` since cloning would allow duplication /// of the nonce sequence. pub struct OpeningKey { key: UnboundKey, nonce_sequence: N, } impl BoundKey for OpeningKey { fn new(key: UnboundKey, nonce_sequence: N) -> Self { Self { key, nonce_sequence, } } #[inline] fn algorithm(&self) -> &'static Algorithm { self.key.algorithm } } impl core::fmt::Debug for OpeningKey { fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { f.debug_struct("OpeningKey") .field("algorithm", &self.algorithm()) .finish() } } impl OpeningKey { /// Authenticates and decrypts (“opens”) data in place. /// /// `aad` is the additional authenticated data (AAD), if any. /// /// On input, `in_out` must be the ciphertext followed by the tag. When /// `open_in_place()` returns `Ok(plaintext)`, the input ciphertext /// has been overwritten by the plaintext; `plaintext` will refer to the /// plaintext without the tag. /// /// When `open_in_place()` returns `Err(..)`, `in_out` may have been /// overwritten in an unspecified way. #[inline] pub fn open_in_place<'in_out, A>( &mut self, aad: Aad, in_out: &'in_out mut [u8], ) -> Result<&'in_out mut [u8], error::Unspecified> where A: AsRef<[u8]>, { self.open_within(aad, in_out, 0..) } /// Authenticates and decrypts (“opens”) data in place, with a shift. /// /// `aad` is the additional authenticated data (AAD), if any. /// /// On input, `in_out[ciphertext_and_tag]` must be the ciphertext followed /// by the tag. When `open_within()` returns `Ok(plaintext)`, the plaintext /// will be at `in_out[0..plaintext.len()]`. In other words, the following /// two code fragments are equivalent for valid values of /// `ciphertext_and_tag`, except `open_within` will often be more efficient: /// /// /// ```skip /// let plaintext = key.open_within(aad, in_out, cipertext_and_tag)?; /// ``` /// /// ```skip /// let ciphertext_and_tag_len = in_out[ciphertext_and_tag].len(); /// in_out.copy_within(ciphertext_and_tag, 0); /// let plaintext = key.open_in_place(aad, &mut in_out[..ciphertext_and_tag_len])?; /// ``` /// /// Similarly, `key.open_within(aad, in_out, 0..)` is equivalent to /// `key.open_in_place(aad, in_out)`. /// /// When `open_in_place()` returns `Err(..)`, `in_out` may have been /// overwritten in an unspecified way. /// /// The shifting feature is useful in the case where multiple packets are /// being reassembled in place. Consider this example where the peer has /// sent the message “Split stream reassembled in place” split into /// three sealed packets: /// /// ```ascii-art /// Packet 1 Packet 2 Packet 3 /// Input: [Header][Ciphertext][Tag][Header][Ciphertext][Tag][Header][Ciphertext][Tag] /// | +--------------+ | /// +------+ +-----+ +----------------------------------+ /// v v v /// Output: [Plaintext][Plaintext][Plaintext] /// “Split stream reassembled in place” /// ``` /// /// This reassembly be accomplished with three calls to `open_within()`. #[inline] pub fn open_within<'in_out, A>( &mut self, aad: Aad, in_out: &'in_out mut [u8], ciphertext_and_tag: RangeFrom, ) -> Result<&'in_out mut [u8], error::Unspecified> where A: AsRef<[u8]>, { open_within_( &self.key, self.nonce_sequence.advance()?, aad, in_out, ciphertext_and_tag, ) } } #[inline] fn open_within_<'in_out, A: AsRef<[u8]>>( key: &UnboundKey, nonce: Nonce, Aad(aad): Aad, in_out: &'in_out mut [u8], ciphertext_and_tag: RangeFrom, ) -> Result<&'in_out mut [u8], error::Unspecified> { fn open_within<'in_out>( key: &UnboundKey, nonce: Nonce, aad: Aad<&[u8]>, in_out: &'in_out mut [u8], ciphertext_and_tag: RangeFrom, ) -> Result<&'in_out mut [u8], error::Unspecified> { let in_prefix_len = ciphertext_and_tag.start; let ciphertext_and_tag_len = in_out .len() .checked_sub(in_prefix_len) .ok_or(error::Unspecified)?; let ciphertext_len = ciphertext_and_tag_len .checked_sub(TAG_LEN) .ok_or(error::Unspecified)?; check_per_nonce_max_bytes(key.algorithm, ciphertext_len)?; let (in_out, received_tag) = in_out.split_at_mut(in_prefix_len + ciphertext_len); let Tag(calculated_tag) = (key.algorithm.open)( &key.inner, nonce, aad, in_prefix_len, in_out, key.cpu_features, ); if constant_time::verify_slices_are_equal(calculated_tag.as_ref(), received_tag).is_err() { // Zero out the plaintext so that it isn't accidentally leaked or used // after verification fails. It would be safest if we could check the // tag before decrypting, but some `open` implementations interleave // authentication with decryption for performance. for b in &mut in_out[..ciphertext_len] { *b = 0; } return Err(error::Unspecified); } // `ciphertext_len` is also the plaintext length. Ok(&mut in_out[..ciphertext_len]) } open_within( key, nonce, Aad::from(aad.as_ref()), in_out, ciphertext_and_tag, ) } /// An AEAD key for encrypting and signing ("sealing"), bound to a nonce /// sequence. /// /// Intentionally not `Clone` or `Copy` since cloning would allow duplication /// of the nonce sequence. pub struct SealingKey { key: UnboundKey, nonce_sequence: N, } impl BoundKey for SealingKey { fn new(key: UnboundKey, nonce_sequence: N) -> Self { Self { key, nonce_sequence, } } #[inline] fn algorithm(&self) -> &'static Algorithm { self.key.algorithm } } impl core::fmt::Debug for SealingKey { fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { f.debug_struct("SealingKey") .field("algorithm", &self.algorithm()) .finish() } } impl SealingKey { /// Deprecated. Renamed to `seal_in_place_append_tag()`. #[deprecated(note = "Renamed to `seal_in_place_append_tag`.")] #[inline] pub fn seal_in_place( &mut self, aad: Aad, in_out: &mut InOut, ) -> Result<(), error::Unspecified> where A: AsRef<[u8]>, InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { self.seal_in_place_append_tag(aad, in_out) } /// Encrypts and signs (“seals”) data in place, appending the tag to the /// resulting ciphertext. /// /// `key.seal_in_place_append_tag(aad, in_out)` is equivalent to: /// /// ```skip /// key.seal_in_place_separate_tag(aad, in_out.as_mut()) /// .map(|tag| in_out.extend(tag.as_ref())) /// ``` #[inline] pub fn seal_in_place_append_tag( &mut self, aad: Aad, in_out: &mut InOut, ) -> Result<(), error::Unspecified> where A: AsRef<[u8]>, InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { self.seal_in_place_separate_tag(aad, in_out.as_mut()) .map(|tag| in_out.extend(tag.as_ref())) } /// Encrypts and signs (“seals”) data in place. /// /// `aad` is the additional authenticated data (AAD), if any. This is /// authenticated but not encrypted. The type `A` could be a byte slice /// `&[u8]`, a byte array `[u8; N]` for some constant `N`, `Vec`, etc. /// If there is no AAD then use `Aad::empty()`. /// /// The plaintext is given as the input value of `in_out`. `seal_in_place()` /// will overwrite the plaintext with the ciphertext and return the tag. /// For most protocols, the caller must append the tag to the ciphertext. /// The tag will be `self.algorithm.tag_len()` bytes long. #[inline] pub fn seal_in_place_separate_tag( &mut self, aad: Aad, in_out: &mut [u8], ) -> Result where A: AsRef<[u8]>, { seal_in_place_separate_tag_( &self.key, self.nonce_sequence.advance()?, Aad::from(aad.as_ref()), in_out, ) } } #[inline] fn seal_in_place_separate_tag_( key: &UnboundKey, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8], ) -> Result { check_per_nonce_max_bytes(key.algorithm, in_out.len())?; Ok((key.algorithm.seal)( &key.inner, nonce, aad, in_out, key.cpu_features, )) } /// The additionally authenticated data (AAD) for an opening or sealing /// operation. This data is authenticated but is **not** encrypted. /// /// The type `A` could be a byte slice `&[u8]`, a byte array `[u8; N]` /// for some constant `N`, `Vec`, etc. pub struct Aad>(A); impl> Aad { /// Construct the `Aad` from the given bytes. #[inline] pub fn from(aad: A) -> Self { Aad(aad) } } impl AsRef<[u8]> for Aad where A: AsRef<[u8]>, { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } impl Aad<[u8; 0]> { /// Construct an empty `Aad`. pub fn empty() -> Self { Self::from([]) } } /// An AEAD key without a designated role or nonce sequence. pub struct UnboundKey { inner: KeyInner, algorithm: &'static Algorithm, cpu_features: cpu::Features, } impl core::fmt::Debug for UnboundKey { fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { f.debug_struct("UnboundKey") .field("algorithm", &self.algorithm) .finish() } } #[allow(clippy::large_enum_variant, variant_size_differences)] enum KeyInner { AesGcm(aes_gcm::Key), ChaCha20Poly1305(chacha20_poly1305::Key), } impl UnboundKey { /// Constructs an `UnboundKey`. /// /// Fails if `key_bytes.len() != algorithm.key_len()`. pub fn new( algorithm: &'static Algorithm, key_bytes: &[u8], ) -> Result { let cpu_features = cpu::features(); Ok(Self { inner: (algorithm.init)(key_bytes, cpu_features)?, algorithm, cpu_features, }) } /// The key's AEAD algorithm. #[inline] pub fn algorithm(&self) -> &'static Algorithm { self.algorithm } } impl From> for UnboundKey { fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self { let mut key_bytes = [0; MAX_KEY_LEN]; let key_bytes = &mut key_bytes[..okm.len().key_len]; let algorithm = *okm.len(); okm.fill(key_bytes).unwrap(); Self::new(algorithm, key_bytes).unwrap() } } impl hkdf::KeyType for &'static Algorithm { #[inline] fn len(&self) -> usize { self.key_len() } } /// Immutable keys for use in situations where `OpeningKey`/`SealingKey` and /// `NonceSequence` cannot reasonably be used. /// /// Prefer to use `OpeningKey`/`SealingKey` and `NonceSequence` when practical. pub struct LessSafeKey { key: UnboundKey, } impl LessSafeKey { /// Constructs a `LessSafeKey` from an `UnboundKey`. pub fn new(key: UnboundKey) -> Self { Self { key } } /// Like [`OpeningKey::open_in_place()`], except it accepts an arbitrary nonce. /// /// `nonce` must be unique for every use of the key to open data. #[inline] pub fn open_in_place<'in_out, A>( &self, nonce: Nonce, aad: Aad, in_out: &'in_out mut [u8], ) -> Result<&'in_out mut [u8], error::Unspecified> where A: AsRef<[u8]>, { self.open_within(nonce, aad, in_out, 0..) } /// Like [`OpeningKey::open_within()`], except it accepts an arbitrary nonce. /// /// `nonce` must be unique for every use of the key to open data. #[inline] pub fn open_within<'in_out, A>( &self, nonce: Nonce, aad: Aad, in_out: &'in_out mut [u8], ciphertext_and_tag: RangeFrom, ) -> Result<&'in_out mut [u8], error::Unspecified> where A: AsRef<[u8]>, { open_within_(&self.key, nonce, aad, in_out, ciphertext_and_tag) } /// Deprecated. Renamed to `seal_in_place_append_tag()`. #[deprecated(note = "Renamed to `seal_in_place_append_tag`.")] #[inline] pub fn seal_in_place( &self, nonce: Nonce, aad: Aad, in_out: &mut InOut, ) -> Result<(), error::Unspecified> where A: AsRef<[u8]>, InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { self.seal_in_place_append_tag(nonce, aad, in_out) } /// Like [`SealingKey::seal_in_place_append_tag()`], except it accepts an /// arbitrary nonce. /// /// `nonce` must be unique for every use of the key to seal data. #[inline] pub fn seal_in_place_append_tag( &self, nonce: Nonce, aad: Aad, in_out: &mut InOut, ) -> Result<(), error::Unspecified> where A: AsRef<[u8]>, InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { self.seal_in_place_separate_tag(nonce, aad, in_out.as_mut()) .map(|tag| in_out.extend(tag.as_ref())) } /// Like `SealingKey::seal_in_place_separate_tag()`, except it accepts an /// arbitrary nonce. /// /// `nonce` must be unique for every use of the key to seal data. #[inline] pub fn seal_in_place_separate_tag( &self, nonce: Nonce, aad: Aad, in_out: &mut [u8], ) -> Result where A: AsRef<[u8]>, { seal_in_place_separate_tag_(&self.key, nonce, Aad::from(aad.as_ref()), in_out) } /// The key's AEAD algorithm. #[inline] pub fn algorithm(&self) -> &'static Algorithm { &self.key.algorithm } } impl core::fmt::Debug for LessSafeKey { fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { f.debug_struct("LessSafeKey") .field("algorithm", self.algorithm()) .finish() } } /// An AEAD Algorithm. pub struct Algorithm { init: fn(key: &[u8], cpu_features: cpu::Features) -> Result, seal: fn( key: &KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8], cpu_features: cpu::Features, ) -> Tag, open: fn( key: &KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_prefix_len: usize, in_out: &mut [u8], cpu_features: cpu::Features, ) -> Tag, key_len: usize, id: AlgorithmID, /// Use `max_input_len!()` to initialize this. // TODO: Make this `usize`. max_input_len: u64, } const fn max_input_len(block_len: usize, overhead_blocks_per_nonce: usize) -> u64 { // Each of our AEADs use a 32-bit block counter so the maximum is the // largest input that will not overflow the counter. ((1u64 << 32) - polyfill::u64_from_usize(overhead_blocks_per_nonce)) * polyfill::u64_from_usize(block_len) } impl Algorithm { /// The length of the key. #[inline(always)] pub fn key_len(&self) -> usize { self.key_len } /// The length of a tag. /// /// See also `MAX_TAG_LEN`. #[inline(always)] pub fn tag_len(&self) -> usize { TAG_LEN } /// The length of the nonces. #[inline(always)] pub fn nonce_len(&self) -> usize { NONCE_LEN } } derive_debug_via_id!(Algorithm); #[derive(Debug, Eq, PartialEq)] enum AlgorithmID { AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305, } impl PartialEq for Algorithm { fn eq(&self, other: &Self) -> bool { self.id == other.id } } impl Eq for Algorithm {} /// An authentication tag. #[must_use] #[repr(C)] pub struct Tag([u8; TAG_LEN]); impl AsRef<[u8]> for Tag { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } const MAX_KEY_LEN: usize = 32; // All the AEADs we support use 128-bit tags. const TAG_LEN: usize = BLOCK_LEN; /// The maximum length of a tag for the algorithms in this module. pub const MAX_TAG_LEN: usize = TAG_LEN; fn check_per_nonce_max_bytes(alg: &Algorithm, in_out_len: usize) -> Result<(), error::Unspecified> { if polyfill::u64_from_usize(in_out_len) > alg.max_input_len { return Err(error::Unspecified); } Ok(()) } #[derive(Clone, Copy)] enum Direction { Opening { in_prefix_len: usize }, Sealing, } mod aes; mod aes_gcm; mod block; mod chacha; mod chacha20_poly1305; pub mod chacha20_poly1305_openssh; mod counter; mod gcm; mod iv; mod nonce; mod poly1305; pub mod quic; mod shift;