From ab1f2784d9cfb88e0c7ba9f870575d18450e50cb Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Tue, 13 Aug 2024 14:17:52 +0000 Subject: [PATCH] ssh-cipher: allows to decrypt a payload without altering state (#266) When decrypting messages from the wire, we need to first decrypt the first couple of bytes (4 or 16 bytes) just to get the length of packet. Once we get the length of the packet we can decrypt the full payload. If we just used the Decryptor api as it is, we would alter the internal state. --- ssh-cipher/src/decryptor.rs | 54 ++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/ssh-cipher/src/decryptor.rs b/ssh-cipher/src/decryptor.rs index d21534a..13f495c 100644 --- a/ssh-cipher/src/decryptor.rs +++ b/ssh-cipher/src/decryptor.rs @@ -25,6 +25,7 @@ pub struct Decryptor { } /// Inner decryptor enum which is deliberately kept out of the public API. +#[derive(Clone)] enum Inner { #[cfg(feature = "aes-cbc")] Aes128Cbc(cbc::Decryptor), @@ -42,6 +43,31 @@ enum Inner { TDesCbc(cbc::Decryptor), } +impl Inner { + fn decrypt(&mut self, buffer: &mut [u8]) -> Result<()> { + #[cfg(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes"))] + match self { + #[cfg(feature = "aes-cbc")] + Self::Aes128Cbc(cipher) => cbc_decrypt(cipher, buffer), + #[cfg(feature = "aes-cbc")] + Self::Aes192Cbc(cipher) => cbc_decrypt(cipher, buffer), + #[cfg(feature = "aes-cbc")] + Self::Aes256Cbc(cipher) => cbc_decrypt(cipher, buffer), + #[cfg(feature = "aes-ctr")] + Self::Aes128Ctr(cipher) => ctr_decrypt(cipher, buffer), + #[cfg(feature = "aes-ctr")] + Self::Aes192Ctr(cipher) => ctr_decrypt(cipher, buffer), + #[cfg(feature = "aes-ctr")] + Self::Aes256Ctr(cipher) => ctr_decrypt(cipher, buffer), + #[cfg(feature = "tdes")] + Self::TDesCbc(cipher) => cbc_decrypt(cipher, buffer), + } + .map_err(|_| Error::Length)?; + + Ok(()) + } +} + impl Decryptor { /// Create a new decryptor object with the given [`Cipher`], key, and IV. pub fn new(cipher: Cipher, key: &[u8], iv: &[u8]) -> Result { @@ -94,26 +120,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 = "tdes")] - Inner::TDesCbc(cipher) => cbc_decrypt(cipher, buffer), - } - .map_err(|_| Error::Length)?; + self.inner.decrypt(buffer) + } - Ok(()) + /// Decrypt the given buffer in place without altering the internal state + /// + /// Returns [`Error::Length`] in the event that `buffer` is not a multiple of the cipher's + /// block size. + pub fn peek_decrypt(&self, buffer: &mut [u8]) -> Result<()> { + let mut inner = self.inner.clone(); + inner.decrypt(buffer) } }