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
56use crate::{checked::CheckedSum, writer::Writer, Error};
7use core::str;
89#[cfg(feature = "alloc")]
10use alloc::{string::String, vec::Vec};
1112#[cfg(feature = "bytes")]
13use bytes::Bytes;
1415#[cfg(feature = "pem")]
16use {
17crate::PEM_LINE_WIDTH,
18 pem::{LineEnding, PemLabel},
19};
2021/// 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.
26fn encoded_len(&self) -> Result<usize, Error>;
2728/// Encode this value using the provided [`Writer`].
29fn encode(&self, writer: &mut impl Writer) -> Result<(), Error>;
3031/// Return the length of this type after encoding when prepended with a
32 /// `uint32` length prefix.
33fn encoded_len_prefixed(&self) -> Result<usize, Error> {
34 [4, self.encoded_len()?].checked_sum()
35 }
3637/// Encode this value, first prepending a `uint32` length prefix
38 /// set to [`Encode::encoded_len`].
39fn encode_prefixed(&self, writer: &mut impl Writer) -> Result<(), Error> {
40self.encoded_len()?.encode(writer)?;
41self.encode(writer)
42 }
43}
4445/// 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.
53fn encode_pem<'o>(&self, line_ending: LineEnding, out: &'o mut [u8]) -> Result<&'o str, Error>;
5455/// Encode this type using the [`Encode`] trait, writing the resulting PEM
56 /// document to a returned [`String`].
57#[cfg(feature = "alloc")]
58fn encode_pem_string(&self, line_ending: LineEnding) -> Result<String, Error>;
59}
6061#[cfg(feature = "pem")]
62impl<T: Encode + PemLabel> EncodePem for T {
63fn encode_pem<'o>(&self, line_ending: LineEnding, out: &'o mut [u8]) -> Result<&'o str, Error> {
64let mut writer =
65 pem::Encoder::new_wrapped(Self::PEM_LABEL, PEM_LINE_WIDTH, line_ending, out)
66 .map_err(Error::from)?;
6768self.encode(&mut writer)?;
69let encoded_len = writer.finish().map_err(Error::from)?;
70 str::from_utf8(&out[..encoded_len]).map_err(Error::from)
71 }
7273#[cfg(feature = "alloc")]
74fn encode_pem_string(&self, line_ending: LineEnding) -> Result<String, Error> {
75let encoded_len = pem::encapsulated_len_wrapped(
76Self::PEM_LABEL,
77 PEM_LINE_WIDTH,
78 line_ending,
79self.encoded_len()?,
80 )
81 .map_err(Error::from)?;
8283let mut buf = vec![0u8; encoded_len];
84let 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}
8990/// Encode a single `byte` to the writer.
91impl Encode for u8 {
92fn encoded_len(&self) -> Result<usize, Error> {
93Ok(1)
94 }
9596fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
97 writer.write(&[*self])
98 }
99}
100101/// 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 {
109fn encoded_len(&self) -> Result<usize, Error> {
110Ok(4)
111 }
112113fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
114 writer.write(&self.to_be_bytes())
115 }
116}
117118/// 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 {
125fn encoded_len(&self) -> Result<usize, Error> {
126Ok(8)
127 }
128129fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
130 writer.write(&self.to_be_bytes())
131 }
132}
133134/// 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 {
141fn encoded_len(&self) -> Result<usize, Error> {
142Ok(4)
143 }
144145fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
146 u32::try_from(*self)?.encode(writer)
147 }
148}
149150/// 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] {
158fn encoded_len(&self) -> Result<usize, Error> {
159 [4, self.len()].checked_sum()
160 }
161162fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
163self.len().encode(writer)?;
164 writer.write(self)
165 }
166}
167168/// 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] {
176fn encoded_len(&self) -> Result<usize, Error> {
177self.as_slice().encoded_len()
178 }
179180fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
181self.as_slice().encode(writer)
182 }
183}
184185/// 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 {
203fn encoded_len(&self) -> Result<usize, Error> {
204self.as_bytes().encoded_len()
205 }
206207fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
208self.as_bytes().encode(writer)
209 }
210}
211212#[cfg(feature = "alloc")]
213impl Encode for Vec<u8> {
214fn encoded_len(&self) -> Result<usize, Error> {
215self.as_slice().encoded_len()
216 }
217218fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
219self.as_slice().encode(writer)
220 }
221}
222223#[cfg(feature = "alloc")]
224impl Encode for String {
225fn encoded_len(&self) -> Result<usize, Error> {
226self.as_str().encoded_len()
227 }
228229fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
230self.as_str().encode(writer)
231 }
232}
233234#[cfg(feature = "alloc")]
235impl Encode for Vec<String> {
236fn encoded_len(&self) -> Result<usize, Error> {
237self.iter().try_fold(4usize, |acc, string| {
238 acc.checked_add(string.encoded_len()?).ok_or(Error::Length)
239 })
240 }
241242fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
243self.encoded_len()?
244.checked_sub(4)
245 .ok_or(Error::Length)?
246.encode(writer)?;
247248for entry in self {
249 entry.encode(writer)?;
250 }
251252Ok(())
253 }
254}
255256#[cfg(feature = "bytes")]
257impl Encode for Bytes {
258fn encoded_len(&self) -> Result<usize, Error> {
259self.as_ref().encoded_len()
260 }
261262fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
263self.as_ref().encode(writer)
264 }
265}