1use alloc::vec::Vec;
14
15use super::reader::{
16 MAX_DER_LEN, TAG_BIT_STRING, TAG_INTEGER, TAG_NULL, TAG_OCTET_STRING, TAG_OID, TAG_SEQUENCE,
17};
18
19pub fn write_length(out: &mut Vec<u8>, len: usize) {
25 #[allow(clippy::cast_possible_truncation)]
26 if len < 128 {
27 out.push(len as u8);
28 } else if len < 256 {
29 out.push(0x81);
30 out.push(len as u8);
31 } else if len < 65_536 {
32 out.push(0x82);
33 out.push((len >> 8) as u8);
34 out.push(len as u8);
35 } else if len < MAX_DER_LEN {
36 out.push(0x83);
37 out.push((len >> 16) as u8);
38 out.push((len >> 8) as u8);
39 out.push(len as u8);
40 } else {
41 panic!("DER length overflow: {} >= {} bytes", len, MAX_DER_LEN);
42 }
43}
44
45pub fn write_integer(out: &mut Vec<u8>, value_be: &[u8]) {
54 assert!(!value_be.is_empty(), "INTEGER content must be non-empty");
55 let mut start = 0;
56 while start < value_be.len() - 1 && value_be[start] == 0 {
57 start += 1;
58 }
59 let trimmed = &value_be[start..];
60 let needs_pad = (trimmed[0] & 0x80) != 0;
61 let int_len = trimmed.len() + usize::from(needs_pad);
62 out.push(TAG_INTEGER);
63 write_length(out, int_len);
64 if needs_pad {
65 out.push(0x00);
66 }
67 out.extend_from_slice(trimmed);
68}
69
70pub fn write_octet_string(out: &mut Vec<u8>, value: &[u8]) {
72 out.push(TAG_OCTET_STRING);
73 write_length(out, value.len());
74 out.extend_from_slice(value);
75}
76
77pub fn write_bit_string(out: &mut Vec<u8>, unused_bits: u8, value: &[u8]) {
83 assert!(unused_bits <= 7, "BIT STRING unused_bits must be 0..=7");
84 out.push(TAG_BIT_STRING);
85 write_length(out, value.len() + 1);
86 out.push(unused_bits);
87 out.extend_from_slice(value);
88}
89
90pub fn write_null(out: &mut Vec<u8>) {
92 out.push(TAG_NULL);
93 out.push(0x00);
94}
95
96pub fn write_oid(out: &mut Vec<u8>, encoded_subids: &[u8]) {
103 assert!(!encoded_subids.is_empty(), "OID content must be non-empty");
104 out.push(TAG_OID);
105 write_length(out, encoded_subids.len());
106 out.extend_from_slice(encoded_subids);
107}
108
109pub fn write_sequence(out: &mut Vec<u8>, body: &[u8]) {
111 out.push(TAG_SEQUENCE);
112 write_length(out, body.len());
113 out.extend_from_slice(body);
114}
115
116pub fn write_context_tagged_explicit(out: &mut Vec<u8>, n: u8, inner: &[u8]) {
121 assert!(n <= 30, "multi-byte context tags not supported");
122 out.push(0xA0 | n);
124 write_length(out, inner.len());
125 out.extend_from_slice(inner);
126}
127
128pub fn write_context_tagged_implicit(out: &mut Vec<u8>, n: u8, value: &[u8]) {
134 assert!(n <= 30, "multi-byte context tags not supported");
135 out.push(0x80 | n);
137 write_length(out, value.len());
138 out.extend_from_slice(value);
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use crate::asn1::reader;
145
146 fn roundtrip_length(len: usize) {
147 let mut out = Vec::new();
148 write_length(&mut out, len);
149 let (decoded, rest) = reader::read_length(&out).expect("decode");
150 assert_eq!(decoded, len);
151 assert!(rest.is_empty(), "no trailing bytes");
152 }
153
154 #[test]
157 fn length_one_byte() {
158 roundtrip_length(0);
159 roundtrip_length(1);
160 roundtrip_length(127);
161 }
162
163 #[test]
164 fn length_two_byte() {
165 roundtrip_length(128);
166 roundtrip_length(255);
167 }
168
169 #[test]
170 fn length_three_byte() {
171 roundtrip_length(256);
172 roundtrip_length(65_535);
173 }
174
175 #[test]
176 fn length_four_byte() {
177 roundtrip_length(65_536);
178 roundtrip_length(MAX_DER_LEN - 1);
179 }
180
181 #[test]
182 #[should_panic(expected = "DER length overflow")]
183 fn length_overflow_panics() {
184 let mut out = Vec::new();
185 write_length(&mut out, MAX_DER_LEN);
186 }
187
188 #[test]
191 fn integer_round_trip_small() {
192 let mut out = Vec::new();
193 write_integer(&mut out, &[0x05]);
194 let (bytes, _) = reader::read_integer(&out).unwrap();
195 assert_eq!(bytes, &[0x05]);
196 }
197
198 #[test]
199 fn integer_round_trip_high_bit_set() {
200 let mut out = Vec::new();
201 write_integer(&mut out, &[0x80, 0x01]);
202 assert_eq!(out, alloc::vec![0x02, 0x03, 0x00, 0x80, 0x01]);
204 let (bytes, _) = reader::read_integer(&out).unwrap();
205 assert_eq!(bytes, &[0x80, 0x01]);
206 }
207
208 #[test]
209 fn integer_strips_leading_zeros() {
210 let mut out = Vec::new();
211 write_integer(&mut out, &[0x00, 0x00, 0x05]);
212 assert_eq!(out, alloc::vec![0x02, 0x01, 0x05]);
214 }
215
216 #[test]
217 fn integer_zero_round_trip() {
218 let mut out = Vec::new();
219 write_integer(&mut out, &[0x00]);
220 assert_eq!(out, alloc::vec![0x02, 0x01, 0x00]);
222 let (bytes, _) = reader::read_integer(&out).unwrap();
223 assert_eq!(bytes, &[0x00]);
224 }
225
226 #[test]
229 fn octet_string_empty() {
230 let mut out = Vec::new();
231 write_octet_string(&mut out, &[]);
232 assert_eq!(out, alloc::vec![0x04, 0x00]);
233 }
234
235 #[test]
236 fn octet_string_round_trip() {
237 let mut out = Vec::new();
238 write_octet_string(&mut out, b"abc");
239 assert_eq!(out, alloc::vec![0x04, 0x03, b'a', b'b', b'c']);
240 }
241
242 #[test]
245 fn bit_string_round_trip() {
246 let mut out = Vec::new();
247 write_bit_string(&mut out, 0, &[0xAB, 0xCD]);
248 assert_eq!(out, alloc::vec![0x03, 0x03, 0x00, 0xAB, 0xCD]);
249 let (unused, bytes, rest) = reader::read_bit_string(&out).unwrap();
250 assert_eq!(unused, 0);
251 assert_eq!(bytes, &[0xAB, 0xCD]);
252 assert!(rest.is_empty());
253 }
254
255 #[test]
258 fn null_round_trip() {
259 let mut out = Vec::new();
260 write_null(&mut out);
261 assert_eq!(out, alloc::vec![0x05, 0x00]);
262 assert_eq!(reader::read_null(&out), Some(&[][..]));
263 }
264
265 #[test]
268 fn oid_round_trip() {
269 let subids: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0C];
271 let mut out = Vec::new();
272 write_oid(&mut out, subids);
273 let (parsed, _) = reader::read_oid(&out).unwrap();
274 assert_eq!(parsed, subids);
275 }
276
277 #[test]
280 fn sequence_wrap() {
281 let mut body = Vec::new();
282 write_integer(&mut body, &[0x01]);
283 write_integer(&mut body, &[0x02]);
284 let mut out = Vec::new();
285 write_sequence(&mut out, &body);
286 assert_eq!(
287 out,
288 alloc::vec![0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02]
289 );
290 }
291
292 #[test]
295 fn context_explicit_round_trip() {
296 let mut inner = Vec::new();
297 write_integer(&mut inner, &[0x01]);
298 let mut out = Vec::new();
299 write_context_tagged_explicit(&mut out, 0, &inner);
300 assert_eq!(out, alloc::vec![0xA0, 0x03, 0x02, 0x01, 0x01]);
301 let (parsed, _) = reader::read_context_tagged_explicit(&out, 0).unwrap();
302 let (v, _) = reader::read_integer(parsed).unwrap();
303 assert_eq!(v, &[0x01]);
304 }
305
306 #[test]
307 fn context_implicit_round_trip() {
308 let mut out = Vec::new();
309 write_context_tagged_implicit(&mut out, 1, b"ab");
310 assert_eq!(out, alloc::vec![0x81, 0x02, b'a', b'b']);
311 let (parsed, _) = reader::read_context_tagged_implicit(&out, 1).unwrap();
312 assert_eq!(parsed, b"ab");
313 }
314
315 #[test]
316 #[should_panic(expected = "multi-byte context tags not supported")]
317 fn context_explicit_above_30_panics() {
318 let mut out = Vec::new();
319 write_context_tagged_explicit(&mut out, 31, &[]);
320 }
321}