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