1use std::fmt;
4
5pub const TAG_BOOLEAN: u8 = 0x01;
6pub const TAG_INTEGER: u8 = 0x02;
7pub const TAG_BIT_STRING: u8 = 0x03;
8pub const TAG_OCTET_STRING: u8 = 0x04;
9pub const TAG_NULL: u8 = 0x05;
10pub const TAG_OID: u8 = 0x06;
11pub const TAG_UTF8_STRING: u8 = 0x0c;
12pub const TAG_PRINTABLE_STRING: u8 = 0x13;
13pub const TAG_T61_STRING: u8 = 0x14;
14pub const TAG_IA5_STRING: u8 = 0x16;
15pub const TAG_UTC_TIME: u8 = 0x17;
16pub const TAG_GENERALIZED_TIME: u8 = 0x18;
17pub const TAG_UNIVERSAL_STRING: u8 = 0x1c;
18pub const TAG_BMP_STRING: u8 = 0x1e;
19pub const TAG_SEQUENCE: u8 = 0x30;
20pub const TAG_SET: u8 = 0x31;
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct DerError(pub std::borrow::Cow<'static, str>);
24
25impl fmt::Display for DerError {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 write!(f, "DER error: {}", self.0)
28 }
29}
30
31impl std::error::Error for DerError {}
32
33pub type DerResult<T> = Result<T, DerError>;
34
35fn err<T>(msg: &'static str) -> DerResult<T> {
36 Err(DerError(msg.into()))
37}
38
39#[derive(Clone, Copy, Debug)]
41pub struct Tlv<'a> {
42 pub tag: u8,
43 pub value: &'a [u8],
44 pub raw: &'a [u8],
46}
47
48impl<'a> Tlv<'a> {
49 pub fn reader(&self) -> Der<'a> {
51 Der::new(self.value)
52 }
53}
54
55#[derive(Clone, Debug)]
57pub struct Der<'a> {
58 data: &'a [u8],
59 pos: usize,
60}
61
62impl<'a> Der<'a> {
63 pub fn new(data: &'a [u8]) -> Der<'a> {
64 Der { data, pos: 0 }
65 }
66
67 pub fn is_empty(&self) -> bool {
68 self.pos >= self.data.len()
69 }
70
71 pub fn peek_tag(&self) -> Option<u8> {
72 self.data.get(self.pos).copied()
73 }
74
75 pub fn read(&mut self) -> DerResult<Tlv<'a>> {
76 let start = self.pos;
77 let d = self.data;
78 if start + 2 > d.len() {
79 return err("truncated element");
80 }
81 let tag = d[start];
82 if tag & 0x1f == 0x1f {
83 return err("multi-byte tags not supported");
84 }
85 let mut p = start + 1;
86 let first = d[p];
87 p += 1;
88 let len = if first < 0x80 {
89 first as usize
90 } else {
91 let n = (first & 0x7f) as usize;
92 if n == 0 || n > 4 {
93 return err("unsupported length encoding");
94 }
95 if p + n > d.len() {
96 return err("truncated length");
97 }
98 let mut l = 0usize;
99 for i in 0..n {
100 l = (l << 8) | d[p + i] as usize;
101 }
102 p += n;
103 l
104 };
105 if p + len > d.len() {
106 return err("element exceeds input");
107 }
108 self.pos = p + len;
109 Ok(Tlv { tag, value: &d[p..p + len], raw: &d[start..p + len] })
110 }
111
112 pub fn expect(&mut self, tag: u8) -> DerResult<Tlv<'a>> {
114 let t = self.read()?;
115 if t.tag != tag {
116 return err("unexpected tag");
117 }
118 Ok(t)
119 }
120
121 pub fn optional(&mut self, tag: u8) -> DerResult<Option<Tlv<'a>>> {
123 if self.peek_tag() == Some(tag) {
124 Ok(Some(self.read()?))
125 } else {
126 Ok(None)
127 }
128 }
129
130 pub fn sequence(&mut self) -> DerResult<Der<'a>> {
131 Ok(self.expect(TAG_SEQUENCE)?.reader())
132 }
133
134 pub fn oid(&mut self) -> DerResult<String> {
135 decode_oid(self.expect(TAG_OID)?.value)
136 }
137
138 pub fn uint(&mut self) -> DerResult<&'a [u8]> {
140 let v = self.expect(TAG_INTEGER)?.value;
141 if v.is_empty() {
142 return err("empty integer");
143 }
144 if v[0] & 0x80 != 0 {
145 return err("negative integer");
146 }
147 let mut s = v;
148 while s.len() > 1 && s[0] == 0 {
149 s = &s[1..];
150 }
151 Ok(s)
152 }
153
154 pub fn small_uint(&mut self) -> DerResult<u64> {
155 let v = self.uint()?;
156 if v.len() > 8 {
157 return err("integer too large");
158 }
159 Ok(v.iter().fold(0u64, |a, b| (a << 8) | *b as u64))
160 }
161
162 pub fn octet_string(&mut self) -> DerResult<&'a [u8]> {
163 Ok(self.expect(TAG_OCTET_STRING)?.value)
164 }
165
166 pub fn bit_string(&mut self) -> DerResult<&'a [u8]> {
168 let v = self.expect(TAG_BIT_STRING)?.value;
169 if v.is_empty() || v[0] != 0 {
170 return err("bit string with unused bits");
171 }
172 Ok(&v[1..])
173 }
174}
175
176pub fn decode_oid(v: &[u8]) -> DerResult<String> {
177 if v.is_empty() {
178 return err("empty OID");
179 }
180 let mut arcs: Vec<u64> = Vec::new();
181 let mut cur: u64 = 0;
182 for (i, b) in v.iter().enumerate() {
183 if cur > (u64::MAX >> 7) {
184 return err("OID arc too large");
185 }
186 cur = (cur << 7) | (b & 0x7f) as u64;
187 if b & 0x80 == 0 {
188 if arcs.is_empty() {
189 let (a, b2) = if cur < 40 {
190 (0, cur)
191 } else if cur < 80 {
192 (1, cur - 40)
193 } else {
194 (2, cur - 80)
195 };
196 arcs.push(a);
197 arcs.push(b2);
198 } else {
199 arcs.push(cur);
200 }
201 cur = 0;
202 } else if i == v.len() - 1 {
203 return err("truncated OID");
204 }
205 }
206 Ok(arcs.iter().map(|a| a.to_string()).collect::<Vec<_>>().join("."))
207}
208
209pub fn encode_oid(oid: &str) -> Vec<u8> {
210 let arcs: Vec<u64> = oid.split('.').map(|a| a.parse().expect("bad OID")).collect();
211 let mut out = Vec::new();
212 let mut push = |mut v: u64| {
213 let mut tmp = vec![(v & 0x7f) as u8];
214 v >>= 7;
215 while v > 0 {
216 tmp.push(0x80 | (v & 0x7f) as u8);
217 v >>= 7;
218 }
219 tmp.reverse();
220 out.extend(tmp);
221 };
222 push(arcs[0] * 40 + arcs[1]);
223 for a in &arcs[2..] {
224 push(*a);
225 }
226 out
227}
228
229pub fn decode_string(tlv: &Tlv) -> Option<String> {
231 match tlv.tag {
232 TAG_UTF8_STRING | TAG_PRINTABLE_STRING | TAG_IA5_STRING => Some(String::from_utf8_lossy(tlv.value).into_owned()),
233 TAG_T61_STRING => Some(tlv.value.iter().map(|b| *b as char).collect()),
234 TAG_BMP_STRING => {
235 let units: Vec<u16> = tlv.value.chunks(2).map(|c| u16::from_be_bytes([c[0], *c.get(1).unwrap_or(&0)])).collect();
236 Some(String::from_utf16_lossy(&units))
237 }
238 TAG_UNIVERSAL_STRING => Some(
239 tlv.value
240 .chunks(4)
241 .filter_map(|c| if c.len() == 4 { char::from_u32(u32::from_be_bytes([c[0], c[1], c[2], c[3]])) } else { None })
242 .collect(),
243 ),
244 _ => None,
245 }
246}
247
248pub fn days_from_civil(y: i64, m: u32, d: u32) -> i64 {
250 let y = if m <= 2 { y - 1 } else { y };
251 let era = if y >= 0 { y } else { y - 399 } / 400;
252 let yoe = y - era * 400;
253 let mp = (m as i64 + 9) % 12;
254 let doy = (153 * mp + 2) / 5 + d as i64 - 1;
255 let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
256 era * 146097 + doe - 719468
257}
258
259pub fn civil_from_days(z: i64) -> (i64, u32, u32) {
261 let z = z + 719468;
262 let era = if z >= 0 { z } else { z - 146096 } / 146097;
263 let doe = z - era * 146097;
264 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
265 let y = yoe + era * 400;
266 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
267 let mp = (5 * doy + 2) / 153;
268 let d = (doy - (153 * mp + 2) / 5 + 1) as u32;
269 let m = if mp < 10 { mp + 3 } else { mp - 9 } as u32;
270 (if m <= 2 { y + 1 } else { y }, m, d)
271}
272
273pub fn parse_time(tlv: &Tlv) -> DerResult<i64> {
275 let s = std::str::from_utf8(tlv.value).map_err(|_| DerError("bad time".into()))?;
276 let digits = |r: std::ops::Range<usize>| -> DerResult<i64> {
277 s.get(r).and_then(|x| if x.bytes().all(|c| c.is_ascii_digit()) { x.parse().ok() } else { None }).ok_or(DerError("bad time digits".into()))
278 };
279 let (year, rest) = match tlv.tag {
280 TAG_UTC_TIME => {
281 let yy = digits(0..2)?;
282 (if yy >= 50 { 1900 + yy } else { 2000 + yy }, 2)
283 }
284 TAG_GENERALIZED_TIME => (digits(0..4)?, 4),
285 _ => return err("not a time"),
286 };
287 let mon = digits(rest..rest + 2)?;
288 let day = digits(rest + 2..rest + 4)?;
289 let hour = digits(rest + 4..rest + 6)?;
290 let min = digits(rest + 6..rest + 8)?;
291 let sec = if s.len() > rest + 8 && s.as_bytes()[rest + 8].is_ascii_digit() { digits(rest + 8..rest + 10)? } else { 0 };
292 if !s.ends_with('Z') || !(1..=12).contains(&mon) || !(1..=31).contains(&day) || hour > 23 || min > 59 || sec > 60 {
293 return err("unsupported time format");
294 }
295 Ok(days_from_civil(year, mon as u32, day as u32) * 86400 + hour * 3600 + min * 60 + sec)
296}
297
298pub fn tlv(tag: u8, content: &[u8]) -> Vec<u8> {
301 let mut out = vec![tag];
302 let len = content.len();
303 if len < 0x80 {
304 out.push(len as u8);
305 } else {
306 let bytes = len.to_be_bytes();
307 let skip = bytes.iter().take_while(|b| **b == 0).count();
308 out.push(0x80 | (bytes.len() - skip) as u8);
309 out.extend_from_slice(&bytes[skip..]);
310 }
311 out.extend_from_slice(content);
312 out
313}
314
315pub fn seq(parts: &[Vec<u8>]) -> Vec<u8> {
316 tlv(TAG_SEQUENCE, &parts.concat())
317}
318
319pub fn set(parts: &[Vec<u8>]) -> Vec<u8> {
320 tlv(TAG_SET, &parts.concat())
321}
322
323pub fn uint(bytes: &[u8]) -> Vec<u8> {
325 let mut b = bytes;
326 while b.len() > 1 && b[0] == 0 {
327 b = &b[1..];
328 }
329 let mut c = Vec::new();
330 if b.is_empty() || b[0] & 0x80 != 0 {
331 c.push(0);
332 }
333 c.extend_from_slice(b);
334 tlv(TAG_INTEGER, &c)
335}
336
337pub fn oid(o: &str) -> Vec<u8> {
338 tlv(TAG_OID, &encode_oid(o))
339}
340
341pub fn bit_string(bytes: &[u8]) -> Vec<u8> {
342 let mut c = vec![0u8];
343 c.extend_from_slice(bytes);
344 tlv(TAG_BIT_STRING, &c)
345}
346
347pub fn octet_string(bytes: &[u8]) -> Vec<u8> {
348 tlv(TAG_OCTET_STRING, bytes)
349}
350
351pub fn explicit(n: u8, content: &[u8]) -> Vec<u8> {
353 tlv(0xa0 | n, content)
354}
355
356pub fn time(unix: i64) -> Vec<u8> {
358 let days = unix.div_euclid(86400);
359 let secs = unix.rem_euclid(86400);
360 let (y, m, d) = civil_from_days(days);
361 let (hh, mm, ss) = (secs / 3600, (secs / 60) % 60, secs % 60);
362 if (1950..2050).contains(&y) {
363 tlv(TAG_UTC_TIME, format!("{:02}{:02}{:02}{:02}{:02}{:02}Z", y % 100, m, d, hh, mm, ss).as_bytes())
364 } else {
365 tlv(TAG_GENERALIZED_TIME, format!("{:04}{:02}{:02}{:02}{:02}{:02}Z", y, m, d, hh, mm, ss).as_bytes())
366 }
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372
373 #[test]
374 fn oid_roundtrip() {
375 for o in ["1.2.840.113549.1.1.11", "2.5.29.17", "1.3.6.1.5.5.7.3.1", "1.2.840.10045.3.1.7", "2.999.3"] {
376 assert_eq!(decode_oid(&encode_oid(o)).unwrap(), o);
377 }
378 assert_eq!(encode_oid("1.2.840.113549"), vec![0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d]);
379 }
380
381 #[test]
382 fn dates() {
383 assert_eq!(days_from_civil(1970, 1, 1), 0);
384 assert_eq!(days_from_civil(2000, 3, 1), 11017);
385 for z in [-1000i64, 0, 11016, 11017, 19000, 47482, 2932896] {
386 let (y, m, d) = civil_from_days(z);
387 assert_eq!(days_from_civil(y, m, d), z);
388 }
389 let t = time(1_700_000_000);
390 let tl = Der::new(&t).read().unwrap();
391 assert_eq!(parse_time(&tl).unwrap(), 1_700_000_000);
392 let t = time(4_102_444_800 + 5); let tl = Der::new(&t).read().unwrap();
394 assert_eq!(tl.tag, TAG_GENERALIZED_TIME);
395 assert_eq!(parse_time(&tl).unwrap(), 4_102_444_805);
396 }
397
398 #[test]
399 fn integers_and_lengths() {
400 let big = vec![0xffu8; 300];
401 let enc = uint(&big);
402 let mut r = Der::new(&enc);
403 assert_eq!(r.uint().unwrap(), &big[..]);
404 assert!(r.is_empty());
405 assert_eq!(uint(&[0x7f]), vec![2, 1, 0x7f]);
406 assert_eq!(uint(&[0x80]), vec![2, 2, 0, 0x80]);
407 assert!(Der::new(&[0x30, 0x05, 0x01]).read().is_err());
408 }
409}