ssh_key/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, Result};
7
8#[cfg(feature = "alloc")]
9use {
10 crate::Error,
11 alloc::{string::String, vec::Vec},
12};
13
14/// Encoding trait.
15///
16/// This trait describes how to encode a given type.
17pub(crate) trait Encode {
18 /// Get the length of this type encoded in bytes, prior to Base64 encoding.
19 fn encoded_len(&self) -> Result<usize>;
20
21 /// Encode this value using the provided [`Encoder`].
22 fn encode(&self, writer: &mut impl Writer) -> Result<()>;
23
24 /// Encode this value, first prepending a `uint32` length prefix
25 /// set to [`Encode::encoded_len`].
26 fn encode_nested(&self, writer: &mut impl Writer) -> Result<()> {
27 self.encoded_len()?.encode(writer)?;
28 self.encode(writer)
29 }
30}
31
32/// Encode a single `byte` to the writer.
33impl Encode for u8 {
34 fn encoded_len(&self) -> Result<usize> {
35 Ok(1)
36 }
37
38 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
39 writer.write(&[*self])
40 }
41}
42
43/// Encode a `uint32` as described in [RFC4251 § 5]:
44///
45/// > Represents a 32-bit unsigned integer. Stored as four bytes in the
46/// > order of decreasing significance (network byte order).
47/// > For example: the value 699921578 (0x29b7f4aa) is stored as 29 b7 f4 aa.
48///
49/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
50impl Encode for u32 {
51 fn encoded_len(&self) -> Result<usize> {
52 Ok(4)
53 }
54
55 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
56 writer.write(&self.to_be_bytes())
57 }
58}
59
60/// Encode a `uint64` as described in [RFC4251 § 5]:
61///
62/// > Represents a 64-bit unsigned integer. Stored as eight bytes in
63/// > the order of decreasing significance (network byte order).
64///
65/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
66impl Encode for u64 {
67 fn encoded_len(&self) -> Result<usize> {
68 Ok(8)
69 }
70
71 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
72 writer.write(&self.to_be_bytes())
73 }
74}
75
76/// Encode a `usize` as a `uint32` as described in [RFC4251 § 5].
77///
78/// Uses [`Encode`] impl on `u32` after converting from a `usize`, handling
79/// potential overflow if `usize` is bigger than `u32`.
80///
81/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
82impl Encode for usize {
83 fn encoded_len(&self) -> Result<usize> {
84 Ok(4)
85 }
86
87 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
88 u32::try_from(*self)?.encode(writer)
89 }
90}
91
92/// Encodes `[u8]` into `byte[n]` as described in [RFC4251 § 5]:
93///
94/// > A byte represents an arbitrary 8-bit value (octet). Fixed length
95/// > data is sometimes represented as an array of bytes, written
96/// > byte[n], where n is the number of bytes in the array.
97///
98/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
99impl Encode for [u8] {
100 fn encoded_len(&self) -> Result<usize> {
101 [4, self.len()].checked_sum()
102 }
103
104 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
105 self.len().encode(writer)?;
106 writer.write(self)
107 }
108}
109
110/// Encodes `[u8; N]` into `byte[n]` as described in [RFC4251 § 5]:
111///
112/// > A byte represents an arbitrary 8-bit value (octet). Fixed length
113/// > data is sometimes represented as an array of bytes, written
114/// > byte[n], where n is the number of bytes in the array.
115///
116/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
117impl<const N: usize> Encode for [u8; N] {
118 fn encoded_len(&self) -> Result<usize> {
119 self.as_slice().encoded_len()
120 }
121
122 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
123 self.as_slice().encode(writer)
124 }
125}
126
127/// Encode a `string` as described in [RFC4251 § 5]:
128///
129/// > Arbitrary length binary string. Strings are allowed to contain
130/// > arbitrary binary data, including null characters and 8-bit
131/// > characters. They are stored as a uint32 containing its length
132/// > (number of bytes that follow) and zero (= empty string) or more
133/// > bytes that are the value of the string. Terminating null
134/// > characters are not used.
135/// >
136/// > Strings are also used to store text. In that case, US-ASCII is
137/// > used for internal names, and ISO-10646 UTF-8 for text that might
138/// > be displayed to the user. The terminating null character SHOULD
139/// > NOT normally be stored in the string. For example: the US-ASCII
140/// > string "testing" is represented as 00 00 00 07 t e s t i n g. The
141/// > UTF-8 mapping does not alter the encoding of US-ASCII characters.
142///
143/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
144impl Encode for &str {
145 fn encoded_len(&self) -> Result<usize> {
146 self.as_bytes().encoded_len()
147 }
148
149 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
150 self.as_bytes().encode(writer)
151 }
152}
153
154#[cfg(feature = "alloc")]
155#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
156impl Encode for Vec<u8> {
157 fn encoded_len(&self) -> Result<usize> {
158 self.as_slice().encoded_len()
159 }
160
161 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
162 self.as_slice().encode(writer)
163 }
164}
165
166#[cfg(feature = "alloc")]
167#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
168impl Encode for String {
169 fn encoded_len(&self) -> Result<usize> {
170 self.as_str().encoded_len()
171 }
172
173 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
174 self.as_str().encode(writer)
175 }
176}
177
178#[cfg(feature = "alloc")]
179#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
180impl Encode for Vec<String> {
181 fn encoded_len(&self) -> Result<usize> {
182 self.iter().try_fold(4usize, |acc, string| {
183 acc.checked_add(string.encoded_len()?).ok_or(Error::Length)
184 })
185 }
186
187 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
188 self.encoded_len()?
189 .checked_sub(4)
190 .ok_or(Error::Length)?
191 .encode(writer)?;
192
193 for entry in self {
194 entry.encode(writer)?;
195 }
196
197 Ok(())
198 }
199}