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    fn encoded_len(&self) -> Result<usize, Error>;
21
22    /// Encode this value using the provided [`Writer`].
23    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error>;
24
25    /// Return the length of this type after encoding when prepended with a
26    /// `uint32` length prefix.
27    fn encoded_len_prefixed(&self) -> Result<usize, Error> {
28        [4, self.encoded_len()?].checked_sum()
29    }
30
31    /// Encode this value, first prepending a `uint32` length prefix
32    /// set to [`Encode::encoded_len`].
33    fn encode_prefixed(&self, writer: &mut impl Writer) -> Result<(), Error> {
34        self.encoded_len()?.encode(writer)?;
35        self.encode(writer)
36    }
37
38    /// Encode this value, returning a `Vec<u8>` containing the encoded message.
39    #[cfg(feature = "alloc")]
40    fn encode_vec(&self) -> Result<Vec<u8>, Error> {
41        let mut ret = Vec::with_capacity(self.encoded_len()?);
42        self.encode(&mut ret)?;
43        Ok(ret)
44    }
45
46    /// Encode this value, returning a [`BytesMut`] containing the encoded message.
47    #[cfg(feature = "bytes")]
48    fn encode_bytes(&self) -> Result<BytesMut, Error> {
49        let mut ret = BytesMut::with_capacity(self.encoded_len()?);
50        self.encode(&mut ret)?;
51        Ok(ret)
52    }
53}
54
55/// Encode a single `byte` to the writer.
56impl Encode for u8 {
57    fn encoded_len(&self) -> Result<usize, Error> {
58        Ok(1)
59    }
60
61    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
62        writer.write(&[*self])
63    }
64}
65
66/// Encode a `boolean` as described in [RFC4251 § 5]:
67///
68/// > A boolean value is stored as a single byte.  The value 0
69/// > represents FALSE, and the value 1 represents TRUE.  All non-zero
70/// > values MUST be interpreted as TRUE; however, applications MUST NOT
71/// > store values other than 0 and 1.
72///
73/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
74impl Encode for bool {
75    fn encoded_len(&self) -> Result<usize, Error> {
76        Ok(1)
77    }
78
79    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
80        if *self {
81            1u8.encode(writer)
82        } else {
83            0u8.encode(writer)
84        }
85    }
86}
87
88/// Encode a `uint32` as described in [RFC4251 § 5]:
89///
90/// > Represents a 32-bit unsigned integer.  Stored as four bytes in the
91/// > order of decreasing significance (network byte order).
92/// > For example: the value 699921578 (0x29b7f4aa) is stored as 29 b7 f4 aa.
93///
94/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
95impl Encode for u32 {
96    fn encoded_len(&self) -> Result<usize, Error> {
97        Ok(4)
98    }
99
100    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
101        writer.write(&self.to_be_bytes())
102    }
103}
104
105/// Encode a `uint64` as described in [RFC4251 § 5]:
106///
107/// > Represents a 64-bit unsigned integer.  Stored as eight bytes in
108/// > the order of decreasing significance (network byte order).
109///
110/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
111impl Encode for u64 {
112    fn encoded_len(&self) -> Result<usize, Error> {
113        Ok(8)
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 `usize` as a `uint32` as described in [RFC4251 § 5].
122///
123/// Uses [`Encode`] impl on `u32` after converting from a `usize`, handling
124/// potential overflow if `usize` is bigger than `u32`.
125///
126/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
127impl Encode for usize {
128    fn encoded_len(&self) -> Result<usize, Error> {
129        Ok(4)
130    }
131
132    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
133        u32::try_from(*self)?.encode(writer)
134    }
135}
136
137/// Encodes `[u8]` into `string` as described in [RFC4251 § 5]:
138///
139/// > Arbitrary length binary string.  Strings are allowed to contain
140/// > arbitrary binary data, including null characters and 8-bit
141/// > characters.  They are stored as a uint32 containing its length
142/// > (number of bytes that follow) and zero (= empty string) or more
143/// > bytes that are the value of the string.  Terminating null
144/// > characters are not used.
145///
146/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
147impl Encode for [u8] {
148    fn encoded_len(&self) -> Result<usize, Error> {
149        [4, self.len()].checked_sum()
150    }
151
152    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
153        self.len().encode(writer)?;
154        writer.write(self)
155    }
156}
157
158/// Encodes byte array using `byte[n]` encoding as described in [RFC4251 § 5]:
159///
160/// > A byte represents an arbitrary 8-bit value (octet).  Fixed length
161/// > data is sometimes represented as an array of bytes, written
162/// > `byte[n]`, where n is the number of bytes in the array.
163///
164/// Note that unlike `string`, this type is encoded without a length prefix,
165/// but instead implicitly obtains its length as `N`.
166///
167/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
168impl<const N: usize> Encode for [u8; N] {
169    fn encoded_len(&self) -> Result<usize, Error> {
170        Ok(N)
171    }
172
173    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
174        writer.write(self)
175    }
176}
177
178/// Encode a `string` as described in [RFC4251 § 5]:
179///
180/// > Arbitrary length binary string.  Strings are allowed to contain
181/// > arbitrary binary data, including null characters and 8-bit
182/// > characters.  They are stored as a uint32 containing its length
183/// > (number of bytes that follow) and zero (= empty string) or more
184/// > bytes that are the value of the string.  Terminating null
185/// > characters are not used.
186/// >
187/// > Strings are also used to store text.  In that case, US-ASCII is
188/// > used for internal names, and ISO-10646 UTF-8 for text that might
189/// > be displayed to the user.  The terminating null character SHOULD
190/// > NOT normally be stored in the string.  For example: the US-ASCII
191/// > string "testing" is represented as 00 00 00 07 t e s t i n g.  The
192/// > UTF-8 mapping does not alter the encoding of US-ASCII characters.
193///
194/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
195impl Encode for &str {
196    fn encoded_len(&self) -> Result<usize, Error> {
197        self.as_bytes().encoded_len()
198    }
199
200    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
201        self.as_bytes().encode(writer)
202    }
203}
204
205#[cfg(feature = "alloc")]
206impl Encode for Vec<u8> {
207    fn encoded_len(&self) -> Result<usize, Error> {
208        self.as_slice().encoded_len()
209    }
210
211    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
212        self.as_slice().encode(writer)
213    }
214}
215
216#[cfg(feature = "alloc")]
217impl Encode for String {
218    fn encoded_len(&self) -> Result<usize, Error> {
219        self.as_str().encoded_len()
220    }
221
222    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
223        self.as_str().encode(writer)
224    }
225}
226
227#[cfg(feature = "alloc")]
228impl Encode for Vec<String> {
229    fn encoded_len(&self) -> Result<usize, Error> {
230        self.iter().try_fold(4usize, |acc, string| {
231            acc.checked_add(string.encoded_len()?).ok_or(Error::Length)
232        })
233    }
234
235    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
236        self.encoded_len()?
237            .checked_sub(4)
238            .ok_or(Error::Length)?
239            .encode(writer)?;
240
241        for entry in self {
242            entry.encode(writer)?;
243        }
244
245        Ok(())
246    }
247}
248
249#[cfg(feature = "bytes")]
250impl Encode for Bytes {
251    fn encoded_len(&self) -> Result<usize, Error> {
252        self.as_ref().encoded_len()
253    }
254
255    fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
256        self.as_ref().encode(writer)
257    }
258}