diff --git a/ssh-cipher/Cargo.toml b/ssh-cipher/Cargo.toml index 325e35a..1de1ec7 100644 --- a/ssh-cipher/Cargo.toml +++ b/ssh-cipher/Cargo.toml @@ -38,9 +38,7 @@ zeroize = { version = "1", optional = true, default-features = false } hex-literal = "1" [features] -aes-cbc = ["dep:aes", "dep:cbc"] -aes-ctr = ["dep:aes", "dep:ctr"] -aes-gcm = ["dep:aead", "dep:aes", "dep:aes-gcm"] +aes = ["dep:aead", "dep:aes", "dep:aes-gcm", "dep:cbc", "dep:ctr"] chacha20poly1305 = ["dep:aead", "dep:chacha20", "dep:poly1305", "dep:ctutils"] tdes = ["dep:des", "dep:cbc"] zeroize = [ diff --git a/ssh-cipher/src/aes.rs b/ssh-cipher/src/aes.rs new file mode 100644 index 0000000..383541f --- /dev/null +++ b/ssh-cipher/src/aes.rs @@ -0,0 +1,81 @@ +//! AES block cipher. + +use ::aes::{Aes128, Aes192, Aes256, Block}; +use ::cipher::{ + BlockCipherDecClosure, BlockCipherDecrypt, BlockCipherEncClosure, BlockCipherEncrypt, + BlockSizeUser, InvalidLength, KeyInit, array::sizes::U16, +}; + +/// Advanced Encryption Standard (AES) low-level block cipher. +/// +/// Supports 128-bit, 192-bit, and 256-bit key sizes. +pub(crate) enum Aes { + Aes128(Aes128), + Aes192(Aes192), + Aes256(Aes256), +} + +impl Aes { + /// Create a new AES block cipher instance. + /// + /// Supports `key` whose length is 16-bytes (128-bit), 24-bytes (192-bits), or 32-bytes + /// (256-bits). + /// + /// # Errors + /// Returns [`InvalidLength`] if the length of `key` is not any of the above. + pub(crate) fn new(key: &[u8]) -> Result { + if let Ok(cipher) = Aes128::new_from_slice(key) { + return Ok(Aes::Aes128(cipher)); + } + + if let Ok(cipher) = Aes192::new_from_slice(key) { + return Ok(Aes::Aes192(cipher)); + } + + if let Ok(cipher) = Aes256::new_from_slice(key) { + return Ok(Aes::Aes256(cipher)); + } + + Err(InvalidLength) + } +} + +impl BlockCipherDecrypt for Aes { + fn decrypt_blocks(&self, blocks: &mut [Block]) { + match self { + Aes::Aes128(aes) => aes.decrypt_blocks(blocks), + Aes::Aes192(aes) => aes.decrypt_blocks(blocks), + Aes::Aes256(aes) => aes.decrypt_blocks(blocks), + } + } + + fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure) { + match self { + Aes::Aes128(aes) => aes.decrypt_with_backend(f), + Aes::Aes192(aes) => aes.decrypt_with_backend(f), + Aes::Aes256(aes) => aes.decrypt_with_backend(f), + } + } +} + +impl BlockCipherEncrypt for Aes { + fn encrypt_blocks(&self, blocks: &mut [Block]) { + match self { + Aes::Aes128(aes) => aes.encrypt_blocks(blocks), + Aes::Aes192(aes) => aes.encrypt_blocks(blocks), + Aes::Aes256(aes) => aes.encrypt_blocks(blocks), + } + } + + fn encrypt_with_backend(&self, f: impl BlockCipherEncClosure) { + match self { + Aes::Aes128(aes) => aes.encrypt_with_backend(f), + Aes::Aes192(aes) => aes.encrypt_with_backend(f), + Aes::Aes256(aes) => aes.encrypt_with_backend(f), + } + } +} + +impl BlockSizeUser for Aes { + type BlockSize = U16; +} diff --git a/ssh-cipher/src/decryptor.rs b/ssh-cipher/src/decryptor.rs index 1ca708c..fcc62a5 100644 --- a/ssh-cipher/src/decryptor.rs +++ b/ssh-cipher/src/decryptor.rs @@ -1,22 +1,19 @@ //! Stateful decryptor object. use crate::{Cipher, Error, Result}; -use cipher::KeyIvInit; -use core::fmt::{self, Debug}; - -#[cfg(any(feature = "aes-cbc", feature = "aes-ctr"))] -use aes::{Aes128, Aes192, Aes256}; -#[cfg(any(feature = "aes-cbc", feature = "tdes"))] -use cipher::SetIvState; -#[cfg(any(feature = "aes-cbc", feature = "tdes"))] use cipher::{ - Block, IvState, + Block, IvState, SetIvState, block::{BlockCipherDecrypt, BlockModeDecrypt}, }; +use core::fmt::{self, Debug}; +#[cfg(feature = "aes")] +use { + crate::aes::Aes, + cipher::{InnerIvInit, StreamCipher, StreamCipherSeek}, + ctr::{Ctr128BE, CtrCore}, +}; #[cfg(feature = "tdes")] -use des::TdesEde3; -#[cfg(feature = "aes-ctr")] -use {crate::encryptor::ctr_encrypt as ctr_decrypt, cipher::StreamCipherSeek, ctr::Ctr128BE}; +use {cipher::KeyIvInit, des::TdesEde3}; /// Stateful decryptor object for unauthenticated SSH symmetric ciphers. /// @@ -25,31 +22,26 @@ use {crate::encryptor::ctr_encrypt as ctr_decrypt, cipher::StreamCipherSeek, ctr pub struct Decryptor { /// Inner enum over possible decryption ciphers. inner: Inner, + + /// Cipher in use by this `Encryptor`. + cipher: Cipher, } /// Inner decryptor enum which is deliberately kept out of the public API. enum Inner { - #[cfg(feature = "aes-cbc")] - Aes128Cbc(cbc::Decryptor), - #[cfg(feature = "aes-cbc")] - Aes192Cbc(cbc::Decryptor), - #[cfg(feature = "aes-cbc")] - Aes256Cbc(cbc::Decryptor), - #[cfg(feature = "aes-ctr")] - Aes128Ctr(Ctr128BE), - #[cfg(feature = "aes-ctr")] - Aes192Ctr(Ctr128BE), - #[cfg(feature = "aes-ctr")] - Aes256Ctr(Ctr128BE), + #[cfg(feature = "aes")] + AesCbc(cbc::Decryptor), + #[cfg(feature = "aes")] + AesCtr(Ctr128BE), #[cfg(feature = "tdes")] TDesCbc(cbc::Decryptor), } /// Current IV state or position within the cipher. enum State { - #[cfg(feature = "aes-cbc")] + #[cfg(feature = "aes")] AesCbc(aes::Block), - #[cfg(feature = "aes-ctr")] + #[cfg(feature = "aes")] AesCtr(u64), #[cfg(feature = "tdes")] TDesCbc(Block), @@ -67,46 +59,28 @@ impl Decryptor { cipher.check_key_and_iv(key, iv)?; let inner = match cipher { - #[cfg(feature = "aes-cbc")] - Cipher::Aes128Cbc => cbc::Decryptor::new_from_slices(key, iv).map(Inner::Aes128Cbc), - #[cfg(feature = "aes-cbc")] - Cipher::Aes192Cbc => cbc::Decryptor::new_from_slices(key, iv).map(Inner::Aes192Cbc), - #[cfg(feature = "aes-cbc")] - Cipher::Aes256Cbc => cbc::Decryptor::new_from_slices(key, iv).map(Inner::Aes256Cbc), - #[cfg(feature = "aes-ctr")] - Cipher::Aes128Ctr => Ctr128BE::new_from_slices(key, iv).map(Inner::Aes128Ctr), - #[cfg(feature = "aes-ctr")] - Cipher::Aes192Ctr => Ctr128BE::new_from_slices(key, iv).map(Inner::Aes192Ctr), - #[cfg(feature = "aes-ctr")] - Cipher::Aes256Ctr => Ctr128BE::new_from_slices(key, iv).map(Inner::Aes256Ctr), + #[cfg(feature = "aes")] + Cipher::Aes128Cbc | Cipher::Aes192Cbc | Cipher::Aes256Cbc => { + cbc::Decryptor::inner_iv_slice_init(Aes::new(key)?, iv).map(Inner::AesCbc) + } + #[cfg(feature = "aes")] + Cipher::Aes128Ctr | Cipher::Aes192Ctr | Cipher::Aes256Ctr => { + let core = CtrCore::inner_iv_slice_init(Aes::new(key)?, iv)?; + Ok(Inner::AesCtr(Ctr128BE::from_core(core))) + } #[cfg(feature = "tdes")] Cipher::TDesCbc => cbc::Decryptor::new_from_slices(key, iv).map(Inner::TDesCbc), _ => return Err(cipher.unsupported()), } .map_err(|_| Error::Length)?; - Ok(Self { inner }) + Ok(Self { inner, cipher }) } /// Get the cipher for this decryptor. #[must_use] pub fn cipher(&self) -> Cipher { - match &self.inner { - #[cfg(feature = "aes-cbc")] - Inner::Aes128Cbc(_) => Cipher::Aes128Cbc, - #[cfg(feature = "aes-cbc")] - Inner::Aes192Cbc(_) => Cipher::Aes192Cbc, - #[cfg(feature = "aes-cbc")] - Inner::Aes256Cbc(_) => Cipher::Aes256Cbc, - #[cfg(feature = "aes-ctr")] - Inner::Aes128Ctr(_) => Cipher::Aes128Ctr, - #[cfg(feature = "aes-ctr")] - Inner::Aes192Ctr(_) => Cipher::Aes192Ctr, - #[cfg(feature = "aes-ctr")] - Inner::Aes256Ctr(_) => Cipher::Aes256Ctr, - #[cfg(feature = "tdes")] - Inner::TDesCbc(_) => Cipher::TDesCbc, - } + self.cipher } /// Decrypt the given buffer in place. @@ -115,24 +89,16 @@ impl Decryptor { /// Returns [`Error::Length`] in the event that `buffer` is not a multiple of the cipher's /// block size. pub fn decrypt(&mut self, buffer: &mut [u8]) -> Result<()> { - #[cfg(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes"))] match &mut self.inner { - #[cfg(feature = "aes-cbc")] - Inner::Aes128Cbc(cipher) => cbc_decrypt(cipher, buffer), - #[cfg(feature = "aes-cbc")] - Inner::Aes192Cbc(cipher) => cbc_decrypt(cipher, buffer), - #[cfg(feature = "aes-cbc")] - Inner::Aes256Cbc(cipher) => cbc_decrypt(cipher, buffer), - #[cfg(feature = "aes-ctr")] - Inner::Aes128Ctr(cipher) => ctr_decrypt(cipher, buffer), - #[cfg(feature = "aes-ctr")] - Inner::Aes192Ctr(cipher) => ctr_decrypt(cipher, buffer), - #[cfg(feature = "aes-ctr")] - Inner::Aes256Ctr(cipher) => ctr_decrypt(cipher, buffer), + #[cfg(feature = "aes")] + Inner::AesCbc(cipher) => cbc_decrypt(cipher, buffer)?, + #[cfg(feature = "aes")] + Inner::AesCtr(cipher) => cipher + .try_apply_keystream(buffer) + .map_err(|_| Error::Crypto)?, #[cfg(feature = "tdes")] - Inner::TDesCbc(cipher) => cbc_decrypt(cipher, buffer), + Inner::TDesCbc(cipher) => cbc_decrypt(cipher, buffer)?, } - .map_err(|_| Error::Length)?; Ok(()) } @@ -155,18 +121,10 @@ impl Decryptor { /// Get the current cipher state, i.e. IV or position within the stream cipher. fn state(&self) -> State { match &self.inner { - #[cfg(feature = "aes-cbc")] - Inner::Aes128Cbc(cipher) => State::AesCbc(cipher.iv_state()), - #[cfg(feature = "aes-cbc")] - Inner::Aes192Cbc(cipher) => State::AesCbc(cipher.iv_state()), - #[cfg(feature = "aes-cbc")] - Inner::Aes256Cbc(cipher) => State::AesCbc(cipher.iv_state()), - #[cfg(feature = "aes-ctr")] - Inner::Aes128Ctr(cipher) => State::AesCtr(cipher.current_pos()), - #[cfg(feature = "aes-ctr")] - Inner::Aes192Ctr(cipher) => State::AesCtr(cipher.current_pos()), - #[cfg(feature = "aes-ctr")] - Inner::Aes256Ctr(cipher) => State::AesCtr(cipher.current_pos()), + #[cfg(feature = "aes")] + Inner::AesCbc(cipher) => State::AesCbc(cipher.iv_state()), + #[cfg(feature = "aes")] + Inner::AesCtr(cipher) => State::AesCtr(cipher.current_pos()), #[cfg(feature = "tdes")] Inner::TDesCbc(cipher) => State::TDesCbc(cipher.iv_state()), } @@ -175,31 +133,13 @@ impl Decryptor { /// Set the current cipher state. fn set_state(&mut self, state: State) -> Result<()> { match (&mut self.inner, state) { - #[cfg(feature = "aes-cbc")] - (Inner::Aes128Cbc(cipher), State::AesCbc(iv)) => { + #[cfg(feature = "aes")] + (Inner::AesCbc(cipher), State::AesCbc(iv)) => { cipher.set_iv(&iv); Ok(()) } - #[cfg(feature = "aes-cbc")] - (Inner::Aes192Cbc(cipher), State::AesCbc(iv)) => { - cipher.set_iv(&iv); - Ok(()) - } - #[cfg(feature = "aes-cbc")] - (Inner::Aes256Cbc(cipher), State::AesCbc(iv)) => { - cipher.set_iv(&iv); - Ok(()) - } - #[cfg(feature = "aes-ctr")] - (Inner::Aes128Ctr(cipher), State::AesCtr(pos)) => { - cipher.try_seek(pos).map_err(|_| Error::Crypto) - } - #[cfg(feature = "aes-ctr")] - (Inner::Aes192Ctr(cipher), State::AesCtr(pos)) => { - cipher.try_seek(pos).map_err(|_| Error::Crypto) - } - #[cfg(feature = "aes-ctr")] - (Inner::Aes256Ctr(cipher), State::AesCtr(pos)) => { + #[cfg(feature = "aes")] + (Inner::AesCtr(cipher), State::AesCtr(pos)) => { cipher.try_seek(pos).map_err(|_| Error::Crypto) } #[cfg(feature = "tdes")] @@ -222,7 +162,7 @@ impl Debug for Decryptor { } /// CBC mode decryption helper which assumes the input is unpadded and block-aligned. -#[cfg(any(feature = "aes-cbc", feature = "tdes"))] +#[cfg(any(feature = "aes", feature = "tdes"))] fn cbc_decrypt(decryptor: &mut cbc::Decryptor, buffer: &mut [u8]) -> Result<()> where C: BlockCipherDecrypt, diff --git a/ssh-cipher/src/encryptor.rs b/ssh-cipher/src/encryptor.rs index b744851..2d5226f 100644 --- a/ssh-cipher/src/encryptor.rs +++ b/ssh-cipher/src/encryptor.rs @@ -1,20 +1,17 @@ //! Stateful encryptor object. use crate::{Cipher, Error, Result}; -use cipher::{BlockCipherEncrypt, KeyIvInit}; +use cipher::{Block, BlockCipherEncrypt, BlockModeEncrypt}; use core::fmt::{self, Debug}; -#[cfg(any(feature = "aes-cbc", feature = "aes-ctr"))] -use aes::{Aes128, Aes192, Aes256}; -#[cfg(any(feature = "aes-cbc", feature = "tdes"))] -use cipher::{Block, BlockModeEncrypt}; -#[cfg(feature = "tdes")] -use des::TdesEde3; -#[cfg(feature = "aes-ctr")] +#[cfg(feature = "aes")] use { - cipher::{BlockSizeUser, StreamCipher, array::sizes::U16}, - ctr::Ctr128BE, + crate::aes::Aes, + cipher::{InnerIvInit, StreamCipher}, + ctr::{Ctr128BE, CtrCore}, }; +#[cfg(feature = "tdes")] +use {cipher::KeyIvInit, des::TdesEde3}; /// Stateful encryptor object for unauthenticated SSH symmetric ciphers. /// @@ -23,22 +20,17 @@ use { pub struct Encryptor { /// Inner enum over possible encryption ciphers. inner: Inner, + + /// Cipher in use by this `Encryptor`. + cipher: Cipher, } /// Inner encryptor enum which is deliberately kept out of the public API. enum Inner { - #[cfg(feature = "aes-cbc")] - Aes128Cbc(cbc::Encryptor), - #[cfg(feature = "aes-cbc")] - Aes192Cbc(cbc::Encryptor), - #[cfg(feature = "aes-cbc")] - Aes256Cbc(cbc::Encryptor), - #[cfg(feature = "aes-ctr")] - Aes128Ctr(Ctr128BE), - #[cfg(feature = "aes-ctr")] - Aes192Ctr(Ctr128BE), - #[cfg(feature = "aes-ctr")] - Aes256Ctr(Ctr128BE), + #[cfg(feature = "aes")] + AesCbc(cbc::Encryptor), + #[cfg(feature = "aes")] + AesCtr(Ctr128BE), #[cfg(feature = "tdes")] TDesCbc(cbc::Encryptor), } @@ -55,46 +47,28 @@ impl Encryptor { cipher.check_key_and_iv(key, iv)?; let inner = match cipher { - #[cfg(feature = "aes-cbc")] - Cipher::Aes128Cbc => cbc::Encryptor::new_from_slices(key, iv).map(Inner::Aes128Cbc), - #[cfg(feature = "aes-cbc")] - Cipher::Aes192Cbc => cbc::Encryptor::new_from_slices(key, iv).map(Inner::Aes192Cbc), - #[cfg(feature = "aes-cbc")] - Cipher::Aes256Cbc => cbc::Encryptor::new_from_slices(key, iv).map(Inner::Aes256Cbc), - #[cfg(feature = "aes-ctr")] - Cipher::Aes128Ctr => Ctr128BE::new_from_slices(key, iv).map(Inner::Aes128Ctr), - #[cfg(feature = "aes-ctr")] - Cipher::Aes192Ctr => Ctr128BE::new_from_slices(key, iv).map(Inner::Aes192Ctr), - #[cfg(feature = "aes-ctr")] - Cipher::Aes256Ctr => Ctr128BE::new_from_slices(key, iv).map(Inner::Aes256Ctr), + #[cfg(feature = "aes")] + Cipher::Aes128Cbc | Cipher::Aes192Cbc | Cipher::Aes256Cbc => { + cbc::Encryptor::inner_iv_slice_init(Aes::new(key)?, iv).map(Inner::AesCbc) + } + #[cfg(feature = "aes")] + Cipher::Aes128Ctr | Cipher::Aes192Ctr | Cipher::Aes256Ctr => { + let core = CtrCore::inner_iv_slice_init(Aes::new(key)?, iv)?; + Ok(Inner::AesCtr(Ctr128BE::from_core(core))) + } #[cfg(feature = "tdes")] Cipher::TDesCbc => cbc::Encryptor::new_from_slices(key, iv).map(Inner::TDesCbc), _ => return Err(cipher.unsupported()), } .map_err(|_| Error::Length)?; - Ok(Self { inner }) + Ok(Self { inner, cipher }) } /// Get the cipher for this encryptor. #[must_use] pub fn cipher(&self) -> Cipher { - match &self.inner { - #[cfg(feature = "aes-cbc")] - Inner::Aes128Cbc(_) => Cipher::Aes128Cbc, - #[cfg(feature = "aes-cbc")] - Inner::Aes192Cbc(_) => Cipher::Aes192Cbc, - #[cfg(feature = "aes-cbc")] - Inner::Aes256Cbc(_) => Cipher::Aes256Cbc, - #[cfg(feature = "aes-ctr")] - Inner::Aes128Ctr(_) => Cipher::Aes128Ctr, - #[cfg(feature = "aes-ctr")] - Inner::Aes192Ctr(_) => Cipher::Aes192Ctr, - #[cfg(feature = "aes-ctr")] - Inner::Aes256Ctr(_) => Cipher::Aes256Ctr, - #[cfg(feature = "tdes")] - Inner::TDesCbc(_) => Cipher::TDesCbc, - } + self.cipher } /// Encrypt the given buffer in place. @@ -104,18 +78,12 @@ impl Encryptor { /// block size. pub fn encrypt(&mut self, buffer: &mut [u8]) -> Result<()> { match &mut self.inner { - #[cfg(feature = "aes-cbc")] - Inner::Aes128Cbc(cipher) => cbc_encrypt(cipher, buffer)?, - #[cfg(feature = "aes-cbc")] - Inner::Aes192Cbc(cipher) => cbc_encrypt(cipher, buffer)?, - #[cfg(feature = "aes-cbc")] - Inner::Aes256Cbc(cipher) => cbc_encrypt(cipher, buffer)?, - #[cfg(feature = "aes-ctr")] - Inner::Aes128Ctr(cipher) => ctr_encrypt(cipher, buffer)?, - #[cfg(feature = "aes-ctr")] - Inner::Aes192Ctr(cipher) => ctr_encrypt(cipher, buffer)?, - #[cfg(feature = "aes-ctr")] - Inner::Aes256Ctr(cipher) => ctr_encrypt(cipher, buffer)?, + #[cfg(feature = "aes")] + Inner::AesCbc(cipher) => cbc_encrypt(cipher, buffer)?, + #[cfg(feature = "aes")] + Inner::AesCtr(cipher) => cipher + .try_apply_keystream(buffer) + .map_err(|_| Error::Crypto)?, #[cfg(feature = "tdes")] Inner::TDesCbc(cipher) => cbc_encrypt(cipher, buffer)?, } @@ -133,7 +101,7 @@ impl Debug for Encryptor { } /// CBC mode encryption helper which assumes the input is unpadded and block-aligned. -#[cfg(any(feature = "aes-cbc", feature = "tdes"))] +#[cfg(any(feature = "aes", feature = "tdes"))] fn cbc_encrypt(encryptor: &mut cbc::Encryptor, buffer: &mut [u8]) -> Result<()> where C: BlockCipherEncrypt, @@ -148,14 +116,3 @@ where encryptor.encrypt_blocks(blocks); Ok(()) } - -/// CTR mode encryption helper which assumes the input is unpadded and block-aligned. -#[cfg(feature = "aes-ctr")] -pub(crate) fn ctr_encrypt(encryptor: &mut Ctr128BE, buffer: &mut [u8]) -> Result<()> -where - C: BlockCipherEncrypt + BlockSizeUser, -{ - encryptor - .try_apply_keystream(buffer) - .map_err(|_| Error::Crypto) -} diff --git a/ssh-cipher/src/error.rs b/ssh-cipher/src/error.rs index 11cd02e..ecfcb77 100644 --- a/ssh-cipher/src/error.rs +++ b/ssh-cipher/src/error.rs @@ -43,3 +43,9 @@ impl fmt::Display for Error { } impl core::error::Error for Error {} + +impl From for Error { + fn from(_: cipher::InvalidLength) -> Error { + Error::Length + } +} diff --git a/ssh-cipher/src/lib.rs b/ssh-cipher/src/lib.rs index 58ba3e1..241ef3c 100644 --- a/ssh-cipher/src/lib.rs +++ b/ssh-cipher/src/lib.rs @@ -8,17 +8,19 @@ mod error; +#[cfg(feature = "aes")] +mod aes; #[cfg(feature = "chacha20poly1305")] mod chacha20poly1305; -#[cfg(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes"))] +#[cfg(any(feature = "aes", feature = "tdes"))] mod decryptor; -#[cfg(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes"))] +#[cfg(any(feature = "aes", feature = "tdes"))] mod encryptor; pub use crate::error::{Error, Result}; pub use cipher; -#[cfg(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes"))] +#[cfg(any(feature = "aes", feature = "tdes"))] pub use crate::{decryptor::Decryptor, encryptor::Encryptor}; #[cfg(feature = "chacha20poly1305")] @@ -28,13 +30,13 @@ use cipher::array::{Array, typenum::U16}; use core::{fmt, str}; use encoding::{Label, LabelError}; -#[cfg(feature = "aes-gcm")] +#[cfg(feature = "aes")] use { aead::array::typenum::U12, aes_gcm::{Aes128Gcm, Aes256Gcm}, }; -#[cfg(any(feature = "aes-gcm", feature = "chacha20poly1305"))] +#[cfg(any(feature = "aes", feature = "chacha20poly1305"))] use aead::{AeadInOut, KeyInit}; /// AES-128 in block chaining (CBC) mode @@ -68,7 +70,7 @@ const CHACHA20_POLY1305: &str = "chacha20-poly1305@openssh.com"; const TDES_CBC: &str = "3des-cbc"; /// Nonce for `aes128-gcm@openssh.com`/`aes256-gcm@openssh.com`. -#[cfg(feature = "aes-gcm")] +#[cfg(feature = "aes")] pub type AesGcmNonce = Array; /// Authentication tag for ciphertext data. @@ -262,13 +264,10 @@ impl Cipher { /// # Errors /// Returns [`Error::Length`] in the event that `buffer` is not a multiple of the cipher's /// block size. - #[cfg_attr( - not(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes")), - allow(unused_variables) - )] + #[cfg_attr(not(any(feature = "aes", feature = "tdes")), allow(unused_variables))] pub fn decrypt(self, key: &[u8], iv: &[u8], buffer: &mut [u8], tag: Option) -> Result<()> { match self { - #[cfg(feature = "aes-gcm")] + #[cfg(feature = "aes")] Self::Aes128Gcm => { let cipher = Aes128Gcm::new_from_slice(key).map_err(|_| Error::KeySize)?; let nonce = iv.try_into().map_err(|_| Error::IvSize)?; @@ -279,7 +278,7 @@ impl Cipher { Ok(()) } - #[cfg(feature = "aes-gcm")] + #[cfg(feature = "aes")] Self::Aes256Gcm => { let cipher = Aes256Gcm::new_from_slice(key).map_err(|_| Error::KeySize)?; let nonce = iv.try_into().map_err(|_| Error::IvSize)?; @@ -300,7 +299,7 @@ impl Cipher { .map_err(|_| Error::Crypto) } // Use `Decryptor` for non-AEAD modes - #[cfg(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes"))] + #[cfg(any(feature = "aes", feature = "tdes"))] _ => { // Non-AEAD modes don't take a tag. if tag.is_some() { @@ -309,7 +308,7 @@ impl Cipher { self.decryptor(key, iv)?.decrypt(buffer) } - #[cfg(not(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes")))] + #[cfg(not(any(feature = "aes", feature = "tdes")))] _ => Err(self.unsupported()), } } @@ -321,7 +320,7 @@ impl Cipher { /// /// # Errors /// Propagates errors from [`Decryptor::new`]. - #[cfg(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes"))] + #[cfg(any(feature = "aes", feature = "tdes"))] pub fn decryptor(self, key: &[u8], iv: &[u8]) -> Result { Decryptor::new(self, key, iv) } @@ -331,13 +330,10 @@ impl Cipher { /// # Errors /// Returns [`Error::Length`] in the event that `buffer` is not a multiple of the cipher's /// block size. - #[cfg_attr( - not(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes")), - allow(unused_variables) - )] + #[cfg_attr(not(any(feature = "aes", feature = "tdes")), allow(unused_variables))] pub fn encrypt(self, key: &[u8], iv: &[u8], buffer: &mut [u8]) -> Result> { match self { - #[cfg(feature = "aes-gcm")] + #[cfg(feature = "aes")] Self::Aes128Gcm => { let cipher = Aes128Gcm::new_from_slice(key).map_err(|_| Error::KeySize)?; let nonce = iv.try_into().map_err(|_| Error::IvSize)?; @@ -347,7 +343,7 @@ impl Cipher { Ok(Some(tag)) } - #[cfg(feature = "aes-gcm")] + #[cfg(feature = "aes")] Self::Aes256Gcm => { let cipher = Aes256Gcm::new_from_slice(key).map_err(|_| Error::KeySize)?; let nonce = iv.try_into().map_err(|_| Error::IvSize)?; @@ -367,12 +363,12 @@ impl Cipher { Ok(Some(tag)) } // Use `Encryptor` for non-AEAD modes - #[cfg(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes"))] + #[cfg(any(feature = "aes", feature = "tdes"))] _ => { self.encryptor(key, iv)?.encrypt(buffer)?; Ok(None) } - #[cfg(not(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes")))] + #[cfg(not(any(feature = "aes", feature = "tdes")))] _ => Err(self.unsupported()), } } @@ -384,13 +380,13 @@ impl Cipher { /// /// # Errors /// Propagates errors from [`Encryptor::new`]. - #[cfg(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes"))] + #[cfg(any(feature = "aes", feature = "tdes"))] pub fn encryptor(self, key: &[u8], iv: &[u8]) -> Result { Encryptor::new(self, key, iv) } /// Check that the key and IV are the expected length for this cipher. - #[cfg(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes"))] + #[cfg(any(feature = "aes", feature = "tdes"))] fn check_key_and_iv(self, key: &[u8], iv: &[u8]) -> Result<()> { let (key_size, iv_size) = self .key_and_iv_size() diff --git a/ssh-cipher/tests/lib.rs b/ssh-cipher/tests/lib.rs index dc3b620..b8a14a4 100644 --- a/ssh-cipher/tests/lib.rs +++ b/ssh-cipher/tests/lib.rs @@ -7,21 +7,21 @@ use ssh_cipher::Cipher; fn round_trip() { const MSG: &[u8] = b"Testing 1 2 3..."; const CIPHERS: &[Cipher] = &[ - #[cfg(feature = "aes-cbc")] + #[cfg(feature = "aes")] Cipher::Aes128Cbc, - #[cfg(feature = "aes-cbc")] + #[cfg(feature = "aes")] Cipher::Aes192Cbc, - #[cfg(feature = "aes-cbc")] + #[cfg(feature = "aes")] Cipher::Aes256Cbc, - #[cfg(feature = "aes-ctr")] + #[cfg(feature = "aes")] Cipher::Aes128Ctr, - #[cfg(feature = "aes-ctr")] + #[cfg(feature = "aes")] Cipher::Aes192Ctr, - #[cfg(feature = "aes-ctr")] + #[cfg(feature = "aes")] Cipher::Aes256Ctr, - #[cfg(feature = "aes-gcm")] + #[cfg(feature = "aes")] Cipher::Aes128Gcm, - #[cfg(feature = "aes-gcm")] + #[cfg(feature = "aes")] Cipher::Aes256Gcm, #[cfg(feature = "chacha20poly1305")] Cipher::ChaCha20Poly1305, diff --git a/ssh-key/Cargo.toml b/ssh-key/Cargo.toml index bc01567..e2aabaf 100644 --- a/ssh-key/Cargo.toml +++ b/ssh-key/Cargo.toml @@ -67,16 +67,14 @@ ed25519 = ["dep:ed25519-dalek", "rand_core"] encryption = [ "dep:bcrypt-pbkdf", "alloc", - "cipher/aes-cbc", - "cipher/aes-ctr", - "cipher/aes-gcm", + "cipher/aes", "cipher/chacha20poly1305", "rand_core" ] p256 = ["dep:p256", "ecdsa"] p384 = ["dep:p384", "ecdsa"] p521 = ["dep:p521", "ecdsa"] -ppk = ["dep:hex", "alloc", "cipher/aes-cbc", "dep:hmac", "dep:argon2", "dep:sha1"] +ppk = ["dep:hex", "alloc", "cipher/aes", "dep:hmac", "dep:argon2", "dep:sha1"] rsa = ["dep:rsa", "alloc", "encoding/bigint", "rand_core"] sha1 = ["dep:sha1"] tdes = ["cipher/tdes", "encryption"]