|
1 | 1 | //! Stateful decryptor object. |
2 | 2 |
|
3 | 3 | use crate::{Cipher, Error, Result}; |
4 | | -use cipher::KeyIvInit; |
| 4 | +use cipher::{KeyIvInit, SetIvState}; |
5 | 5 | use core::fmt::{self, Debug}; |
6 | 6 |
|
7 | | -#[cfg(feature = "aes-ctr")] |
8 | | -use crate::{Ctr128BE, encryptor::ctr_encrypt as ctr_decrypt}; |
9 | 7 | #[cfg(any(feature = "aes-cbc", feature = "aes-ctr"))] |
10 | 8 | use aes::{Aes128, Aes192, Aes256}; |
11 | 9 | #[cfg(any(feature = "aes-cbc", feature = "tdes"))] |
12 | 10 | use cipher::{ |
13 | | - Block, |
| 11 | + Block, IvState, StreamCipherSeek, |
14 | 12 | block::{BlockCipherDecrypt, BlockModeDecrypt}, |
15 | 13 | }; |
16 | 14 | #[cfg(feature = "tdes")] |
17 | 15 | use des::TdesEde3; |
| 16 | +#[cfg(feature = "aes-ctr")] |
| 17 | +use {crate::encryptor::ctr_encrypt as ctr_decrypt, ctr::Ctr128BE}; |
18 | 18 |
|
19 | 19 | /// Stateful decryptor object for unauthenticated SSH symmetric ciphers. |
20 | 20 | /// |
@@ -43,6 +43,16 @@ enum Inner { |
43 | 43 | TDesCbc(cbc::Decryptor<TdesEde3>), |
44 | 44 | } |
45 | 45 |
|
| 46 | +/// Current IV state or position within the cipher. |
| 47 | +enum State { |
| 48 | + #[cfg(feature = "aes-cbc")] |
| 49 | + AesCbc(aes::Block), |
| 50 | + #[cfg(feature = "aes-ctr")] |
| 51 | + AesCtr(u64), |
| 52 | + #[cfg(feature = "tdes")] |
| 53 | + TDesCbc(Block<TdesEde3>), |
| 54 | +} |
| 55 | + |
46 | 56 | impl Decryptor { |
47 | 57 | /// Create a new decryptor object with the given [`Cipher`], `key`, and `iv` (i.e. |
48 | 58 | /// initialization vector). |
@@ -124,6 +134,77 @@ impl Decryptor { |
124 | 134 |
|
125 | 135 | Ok(()) |
126 | 136 | } |
| 137 | + |
| 138 | + /// Call the provided function with an ephemeral [`Decryptor`] state which will be reset upon |
| 139 | + /// completion, returning the result of the function. |
| 140 | + pub fn peek<T, F>(&mut self, mut f: F) -> Result<T> |
| 141 | + where |
| 142 | + F: FnMut(&mut Self) -> Result<T>, |
| 143 | + { |
| 144 | + let state = self.state(); |
| 145 | + let ret = f(self); |
| 146 | + self.set_state(state)?; |
| 147 | + ret |
| 148 | + } |
| 149 | + |
| 150 | + /// Get the current cipher state, i.e. IV or position within the stream cipher. |
| 151 | + fn state(&self) -> State { |
| 152 | + match &self.inner { |
| 153 | + #[cfg(feature = "aes-cbc")] |
| 154 | + Inner::Aes128Cbc(cipher) => State::AesCbc(cipher.iv_state()), |
| 155 | + #[cfg(feature = "aes-cbc")] |
| 156 | + Inner::Aes192Cbc(cipher) => State::AesCbc(cipher.iv_state()), |
| 157 | + #[cfg(feature = "aes-cbc")] |
| 158 | + Inner::Aes256Cbc(cipher) => State::AesCbc(cipher.iv_state()), |
| 159 | + #[cfg(feature = "aes-ctr")] |
| 160 | + Inner::Aes128Ctr(cipher) => State::AesCtr(cipher.current_pos()), |
| 161 | + #[cfg(feature = "aes-ctr")] |
| 162 | + Inner::Aes192Ctr(cipher) => State::AesCtr(cipher.current_pos()), |
| 163 | + #[cfg(feature = "aes-ctr")] |
| 164 | + Inner::Aes256Ctr(cipher) => State::AesCtr(cipher.current_pos()), |
| 165 | + #[cfg(feature = "tdes")] |
| 166 | + Inner::TDesCbc(cipher) => State::TDesCbc(cipher.iv_state()), |
| 167 | + } |
| 168 | + } |
| 169 | + |
| 170 | + /// Set the current cipher state. |
| 171 | + fn set_state(&mut self, state: State) -> Result<()> { |
| 172 | + match (&mut self.inner, state) { |
| 173 | + #[cfg(feature = "aes-cbc")] |
| 174 | + (Inner::Aes128Cbc(cipher), State::AesCbc(iv)) => { |
| 175 | + cipher.set_iv(&iv); |
| 176 | + Ok(()) |
| 177 | + } |
| 178 | + #[cfg(feature = "aes-cbc")] |
| 179 | + (Inner::Aes192Cbc(cipher), State::AesCbc(iv)) => { |
| 180 | + cipher.set_iv(&iv); |
| 181 | + Ok(()) |
| 182 | + } |
| 183 | + #[cfg(feature = "aes-cbc")] |
| 184 | + (Inner::Aes256Cbc(cipher), State::AesCbc(iv)) => { |
| 185 | + cipher.set_iv(&iv); |
| 186 | + Ok(()) |
| 187 | + } |
| 188 | + #[cfg(feature = "aes-ctr")] |
| 189 | + (Inner::Aes128Ctr(cipher), State::AesCtr(pos)) => { |
| 190 | + cipher.try_seek(pos).map_err(|_| Error::Crypto) |
| 191 | + } |
| 192 | + #[cfg(feature = "aes-ctr")] |
| 193 | + (Inner::Aes192Ctr(cipher), State::AesCtr(pos)) => { |
| 194 | + cipher.try_seek(pos).map_err(|_| Error::Crypto) |
| 195 | + } |
| 196 | + #[cfg(feature = "aes-ctr")] |
| 197 | + (Inner::Aes256Ctr(cipher), State::AesCtr(pos)) => { |
| 198 | + cipher.try_seek(pos).map_err(|_| Error::Crypto) |
| 199 | + } |
| 200 | + #[cfg(feature = "tdes")] |
| 201 | + (Inner::TDesCbc(cipher), State::TDesCbc(iv)) => { |
| 202 | + cipher.set_iv(&iv); |
| 203 | + Ok(()) |
| 204 | + } |
| 205 | + _ => Err(Error::Crypto), // should be unreachable |
| 206 | + } |
| 207 | + } |
127 | 208 | } |
128 | 209 |
|
129 | 210 | impl Debug for Decryptor { |
|
0 commit comments