Skip to content

Commit 7e237de

Browse files
committed
ssh-cipher: allows to decrypt a payload without altering state
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.
1 parent 16d0fac commit 7e237de

1 file changed

Lines changed: 35 additions & 19 deletions

File tree

ssh-cipher/src/decryptor.rs

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub struct Decryptor {
2525
}
2626

2727
/// Inner decryptor enum which is deliberately kept out of the public API.
28+
#[derive(Clone)]
2829
enum Inner {
2930
#[cfg(feature = "aes-cbc")]
3031
Aes128Cbc(cbc::Decryptor<Aes128>),
@@ -42,6 +43,31 @@ enum Inner {
4243
TDesCbc(cbc::Decryptor<TdesEde3>),
4344
}
4445

46+
impl Inner {
47+
fn decrypt(&mut self, buffer: &mut [u8]) -> Result<()> {
48+
#[cfg(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes"))]
49+
match self {
50+
#[cfg(feature = "aes-cbc")]
51+
Self::Aes128Cbc(cipher) => cbc_decrypt(cipher, buffer),
52+
#[cfg(feature = "aes-cbc")]
53+
Self::Aes192Cbc(cipher) => cbc_decrypt(cipher, buffer),
54+
#[cfg(feature = "aes-cbc")]
55+
Self::Aes256Cbc(cipher) => cbc_decrypt(cipher, buffer),
56+
#[cfg(feature = "aes-ctr")]
57+
Self::Aes128Ctr(cipher) => ctr_decrypt(cipher, buffer),
58+
#[cfg(feature = "aes-ctr")]
59+
Self::Aes192Ctr(cipher) => ctr_decrypt(cipher, buffer),
60+
#[cfg(feature = "aes-ctr")]
61+
Self::Aes256Ctr(cipher) => ctr_decrypt(cipher, buffer),
62+
#[cfg(feature = "tdes")]
63+
Self::TDesCbc(cipher) => cbc_decrypt(cipher, buffer),
64+
}
65+
.map_err(|_| Error::Length)?;
66+
67+
Ok(())
68+
}
69+
}
70+
4571
impl Decryptor {
4672
/// Create a new decryptor object with the given [`Cipher`], key, and IV.
4773
pub fn new(cipher: Cipher, key: &[u8], iv: &[u8]) -> Result<Self> {
@@ -94,26 +120,16 @@ impl Decryptor {
94120
/// Returns [`Error::Length`] in the event that `buffer` is not a multiple of the cipher's
95121
/// block size.
96122
pub fn decrypt(&mut self, buffer: &mut [u8]) -> Result<()> {
97-
#[cfg(any(feature = "aes-cbc", feature = "aes-ctr", feature = "tdes"))]
98-
match &mut self.inner {
99-
#[cfg(feature = "aes-cbc")]
100-
Inner::Aes128Cbc(cipher) => cbc_decrypt(cipher, buffer),
101-
#[cfg(feature = "aes-cbc")]
102-
Inner::Aes192Cbc(cipher) => cbc_decrypt(cipher, buffer),
103-
#[cfg(feature = "aes-cbc")]
104-
Inner::Aes256Cbc(cipher) => cbc_decrypt(cipher, buffer),
105-
#[cfg(feature = "aes-ctr")]
106-
Inner::Aes128Ctr(cipher) => ctr_decrypt(cipher, buffer),
107-
#[cfg(feature = "aes-ctr")]
108-
Inner::Aes192Ctr(cipher) => ctr_decrypt(cipher, buffer),
109-
#[cfg(feature = "aes-ctr")]
110-
Inner::Aes256Ctr(cipher) => ctr_decrypt(cipher, buffer),
111-
#[cfg(feature = "tdes")]
112-
Inner::TDesCbc(cipher) => cbc_decrypt(cipher, buffer),
113-
}
114-
.map_err(|_| Error::Length)?;
123+
self.inner.decrypt(buffer)
124+
}
115125

116-
Ok(())
126+
/// Decrypt the given buffer in place without altering the internal state
127+
///
128+
/// Returns [`Error::Length`] in the event that `buffer` is not a multiple of the cipher's
129+
/// block size.
130+
pub fn peek_decrypt(&self, buffer: &mut [u8]) -> Result<()> {
131+
let mut inner = self.inner.clone();
132+
inner.decrypt(buffer)
117133
}
118134
}
119135

0 commit comments

Comments
 (0)