Skip to main content

ssh_encoding/
encode.rs

1//! Encoder-side implementation of the SSH protocol's data type representations
2//! as described in [RFC4251 § 5].
3//!
4//! [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
5
6use crate::{Error, checked::CheckedSum, writer::Writer};
7use core::str;
8
9#[cfg(feature = "alloc")]
10use alloc::{string::String, vec::Vec};
11
12#[cfg(feature = "bytes")]
13use bytes::{Bytes, BytesMut};
14
15/// Encoding trait.
16///
17/// This trait describes how to encode a given type.
18pub trait Encode {
19    /// Get the length of this type encoded in bytes, prior to Base64 encoding.
20    ///
21    /// # Errors
22    /// Returns errors specific to the concrete implementation of this trait.
23    fn encoded_len(&self) -> Result<usize, Error>;
24
25    /// Encode this value using the provided [`Writer`].
26    ///
27    /// # Errors
28    /// Returns errors specific to the concrete implementation of this trait.
29    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error>;
30
31    /// Return the length of this type after encoding when prepended with a `uint32` length prefix.
32    ///
33    /// # Errors
34    /// Returns errors specific to the concrete implementation of this trait.
35    fn encoded_len_prefixed(&self) -> Result<usize, Error> {
36        [4, self.encoded_len()?].checked_sum()
37    }
38
39    /// Encode this value, first prepending a `uint32` length prefix set to [`Encode::encoded_len`].
40    ///
41    /// # Errors
42    /// Returns errors specific to the concrete implementation of this trait.
43    fn encode_prefixed(&self, writer: &mut impl Writer) -> Result<(), Error> {
44        self.encoded_len()?.encode(writer)?;
45        self.encode(writer)
46    }
47
48    /// Encode this value, returning a `Vec<u8>` containing the encoded message.
49    ///
50    /// # Errors
51    /// Returns errors specific to the concrete implementation of this trait.
52    #[cfg(feature = "alloc")]
53    fn encode_vec(&self) -> Result<Vec<u8>, Error> {
54        let mut ret = Vec::with_capacity(self.encoded_len()?);
55        self.encode(&mut ret)?;
56        Ok(ret)
57    }
58
59    /// Encode this value, returning a [`BytesMut`] containing the encoded message.
60    ///
61    /// # Errors
62    /// Returns errors specific to the concrete implementation of this trait.
63    #[cfg(feature = "bytes")]
64    fn encode_bytes(&self) -> Result<BytesMut, Error> {
65        let mut ret = BytesMut::with_capacity(self.encoded_len()?);
66        self.encode(&mut ret)?;
67        Ok(ret)
68    }
69}
70
71/// Encode a single `byte` to the writer.
72impl Encode for u8 {
73    fn encoded_len(&self) -> Result<usize, Error> {
74        Ok(1)
75    }
76
77    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
78        writer.write(&[*self])
79    }
80}
81
82/// Encode a `boolean` as described in [RFC4251 § 5]:
83///
84/// > A boolean value is stored as a single byte.  The value 0
85/// > represents FALSE, and the value 1 represents TRUE.  All non-zero
86/// > values MUST be interpreted as TRUE; however, applications MUST NOT
87/// > store values other than 0 and 1.
88///
89/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
90impl Encode for bool {
91    fn encoded_len(&self) -> Result<usize, Error> {
92        Ok(1)
93    }
94
95    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
96        if *self {
97            1u8.encode(writer)
98        } else {
99            0u8.encode(writer)
100        }
101    }
102}
103
104/// Encode a `uint32` as described in [RFC4251 § 5]:
105///
106/// > Represents a 32-bit unsigned integer.  Stored as four bytes in the
107/// > order of decreasing significance (network byte order).
108/// > For example: the value 699921578 (0x29b7f4aa) is stored as 29 b7 f4 aa.
109///
110/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
111impl Encode for u32 {
112    fn encoded_len(&self) -> Result<usize, Error> {
113        Ok(4)
114    }
115
116    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
117        writer.write(&self.to_be_bytes())
118    }
119}
120
121/// Encode a `uint64` as described in [RFC4251 § 5]:
122///
123/// > Represents a 64-bit unsigned integer.  Stored as eight bytes in
124/// > the order of decreasing significance (network byte order).
125///
126/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
127impl Encode for u64 {
128    fn encoded_len(&self) -> Result<usize, Error> {
129        Ok(8)
130    }
131
132    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
133        writer.write(&self.to_be_bytes())
134    }
135}
136
137/// Encode a `usize` as a `uint32` as described in [RFC4251 § 5].
138///
139/// Uses [`Encode`] impl on `u32` after converting from a `usize`, handling
140/// potential overflow if `usize` is bigger than `u32`.
141///
142/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
143impl Encode for usize {
144    fn encoded_len(&self) -> Result<usize, Error> {
145        Ok(4)
146    }
147
148    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
149        u32::try_from(*self)?.encode(writer)
150    }
151}
152
153/// Encodes `[u8]` into `string` as described in [RFC4251 § 5]:
154///
155/// > Arbitrary length binary string.  Strings are allowed to contain
156/// > arbitrary binary data, including null characters and 8-bit
157/// > characters.  They are stored as a uint32 containing its length
158/// > (number of bytes that follow) and zero (= empty string) or more
159/// > bytes that are the value of the string.  Terminating null
160/// > characters are not used.
161///
162/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
163impl Encode for [u8] {
164    fn encoded_len(&self) -> Result<usize, Error> {
165        [4, self.len()].checked_sum()
166    }
167
168    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
169        self.len().encode(writer)?;
170        writer.write(self)
171    }
172}
173
174/// Encodes byte array using `byte[n]` encoding as described in [RFC4251 § 5]:
175///
176/// > A byte represents an arbitrary 8-bit value (octet).  Fixed length
177/// > data is sometimes represented as an array of bytes, written
178/// > `byte[n]`, where n is the number of bytes in the array.
179///
180/// Note that unlike `string`, this type is encoded without a length prefix,
181/// but instead implicitly obtains its length as `N`.
182///
183/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
184impl<const N: usize> Encode for [u8; N] {
185    fn encoded_len(&self) -> Result<usize, Error> {
186        Ok(N)
187    }
188
189    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
190        writer.write(self)
191    }
192}
193
194/// A macro to implement `Encode` for a type by delegating to some transformed version of `self`.
195macro_rules! impl_by_delegation {
196    (
197        $(
198            $(#[$attr:meta])*
199            impl $( ($($generics:tt)+) )? Encode for $type:ty where $self:ident -> $delegate:expr;
200        )+
201    ) => {
202        $(
203            $(#[$attr])*
204            impl $(< $($generics)* >)? Encode for $type  {
205                fn encoded_len(&$self) -> Result<usize, Error> {
206                    $delegate.encoded_len()
207                }
208
209                fn encode(&$self, writer: &mut impl Writer) -> Result<(), Error> {
210                    $delegate.encode(writer)
211                }
212            }
213        )+
214    };
215}
216
217impl_by_delegation!(
218    /// Encode a `string` as described in [RFC4251 § 5]:
219    ///
220    /// > Arbitrary length binary string.  Strings are allowed to contain
221    /// > arbitrary binary data, including null characters and 8-bit
222    /// > characters.  They are stored as a uint32 containing its length
223    /// > (number of bytes that follow) and zero (= empty string) or more
224    /// > bytes that are the value of the string.  Terminating null
225    /// > characters are not used.
226    /// >
227    /// > Strings are also used to store text.  In that case, US-ASCII is
228    /// > used for internal names, and ISO-10646 UTF-8 for text that might
229    /// > be displayed to the user.  The terminating null character SHOULD
230    /// > NOT normally be stored in the string.  For example: the US-ASCII
231    /// > string "testing" is represented as 00 00 00 07 t e s t i n g.  The
232    /// > UTF-8 mapping does not alter the encoding of US-ASCII characters.
233    ///
234    /// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
235    impl Encode for str where self -> self.as_bytes();
236
237    #[cfg(feature = "alloc")]
238    impl Encode for Vec<u8> where self -> self.as_slice();
239    #[cfg(feature = "alloc")]
240    impl Encode for String where self -> self.as_bytes();
241    #[cfg(feature = "bytes")]
242    impl Encode for Bytes where self -> self.as_ref();
243
244    // While deref coercion ensures that `&E` can use the `Encode` trait methods, it will not be
245    // allowd in trait bounds, as `&E` does not implement `Encode` itself just because `E: Encode`.
246    // A blanket impl for `&E` would be the most generic, but that collides with the `Label` trait's
247    // blanket impl. Instead, we can do it explicitly for the immediatley relevant base types.
248    impl Encode for &str where self -> **self;
249    impl Encode for &[u8] where self -> **self;
250    #[cfg(feature = "alloc")]
251    impl Encode for &Vec<u8> where self -> **self;
252    #[cfg(feature = "alloc")]
253    impl Encode for &String where self -> **self;
254    #[cfg(feature = "bytes")]
255    impl Encode for &Bytes where self -> **self;
256
257);
258
259/// A trait indicating that the type is encoded like an RFC4251 string.
260///
261/// Implementing this trait allows encoding sequences of the type as a  string of strings.
262///
263/// A `string` is described in [RFC4251 § 5]:
264///
265/// > Arbitrary length binary string.  Strings are allowed to contain
266/// > arbitrary binary data, including null characters and 8-bit
267/// > characters.  They are stored as a uint32 containing its length
268/// > (number of bytes that follow) and zero (= empty string) or more
269/// > bytes that are the value of the string.  Terminating null
270/// > characters are not used.
271/// >
272/// > Strings are also used to store text.  In that case, US-ASCII is
273/// > used for internal names, and ISO-10646 UTF-8 for text that might
274/// > be displayed to the user.  The terminating null character SHOULD
275/// > NOT normally be stored in the string.  For example: the US-ASCII
276/// > string "testing" is represented as 00 00 00 07 t e s t i n g.  The
277/// > UTF-8 mapping does not alter the encoding of US-ASCII characters.
278///
279/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
280pub trait Rfc4251String: Encode {}
281
282impl Rfc4251String for str {}
283impl Rfc4251String for [u8] {}
284#[cfg(feature = "alloc")]
285impl Rfc4251String for String {}
286#[cfg(feature = "alloc")]
287impl Rfc4251String for Vec<u8> {}
288#[cfg(feature = "bytes")]
289impl Rfc4251String for Bytes {}
290
291/// Any reference to [`Rfc4251String`] is itself [`Rfc4251String`] if `&T: Encode`.
292impl<'a, T> Rfc4251String for &'a T
293where
294    T: Rfc4251String + ?Sized,
295    &'a T: Encode,
296{
297}
298
299/// Encode a slice of string-like types as a string wrapping all the entries.
300impl<T: Rfc4251String> Encode for [T] {
301    fn encoded_len(&self) -> Result<usize, Error> {
302        self.iter().try_fold(4usize, |acc, string| {
303            acc.checked_add(string.encoded_len()?).ok_or(Error::Length)
304        })
305    }
306
307    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
308        self.encoded_len()?
309            .checked_sub(4)
310            .ok_or(Error::Length)?
311            .encode(writer)?;
312        self.iter().try_fold((), |(), entry| entry.encode(writer))
313    }
314}
315
316impl_by_delegation!(
317    #[cfg(feature = "alloc")]
318    impl (T: Rfc4251String) Encode for Vec<T> where self -> self.as_slice();
319);