Skip to main content

ssh_cipher/block_cipher/
encryptor.rs

1//! Stateful encryptor object.
2
3use super::{BlockMode, State, sealed::BlockCipher};
4use crate::{Cipher, Error, Result};
5use ::cipher::{
6    Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockModeEncBackend, BlockModeEncClosure,
7    BlockModeEncrypt,
8    common::{
9        BlockSizeUser, InnerUser, ParBlocksSizeUser,
10        array::{ArraySize, sizes::U1},
11    },
12    inout::InOut,
13};
14use core::fmt::{self, Debug};
15
16/// Stateful encryptor object for unauthenticated symmetric ciphers used in the SSH packet
17/// encryption protocol.
18pub struct Encryptor<C: BlockCipher> {
19    /// Inner block cipher.
20    cipher: C,
21
22    /// State of the block cipher's mode of operation.
23    state: State<C::BlockSize>,
24}
25
26impl<C: BlockCipher> Encryptor<C> {
27    /// Create a new encryptor object with the given [`Cipher`], `key`, and `iv` (i.e.
28    /// initialization vector).
29    ///
30    /// # Errors
31    /// - Returns [`Error::Crypto`] if the given `cipher` cannot be used with `Encryptor`.
32    /// - Returns [`Error::Length`] if `key` or `iv` are the wrong length for the given `cipher`.
33    /// - Returns [`Error::UnsupportedCipher`] if support for the given `cipher` is not enabled
34    ///   in the crate features.
35    pub fn new(cipher: Cipher, key: &[u8], iv: &[u8]) -> Result<Self> {
36        if !C::is_supported(cipher) {
37            return Err(Error::UnsupportedCipher(cipher));
38        }
39
40        let mode = cipher.block_mode().ok_or(Error::Crypto)?;
41        let cipher = C::new_from_slice(key)?;
42        let state = State::new_from_slice(mode, iv)?;
43        Ok(Self { cipher, state })
44    }
45}
46
47impl<C: BlockCipher> BlockModeEncrypt for Encryptor<C> {
48    fn encrypt_with_backend(&mut self, f: impl BlockModeEncClosure<BlockSize = Self::BlockSize>) {
49        struct Closure<'a, BS, BC>
50        where
51            BS: ArraySize,
52            BC: BlockModeEncClosure<BlockSize = BS>,
53        {
54            state: &'a mut State<BS>,
55            f: BC,
56        }
57
58        impl<BS, BC> BlockSizeUser for Closure<'_, BS, BC>
59        where
60            BS: ArraySize,
61            BC: BlockModeEncClosure<BlockSize = BS>,
62        {
63            type BlockSize = BS;
64        }
65
66        impl<BS, BC> BlockCipherEncClosure for Closure<'_, BS, BC>
67        where
68            BS: ArraySize,
69            BC: BlockModeEncClosure<BlockSize = BS>,
70        {
71            #[inline(always)]
72            fn call<B: BlockCipherEncBackend<BlockSize = Self::BlockSize>>(
73                self,
74                cipher_backend: &B,
75            ) {
76                let Self { state, f } = self;
77                f.call(&mut Backend {
78                    state,
79                    cipher_backend,
80                });
81            }
82        }
83
84        let Self { cipher, state } = self;
85        cipher.encrypt_with_backend(Closure { state, f });
86    }
87}
88
89impl<C: BlockCipher> BlockSizeUser for Encryptor<C> {
90    type BlockSize = C::BlockSize;
91}
92
93impl<C: BlockCipher> Debug for Encryptor<C> {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        f.debug_struct("Encryptor").finish_non_exhaustive()
96    }
97}
98
99impl<C: BlockCipher> InnerUser for Encryptor<C> {
100    type Inner = C;
101}
102
103struct Backend<'a, BS, BK>
104where
105    BS: ArraySize,
106    BK: BlockCipherEncBackend<BlockSize = BS>,
107{
108    state: &'a mut State<BS>,
109    cipher_backend: &'a BK,
110}
111
112impl<BS, BK> BlockSizeUser for Backend<'_, BS, BK>
113where
114    BS: ArraySize,
115    BK: BlockCipherEncBackend<BlockSize = BS>,
116{
117    type BlockSize = BS;
118}
119
120impl<BS, BK> ParBlocksSizeUser for Backend<'_, BS, BK>
121where
122    BS: ArraySize,
123    BK: BlockCipherEncBackend<BlockSize = BS>,
124{
125    // CBC encryption cannot be performed in parallel
126    // TODO(tarcieri): parallel encryption support for CTR mode, serial for CBC
127    type ParBlocksSize = U1;
128}
129
130impl<BS, BK> BlockModeEncBackend for Backend<'_, BS, BK>
131where
132    BS: ArraySize,
133    BK: BlockCipherEncBackend<BlockSize = BS>,
134{
135    #[inline(always)]
136    fn encrypt_block(&mut self, mut block: InOut<'_, '_, Block<Self>>) {
137        let mut t = block.clone_in();
138
139        match self.state.mode() {
140            BlockMode::Cbc => {
141                self.state.xor_into(&mut t);
142                self.cipher_backend.encrypt_block(InOut::from(&mut t));
143                self.state.as_mut().copy_from_slice(&t);
144            }
145            BlockMode::Ctr => {
146                let mut pad = self.state.clone();
147                self.cipher_backend.encrypt_block(pad.as_mut().into());
148                pad.xor_into(&mut t);
149                self.state.increment_counter();
150            }
151        }
152
153        *block.get_out() = t;
154    }
155}