rustica_keys/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 rustica_keys::ssh::Writer;
17 /// let writer = Writer::new();
18 /// ```
19 pub fn new() -> Writer {
20 Writer { inner: Vec::new() }
21 }
22
23 /// Writes a byte sequence to the underlying vector.
24 /// The value is represented as a the byte sequence length,
25 /// followed by the actual byte sequence.
26 ///
27 /// # Example
28 /// ```rust
29 /// # use rustica_keys::ssh::Writer;
30 /// let mut writer = Writer::new();
31 /// writer.write_bytes(&[0, 0, 0, 42]);
32 /// let bytes = writer.into_bytes();
33 /// assert_eq!(bytes, vec![0, 0, 0, 4, 0, 0, 0, 42]);
34 /// ```
35 pub fn write_bytes(&mut self, val: &[u8]) {
36 let size = val.len() as u32;
37 let mut buf = size.to_be_bytes().to_vec();
38 self.inner.append(&mut buf);
39 self.inner.extend_from_slice(&val);
40 }
41
42 /// Writes a `string` value to the underlying byte sequence.
43 ///
44 /// # Example
45 /// ```rust
46 /// # use rustica_keys::ssh::Writer;
47 /// let mut writer = Writer::new();
48 /// writer.write_string("a test string");
49 /// let bytes = writer.into_bytes();
50 /// assert_eq!(bytes, [0, 0, 0, 13, 97, 32, 116, 101, 115, 116, 32, 115, 116, 114, 105, 110, 103]);
51 /// ```
52 pub fn write_string(&mut self, val: &str) {
53 self.write_bytes(val.as_bytes());
54 }
55
56 /// Writes a `u64` value to the underlying byte sequence.
57 ///
58 /// # Example
59 /// ```rust
60 /// # use rustica_keys::ssh::Writer;
61 /// let mut writer = Writer::new();
62 /// writer.write_u64(0xFFFFFFFFFFFFFFFF);
63 /// let bytes = writer.into_bytes();
64 /// assert_eq!(bytes, [255, 255, 255, 255, 255, 255, 255, 255]);
65 /// ```
66 pub fn write_u64(&mut self, val: u64) {
67 let bytes = val.to_be_bytes();
68 self.inner.extend_from_slice(&bytes);
69 }
70
71 /// Writes a `u32` value to the underlying byte sequence.
72 ///
73 /// # Example
74 /// ```rust
75 /// # use rustica_keys::ssh::Writer;
76 /// let mut writer = Writer::new();
77 /// writer.write_u32(0xFFFFFFFF);
78 /// let bytes = writer.into_bytes();
79 /// assert_eq!(bytes, [255, 255, 255, 255]);
80 /// ```
81 pub fn write_u32(&mut self, val: u32) {
82 let bytes = val.to_be_bytes();
83 self.inner.extend_from_slice(&bytes);
84 }
85
86 /// Writes an `mpint` value to the underlying byte sequence.
87 /// If the MSB bit of the first byte is set then the number is
88 /// negative, otherwise it is positive.
89 /// Positive numbers must be preceeded by a leading zero byte according to RFC 4251, section 5.
90 ///
91 /// # Example
92 /// ```rust
93 /// # use rustica_keys::ssh::Writer;
94 /// let mut writer = Writer::new();
95 /// writer.write_mpint(&[1, 0, 1]);
96 /// let bytes = writer.into_bytes();
97 /// assert_eq!(bytes, [0, 0, 0, 3, 1, 0, 1]);
98 /// ```
99 pub fn write_mpint(&mut self, val: &[u8]) {
100 let mut bytes = val.to_vec();
101
102 // If most significant bit is set then prepend a zero byte to
103 // avoid interpretation as a negative number.
104 if val.get(0).unwrap_or(&0) & 0x80 != 0 {
105 bytes.insert(0, 0);
106 }
107
108 self.write_bytes(&bytes);
109 }
110
111 /// Writes a `Vec<String>` to the underlying byte sequence.
112 ///
113 /// # Example
114 /// ```rust
115 /// # use rustica_keys::ssh::Writer;
116 /// let mut writer = Writer::new();
117 ///
118 /// writer.write_string_vec(&vec![String::from("Test"), String::from("Test")]);
119 /// let bytes = writer.into_bytes();
120 /// assert_eq!(bytes, [0, 0, 0, 16, 0, 0, 0, 4, 84, 101, 115, 116, 0, 0, 0, 4, 84, 101, 115, 116]);
121 /// ```
122 pub fn write_string_vec(&mut self, vec: &[String]) {
123 let total_length = vec.iter().map(|x| x.len()).fold(vec.len()*4, |x, y| x + y) as u32;
124 self.write_u32(total_length);
125
126 for item in vec {
127 self.write_string(item);
128 }
129 }
130
131 /// Writes a `HashMap<String, String>` to the underlying byte sequence.
132 ///
133 /// # Example
134 /// ```rust
135 /// # use rustica_keys::ssh::Writer;
136 /// # use std::collections::HashMap;
137 ///
138 /// let mut writer = Writer::new();
139 /// let mut example_map = HashMap::new();
140 /// example_map.insert(String::from("Test"), String::from(""));
141 /// writer.write_string_map(&example_map);
142 /// let bytes = writer.into_bytes();
143 /// assert_eq!(bytes, [0, 0, 0, 12, 0, 0, 0, 4, 84, 101, 115, 116, 0, 0, 0, 0]);
144 /// ```
145 pub fn write_string_map(&mut self, map: &HashMap<String, String>) {
146 let total_length = map.iter()
147 .map(|x| x.0.len() + x.1.len() + if !x.1.is_empty() { 4 } else { 0 })
148 .fold(map.len() * 8, |x, y| x + y) as u32;
149
150 self.write_u32(total_length);
151
152 for (k,v) in map {
153 self.write_string(k);
154 if v.is_empty() {
155 self.write_u32(0x0);
156 } else {
157 self.write_u32(v.len() as u32 + 4);
158 self.write_string(v);
159 }
160 }
161 }
162
163 /// Writes a `PublicKey` to the underlying byte sequence.
164 ///
165 /// # Example
166 /// ```
167 /// ```
168 pub fn write_pub_key(&mut self, key: &PublicKey) {
169 // Write the public key
170 match &key.kind {
171 PublicKeyKind::Ecdsa(key) => {
172 self.write_string(key.curve.identifier);
173 self.write_bytes(&key.key);
174 },
175 PublicKeyKind::Rsa(key) => {
176 self.write_bytes(&key.n);
177 self.write_bytes(&key.e);
178 },
179 PublicKeyKind::Ed25519(key) => {
180 self.write_bytes(&key.key);
181 }
182 };
183 }
184
185 /// Converts the `Writer` into a byte sequence.
186 /// This consumes the underlying byte sequence used by the `Writer`.
187 ///
188 /// # Example
189 /// ```rust
190 /// # use rustica_keys::ssh::Writer;
191 ///
192 /// let mut writer = Writer::new();
193 /// writer.write_string("some data");
194 /// let bytes = writer.into_bytes();
195 /// assert_eq!(bytes, [0, 0, 0, 9, 115, 111, 109, 101, 32, 100, 97, 116, 97]);
196 /// ```
197 pub fn into_bytes(self) -> Vec<u8> {
198 self.inner
199 }
200
201 /// Converts the `Writer` into a byte sequence.
202 /// This consumes the underlying byte sequence used by the `Writer`.
203 ///
204 /// # Example
205 /// ```rust
206 /// # use rustica_keys::ssh::Writer;
207 ///
208 /// let mut writer = Writer::new();
209 /// writer.write_string("some data");
210 /// let bytes = writer.into_bytes();
211 /// assert_eq!(bytes, [0, 0, 0, 9, 115, 111, 109, 101, 32, 100, 97, 116, 97]);
212 /// ```
213 pub fn as_bytes(&self) -> &[u8] {
214 self.inner.as_slice()
215 }
216}
217
218impl Default for Writer {
219 fn default() -> Self {
220 Writer::new()
221 }
222}