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