ssh_packet/packet/
mod.rs

1use binrw::{
2    meta::{ReadEndian, WriteEndian},
3    BinRead, BinWrite,
4};
5
6mod cipher;
7pub use cipher::{CipherCore, OpeningCipher, SealingCipher};
8
9mod mac;
10pub use mac::Mac;
11
12/// Maximum size for a SSH packet, coincidentally this is
13/// the maximum size for a TCP packet.
14pub const PACKET_MAX_SIZE: usize = u16::MAX as usize;
15
16/// Minimum size for a SSH packet, coincidentally this is
17/// the largest block cipher's block-size.
18pub const PACKET_MIN_SIZE: usize = 16;
19
20/// A packet _deserialization_ & _serialization_ helper.
21///
22/// see <https://datatracker.ietf.org/doc/html/rfc4253#section-6>.
23#[derive(Debug, Clone)]
24pub struct Packet(pub Vec<u8>);
25
26impl Packet {
27    /// Try to deserialize the [`Packet`] into `T`.
28    pub fn to<T: for<'a> BinRead<Args<'a> = ()> + ReadEndian>(&self) -> Result<T, binrw::Error> {
29        T::read(&mut std::io::Cursor::new(&self.0))
30    }
31
32    #[cfg(feature = "futures")]
33    #[cfg_attr(docsrs, doc(cfg(feature = "futures")))]
34    /// Read a [`Packet`] from the provided asynchronous `reader`.
35    pub async fn from_reader<R, C>(reader: &mut R, cipher: &mut C, seq: u32) -> Result<Self, C::Err>
36    where
37        R: futures::io::AsyncRead + Unpin,
38        C: OpeningCipher,
39    {
40        use futures::io::AsyncReadExt;
41
42        let mut buf = vec![0; cipher.block_size()];
43        reader.read_exact(&mut buf[..]).await?;
44
45        if !cipher.mac().etm() {
46            cipher.decrypt(&mut buf[..])?;
47        }
48
49        let len = u32::from_be_bytes(
50            buf[..4]
51                .try_into()
52                .expect("buffer of size 4 is not of size 4"),
53        );
54
55        if len as usize > PACKET_MAX_SIZE {
56            return Err(binrw::Error::Custom {
57                pos: len as u64,
58                err: Box::new(format!("packet size too large, {len} > {PACKET_MAX_SIZE}")),
59            })?;
60        }
61
62        // Read the rest of the data from the reader
63        buf.resize(std::mem::size_of_val(&len) + len as usize, 0);
64        reader.read_exact(&mut buf[cipher.block_size()..]).await?;
65
66        let mut mac = vec![0; cipher.mac().size()];
67        reader.read_exact(&mut mac[..]).await?;
68
69        if cipher.mac().etm() {
70            cipher.open(&buf, mac, seq)?;
71            cipher.decrypt(&mut buf[4..])?;
72        } else {
73            cipher.decrypt(&mut buf[cipher.block_size()..])?;
74            cipher.open(&buf, mac, seq)?;
75        }
76
77        let (padlen, mut decrypted) =
78            buf[4..].split_first().ok_or_else(|| binrw::Error::Custom {
79                pos: 0x4,
80                err: Box::new(format!("Packet size too small ({len})")),
81            })?;
82
83        if *padlen as usize > len as usize - 1 {
84            return Err(binrw::Error::Custom {
85                pos: 0x4,
86                err: Box::new(format!("Padding size too large, {padlen} > {} - 1", len)),
87            })?;
88        }
89
90        let mut payload = vec![0; len as usize - *padlen as usize - std::mem::size_of_val(padlen)];
91        std::io::Read::read_exact(&mut decrypted, &mut payload[..])?;
92
93        let payload = cipher.decompress(payload)?;
94
95        Ok(Self(payload))
96    }
97
98    #[cfg(feature = "futures")]
99    #[cfg_attr(docsrs, doc(cfg(feature = "futures")))]
100    /// Write the [`Packet`] to the provided asynchronous `writer`.
101    pub async fn to_writer<W, C>(
102        &self,
103        writer: &mut W,
104        cipher: &mut C,
105        seq: u32,
106    ) -> Result<(), C::Err>
107    where
108        W: futures::io::AsyncWrite + Unpin,
109        C: SealingCipher,
110    {
111        use futures::AsyncWriteExt;
112
113        let compressed = cipher.compress(&self.0)?;
114
115        let padding = cipher.padding(compressed.len());
116        let buf = cipher.pad(compressed, padding)?;
117        let mut buf = [(buf.len() as u32).to_be_bytes().to_vec(), buf].concat();
118
119        let (buf, mac) = if cipher.mac().etm() {
120            cipher.encrypt(&mut buf[4..])?;
121            let mac = cipher.seal(&buf, seq)?;
122
123            (buf, mac)
124        } else {
125            let mac = cipher.seal(&buf, seq)?;
126            cipher.encrypt(&mut buf[..])?;
127
128            (buf, mac)
129        };
130
131        writer.write_all(&buf).await?;
132        writer.write_all(&mac).await?;
133
134        Ok(())
135    }
136}
137
138impl std::ops::Deref for Packet {
139    type Target = [u8];
140
141    fn deref(&self) -> &Self::Target {
142        &self.0
143    }
144}
145
146/// Allow types implementing [`BinWrite`] to be easily converted to a [`Packet`].
147pub trait IntoPacket {
148    /// Convert the current type to a [`Packet`].
149    fn into_packet(self) -> Packet;
150}
151
152impl IntoPacket for Packet {
153    fn into_packet(self) -> Packet {
154        self
155    }
156}
157
158impl<T: for<'a> BinWrite<Args<'a> = ()> + WriteEndian> IntoPacket for &T {
159    fn into_packet(self) -> Packet {
160        let mut buffer = std::io::Cursor::new(Vec::new());
161        self.write(&mut buffer)
162            .expect("failed to convert `impl BinWrite` type to Packet");
163
164        Packet(buffer.into_inner())
165    }
166}