sshcerts/ssh/writer.rs
1use std::collections::HashMap;
2
3use super::pubkey::{PublicKey, PublicKeyKind};
4
5/// A `Writer` is used for encoding a key in OpenSSH compatible format.
6#[derive(Debug)]
7pub struct Writer {
8 inner: Vec<u8>,
9}
10
11impl Writer {
12 /// Creates a new `Writer` instance.
13 ///
14 /// # Example
15 /// ```rust
16 /// # use sshcerts::ssh::Writer;
17 /// let writer = Writer::new();
18 /// ```
19 pub fn new() -> Writer {
20 Writer { inner: Vec::new() }
21 }
22
23 /// Write a cstring to the underlying vector
24 ///
25 /// # Example
26 /// ```rust
27 /// # use sshcerts::ssh::Writer;
28 /// let mut writer = Writer::new();
29 /// writer.write_cstring("AAAA");
30 /// let bytes = writer.into_bytes();
31 /// assert_eq!(bytes, vec![65, 65, 65, 65, 00]);
32 /// ```
33 pub fn write_cstring(&mut self, s: &str) {
34 let bytes = s.as_bytes();
35 self.inner.extend_from_slice(bytes);
36 self.inner.push(0x0);
37 }
38
39 /// Writes a byte sequence to the underlying vector.
40 /// The value is represented as a the byte sequence length,
41 /// followed by the actual byte sequence.
42 ///
43 /// # Example
44 /// ```rust
45 /// # use sshcerts::ssh::Writer;
46 /// let mut writer = Writer::new();
47 /// writer.write_bytes(&[0, 0, 0, 42]);
48 /// let bytes = writer.into_bytes();
49 /// assert_eq!(bytes, vec![0, 0, 0, 4, 0, 0, 0, 42]);
50 /// ```
51 pub fn write_bytes(&mut self, val: &[u8]) {
52 let size = val.len() as u32;
53 let mut buf = size.to_be_bytes().to_vec();
54 self.inner.append(&mut buf);
55 self.inner.extend_from_slice(val);
56 }
57
58 /// Writes a raw byte sequence to the underlying vector.e.
59 ///
60 /// # Example
61 /// ```rust
62 /// # use sshcerts::ssh::Writer;
63 /// let mut writer = Writer::new();
64 /// writer.write_raw_bytes(&[0, 0, 0, 42]);
65 /// let bytes = writer.into_bytes();
66 /// assert_eq!(bytes, vec![0, 0, 0, 42]);
67 /// ```
68 pub fn write_raw_bytes(&mut self, val: &[u8]) {
69 self.inner.extend_from_slice(val);
70 }
71
72 /// Writes a `string` value to the underlying byte sequence.
73 ///
74 /// # Example
75 /// ```rust
76 /// # use sshcerts::ssh::Writer;
77 /// let mut writer = Writer::new();
78 /// writer.write_string("a test string");
79 /// let bytes = writer.into_bytes();
80 /// assert_eq!(bytes, [0, 0, 0, 13, 97, 32, 116, 101, 115, 116, 32, 115, 116, 114, 105, 110, 103]);
81 /// ```
82 pub fn write_string(&mut self, val: &str) {
83 self.write_bytes(val.as_bytes());
84 }
85
86 /// Writes a `u64` value to the underlying byte sequence.
87 ///
88 /// # Example
89 /// ```rust
90 /// # use sshcerts::ssh::Writer;
91 /// let mut writer = Writer::new();
92 /// writer.write_u64(0xFFFFFFFFFFFFFFFF);
93 /// let bytes = writer.into_bytes();
94 /// assert_eq!(bytes, [255, 255, 255, 255, 255, 255, 255, 255]);
95 /// ```
96 pub fn write_u64(&mut self, val: u64) {
97 let bytes = val.to_be_bytes();
98 self.inner.extend_from_slice(&bytes);
99 }
100
101 /// Writes a `u32` value to the underlying byte sequence.
102 ///
103 /// # Example
104 /// ```rust
105 /// # use sshcerts::ssh::Writer;
106 /// let mut writer = Writer::new();
107 /// writer.write_u32(0xFFFFFFFF);
108 /// let bytes = writer.into_bytes();
109 /// assert_eq!(bytes, [255, 255, 255, 255]);
110 /// ```
111 pub fn write_u32(&mut self, val: u32) {
112 let bytes = val.to_be_bytes();
113 self.inner.extend_from_slice(&bytes);
114 }
115
116 /// Writes an `mpint` value to the underlying byte sequence.
117 /// If the MSB bit of the first byte is set then the number is
118 /// negative, otherwise it is positive.
119 /// Positive numbers must be preceeded by a leading zero byte according to RFC 4251, section 5.
120 ///
121 /// We also make a reasonable effort to fix encoding errors and only write valid, positive, mpints
122 ///
123 /// # Example
124 /// ```rust
125 /// # use sshcerts::ssh::Writer;
126 /// let mut writer = Writer::new();
127 /// writer.write_mpint(&[1, 0, 1]);
128 /// let bytes = writer.into_bytes();
129 /// assert_eq!(bytes, [0, 0, 0, 3, 1, 0, 1]);
130 /// ```
131 pub fn write_mpint(&mut self, val: &[u8]) {
132 let mut leading_zeros = 0;
133 // Remove leading 0s
134 for item in val {
135 if *item == 0 {
136 leading_zeros += 1
137 } else {
138 break;
139 }
140 }
141
142 let val = &val[leading_zeros..];
143 let mut bytes = val.to_vec();
144
145 // If most significant bit is set then prepend a zero byte to
146 // avoid interpretation as a negative number.
147 if val.first().unwrap_or(&0) & 0x80 != 0 {
148 bytes.insert(0, 0);
149 }
150
151 self.write_bytes(&bytes);
152 }
153
154 /// Writes a `Vec<String>` to the underlying byte sequence.
155 ///
156 /// # Example
157 /// ```rust
158 /// # use sshcerts::ssh::Writer;
159 /// let mut writer = Writer::new();
160 ///
161 /// writer.write_string_vec(&vec![String::from("Test"), String::from("Test")]);
162 /// let bytes = writer.into_bytes();
163 /// assert_eq!(bytes, [0, 0, 0, 16, 0, 0, 0, 4, 84, 101, 115, 116, 0, 0, 0, 4, 84, 101, 115, 116]);
164 /// ```
165 pub fn write_string_vec(&mut self, vec: &[String]) {
166 let total_length = vec
167 .iter()
168 .map(|x| x.len())
169 .fold(vec.len() * 4, |x, y| x + y) as u32;
170 self.write_u32(total_length);
171
172 for item in vec {
173 self.write_string(item);
174 }
175 }
176
177 /// Writes a `HashMap<String, String>` to the underlying byte sequence.
178 ///
179 /// # Example
180 /// ```rust
181 /// # use sshcerts::ssh::Writer;
182 /// # use std::collections::HashMap;
183 ///
184 /// let mut writer = Writer::new();
185 /// let mut example_map = HashMap::new();
186 /// example_map.insert(String::from("Test"), String::from(""));
187 /// writer.write_string_map(&example_map);
188 /// let bytes = writer.into_bytes();
189 /// assert_eq!(bytes, [0, 0, 0, 12, 0, 0, 0, 4, 84, 101, 115, 116, 0, 0, 0, 0]);
190 /// ```
191 pub fn write_string_map(&mut self, map: &HashMap<String, String>) {
192 let total_length = map
193 .iter()
194 .map(|x| x.0.len() + x.1.len() + if !x.1.is_empty() { 4 } else { 0 })
195 .fold(map.len() * 8, |x, y| x + y) as u32;
196
197 self.write_u32(total_length);
198
199 for (k, v) in map {
200 self.write_string(k);
201 if v.is_empty() {
202 self.write_u32(0x0);
203 } else {
204 self.write_u32(v.len() as u32 + 4);
205 self.write_string(v);
206 }
207 }
208 }
209
210 /// Writes a `PublicKey` to the underlying byte sequence.
211 ///
212 /// # Example
213 /// ```
214 /// ```
215 pub fn write_pub_key(&mut self, key: &PublicKey) {
216 let mut inner_writer = Writer::new();
217 inner_writer.write_string(key.key_type.name);
218 inner_writer.write_pub_key_data(key);
219
220 let pubkey_bytes = inner_writer.as_bytes();
221 self.write_bytes(pubkey_bytes);
222 }
223
224 /// Writes `PublicKey` data to the underlying byte sequence.
225 ///
226 /// # Example
227 /// ```
228 /// ```
229 pub fn write_pub_key_data(&mut self, key: &PublicKey) {
230 // Write the public key
231 match &key.kind {
232 PublicKeyKind::Rsa(ref k) => {
233 self.write_mpint(&k.e);
234 self.write_mpint(&k.n);
235 }
236 PublicKeyKind::Ecdsa(ref k) => {
237 self.write_string(k.curve.identifier);
238 self.write_bytes(&k.key);
239 if key.key_type.is_sk {
240 self.write_string(k.sk_application.as_ref().unwrap());
241 }
242 }
243 PublicKeyKind::Ed25519(ref k) => {
244 self.write_bytes(&k.key);
245 if key.key_type.is_sk {
246 self.write_string(k.sk_application.as_ref().unwrap());
247 }
248 }
249 }
250 }
251
252 /// Converts the `Writer` into a byte sequence.
253 /// This consumes the underlying byte sequence used by the `Writer`.
254 ///
255 /// # Example
256 /// ```rust
257 /// # use sshcerts::ssh::Writer;
258 ///
259 /// let mut writer = Writer::new();
260 /// writer.write_string("some data");
261 /// let bytes = writer.into_bytes();
262 /// assert_eq!(bytes, [0, 0, 0, 9, 115, 111, 109, 101, 32, 100, 97, 116, 97]);
263 /// ```
264 pub fn into_bytes(self) -> Vec<u8> {
265 self.inner
266 }
267
268 /// Converts the `Writer` into a byte sequence.
269 /// This consumes the underlying byte sequence used by the `Writer`.
270 ///
271 /// # Example
272 /// ```rust
273 /// # use sshcerts::ssh::Writer;
274 ///
275 /// let mut writer = Writer::new();
276 /// writer.write_string("some data");
277 /// let bytes = writer.into_bytes();
278 /// assert_eq!(bytes, [0, 0, 0, 9, 115, 111, 109, 101, 32, 100, 97, 116, 97]);
279 /// ```
280 pub fn as_bytes(&self) -> &[u8] {
281 self.inner.as_slice()
282 }
283}
284
285impl Default for Writer {
286 fn default() -> Self {
287 Writer::new()
288 }
289}