/rust/registry/src/index.crates.io-1949cf8c6b5b557f/simple_asn1-0.6.3/src/lib.rs
Line | Count | Source |
1 | | //! A small ASN.1 parsing library for Rust. In particular, this library is used |
2 | | //! to translate the binary DER encoding of an ASN.1-formatted document into the |
3 | | //! core primitives of ASN.1. It is assumed that you can do what you need to |
4 | | //! from there. |
5 | | //! |
6 | | //! The critical items for this document are the traits `ToASN1` and `FromASN1`. |
7 | | //! The first takes your data type and encodes it into a `Vec` of simple ASN.1 |
8 | | //! structures (`ASN1Block`s). The latter inverts the process. |
9 | | //! |
10 | | //! Items that implement `ToASN1` can be used with the function `der_encode` |
11 | | //! to provide single-step encoding of a data type to binary DER encoding. |
12 | | //! Similarly, items that are `FromASN` can be single-step decoded using |
13 | | //! the helper function `der_decode`. |
14 | | //! |
15 | | //! You can implement one or both traits, depending on your needs. If you do |
16 | | //! implement both, the obvious encode/decode quickcheck property is strongly |
17 | | //! advised. |
18 | | //! |
19 | | //! For decoding schemes that require the actual bytes associated with the |
20 | | //! binary representation, we also provide `FromASN1WithBody`. This can be |
21 | | //! used with the offset information in the primitive `ASN1Block`s to, for |
22 | | //! example, validate signatures in X509 documents. |
23 | | //! |
24 | | //! Finally, this library supports ASN.1 class information. I'm still not sure |
25 | | //! why it's useful, but there it is. |
26 | | //! |
27 | | //! Please send any bug reports, patches, and curses to the GitHub repository |
28 | | //! at <code>https://github.com/acw/simple_asn1</code>. |
29 | | pub use num_bigint::{BigInt, BigUint}; |
30 | | use num_traits::{FromPrimitive, One, ToPrimitive, Zero}; |
31 | | #[cfg(test)] |
32 | | use quickcheck::quickcheck; |
33 | | use std::convert::TryFrom; |
34 | | use std::iter::FromIterator; |
35 | | use std::mem::size_of; |
36 | | use std::str::Utf8Error; |
37 | | use thiserror::Error; |
38 | | use time::PrimitiveDateTime; |
39 | | |
40 | | /// An ASN.1 block class. |
41 | | /// |
42 | | /// I'm not sure if/when these are used, but here they are in case you want |
43 | | /// to do something with them. |
44 | | #[derive(Clone, Copy, Debug, PartialEq)] |
45 | | pub enum ASN1Class { |
46 | | Universal, |
47 | | Application, |
48 | | ContextSpecific, |
49 | | Private, |
50 | | } |
51 | | |
52 | | /// A primitive block from ASN.1. |
53 | | /// |
54 | | /// Primitive blocks all contain the offset from the beginning of the parsed |
55 | | /// document, followed by whatever data is associated with the block. The latter |
56 | | /// should be fairly self-explanatory, so let's discuss the offset. |
57 | | /// |
58 | | /// The offset is only valid during the reading process. It is ignored for |
59 | | /// the purposes of encoding blocks into their binary form. It is also |
60 | | /// ignored for the purpose of comparisons via `==`. It is included entirely |
61 | | /// to support the parsing of things like X509 certificates, in which it is |
62 | | /// necessary to know when particular blocks end. |
63 | | /// |
64 | | /// The [`ASN1Class`] of explicitly tagged blocks is either `Application`, |
65 | | /// `ContextSpecific` or `Private`. `Unknown` can have any class. |
66 | | /// The class of all other variants is `Universal`. |
67 | | /// |
68 | | /// [`ASN1Class`]: enum.ASN1Class.html |
69 | | #[derive(Clone, Debug)] |
70 | | pub enum ASN1Block { |
71 | | Boolean(usize, bool), |
72 | | Integer(usize, BigInt), |
73 | | BitString(usize, usize, Vec<u8>), |
74 | | OctetString(usize, Vec<u8>), |
75 | | Null(usize), |
76 | | ObjectIdentifier(usize, OID), |
77 | | UTF8String(usize, String), |
78 | | PrintableString(usize, String), |
79 | | TeletexString(usize, String), |
80 | | IA5String(usize, String), |
81 | | UTCTime(usize, PrimitiveDateTime), |
82 | | GeneralizedTime(usize, PrimitiveDateTime), |
83 | | UniversalString(usize, String), |
84 | | BMPString(usize, String), |
85 | | Sequence(usize, Vec<ASN1Block>), |
86 | | Set(usize, Vec<ASN1Block>), |
87 | | /// An explicitly tagged block. |
88 | | /// |
89 | | /// The class can be either `Application`, `ContextSpecific` or `Private`. |
90 | | /// The other parameters are `offset`, `tag` and `content`. |
91 | | /// |
92 | | /// This block is always `constructed`. |
93 | | Explicit(ASN1Class, usize, BigUint, Box<ASN1Block>), |
94 | | /// An unkown block. |
95 | | /// |
96 | | /// The parameters are `class`, `constructed`, `offset`, `tag` and |
97 | | /// `content`. |
98 | | Unknown(ASN1Class, bool, usize, BigUint, Vec<u8>), |
99 | | } |
100 | | |
101 | | impl ASN1Block { |
102 | | /// Get the class associated with the given ASN1Block, regardless of what |
103 | | /// kind of block it is. |
104 | 0 | pub fn class(&self) -> ASN1Class { |
105 | 0 | match *self { |
106 | 0 | ASN1Block::Boolean(_, _) => ASN1Class::Universal, |
107 | 0 | ASN1Block::Integer(_, _) => ASN1Class::Universal, |
108 | 0 | ASN1Block::BitString(_, _, _) => ASN1Class::Universal, |
109 | 0 | ASN1Block::OctetString(_, _) => ASN1Class::Universal, |
110 | 0 | ASN1Block::Null(_) => ASN1Class::Universal, |
111 | 0 | ASN1Block::ObjectIdentifier(_, _) => ASN1Class::Universal, |
112 | 0 | ASN1Block::UTF8String(_, _) => ASN1Class::Universal, |
113 | 0 | ASN1Block::PrintableString(_, _) => ASN1Class::Universal, |
114 | 0 | ASN1Block::TeletexString(_, _) => ASN1Class::Universal, |
115 | 0 | ASN1Block::IA5String(_, _) => ASN1Class::Universal, |
116 | 0 | ASN1Block::UTCTime(_, _) => ASN1Class::Universal, |
117 | 0 | ASN1Block::GeneralizedTime(_, _) => ASN1Class::Universal, |
118 | 0 | ASN1Block::UniversalString(_, _) => ASN1Class::Universal, |
119 | 0 | ASN1Block::BMPString(_, _) => ASN1Class::Universal, |
120 | 0 | ASN1Block::Sequence(_, _) => ASN1Class::Universal, |
121 | 0 | ASN1Block::Set(_, _) => ASN1Class::Universal, |
122 | 0 | ASN1Block::Explicit(c, _, _, _) => c, |
123 | 0 | ASN1Block::Unknown(c, _, _, _, _) => c, |
124 | | } |
125 | 0 | } |
126 | | /// Get the starting offset associated with the given ASN1Block, regardless |
127 | | /// of what kind of block it is. |
128 | 0 | pub fn offset(&self) -> usize { |
129 | 0 | match *self { |
130 | 0 | ASN1Block::Boolean(o, _) => o, |
131 | 0 | ASN1Block::Integer(o, _) => o, |
132 | 0 | ASN1Block::BitString(o, _, _) => o, |
133 | 0 | ASN1Block::OctetString(o, _) => o, |
134 | 0 | ASN1Block::Null(o) => o, |
135 | 0 | ASN1Block::ObjectIdentifier(o, _) => o, |
136 | 0 | ASN1Block::UTF8String(o, _) => o, |
137 | 0 | ASN1Block::PrintableString(o, _) => o, |
138 | 0 | ASN1Block::TeletexString(o, _) => o, |
139 | 0 | ASN1Block::IA5String(o, _) => o, |
140 | 0 | ASN1Block::UTCTime(o, _) => o, |
141 | 0 | ASN1Block::GeneralizedTime(o, _) => o, |
142 | 0 | ASN1Block::UniversalString(o, _) => o, |
143 | 0 | ASN1Block::BMPString(o, _) => o, |
144 | 0 | ASN1Block::Sequence(o, _) => o, |
145 | 0 | ASN1Block::Set(o, _) => o, |
146 | 0 | ASN1Block::Explicit(_, o, _, _) => o, |
147 | 0 | ASN1Block::Unknown(_, _, o, _, _) => o, |
148 | | } |
149 | 0 | } |
150 | | } |
151 | | |
152 | | impl PartialEq for ASN1Block { |
153 | 0 | fn eq(&self, other: &ASN1Block) -> bool { |
154 | 0 | match (self, other) { |
155 | 0 | (&ASN1Block::Boolean(_, a1), &ASN1Block::Boolean(_, a2)) => a1 == a2, |
156 | 0 | (&ASN1Block::Integer(_, ref a1), &ASN1Block::Integer(_, ref a2)) => a1 == a2, |
157 | 0 | (&ASN1Block::BitString(_, a1, ref b1), &ASN1Block::BitString(_, a2, ref b2)) => { |
158 | 0 | (a1 == a2) && (b1 == b2) |
159 | | } |
160 | 0 | (&ASN1Block::OctetString(_, ref a1), &ASN1Block::OctetString(_, ref a2)) => a1 == a2, |
161 | 0 | (&ASN1Block::Null(_), &ASN1Block::Null(_)) => true, |
162 | 0 | (&ASN1Block::ObjectIdentifier(_, ref a1), &ASN1Block::ObjectIdentifier(_, ref a2)) => { |
163 | 0 | a1 == a2 |
164 | | } |
165 | 0 | (&ASN1Block::UTF8String(_, ref a1), &ASN1Block::UTF8String(_, ref a2)) => a1 == a2, |
166 | 0 | (&ASN1Block::PrintableString(_, ref a1), &ASN1Block::PrintableString(_, ref a2)) => { |
167 | 0 | a1 == a2 |
168 | | } |
169 | 0 | (&ASN1Block::TeletexString(_, ref a1), &ASN1Block::TeletexString(_, ref a2)) => { |
170 | 0 | a1 == a2 |
171 | | } |
172 | 0 | (&ASN1Block::IA5String(_, ref a1), &ASN1Block::IA5String(_, ref a2)) => a1 == a2, |
173 | 0 | (&ASN1Block::UTCTime(_, ref a1), &ASN1Block::UTCTime(_, ref a2)) => a1 == a2, |
174 | 0 | (&ASN1Block::GeneralizedTime(_, ref a1), &ASN1Block::GeneralizedTime(_, ref a2)) => { |
175 | 0 | a1 == a2 |
176 | | } |
177 | 0 | (&ASN1Block::UniversalString(_, ref a1), &ASN1Block::UniversalString(_, ref a2)) => { |
178 | 0 | a1 == a2 |
179 | | } |
180 | 0 | (&ASN1Block::BMPString(_, ref a1), &ASN1Block::BMPString(_, ref a2)) => a1 == a2, |
181 | 0 | (&ASN1Block::Sequence(_, ref a1), &ASN1Block::Sequence(_, ref a2)) => a1 == a2, |
182 | 0 | (&ASN1Block::Set(_, ref a1), &ASN1Block::Set(_, ref a2)) => a1 == a2, |
183 | | ( |
184 | 0 | &ASN1Block::Explicit(a1, _, ref b1, ref c1), |
185 | 0 | &ASN1Block::Explicit(a2, _, ref b2, ref c2), |
186 | 0 | ) => (a1 == a2) && (b1 == b2) && (c1 == c2), |
187 | | ( |
188 | 0 | &ASN1Block::Unknown(a1, b1, _, ref c1, ref d1), |
189 | 0 | &ASN1Block::Unknown(a2, b2, _, ref c2, ref d2), |
190 | 0 | ) => (a1 == a2) && (b1 == b2) && (c1 == c2) && (d1 == d2), |
191 | 0 | _ => false, |
192 | | } |
193 | 0 | } |
194 | | } |
195 | | |
196 | | /// An ASN.1 OID. |
197 | | #[derive(Clone, Debug, PartialEq, Eq)] |
198 | | pub struct OID(Vec<BigUint>); |
199 | | |
200 | | impl OID { |
201 | | /// Generate an ASN.1. The vector should be in the obvious format, |
202 | | /// with each component going left-to-right. |
203 | 0 | pub fn new(x: Vec<BigUint>) -> OID { |
204 | 0 | OID(x) |
205 | 0 | } |
206 | | |
207 | | /// converts the |
208 | 0 | pub fn as_raw(&self) -> Result<Vec<u8>, ASN1EncodeErr> { |
209 | 0 | match (self.0.get(0), self.0.get(1)) { |
210 | 0 | (Some(v1), Some(v2)) => { |
211 | 0 | let two = BigUint::from_u8(2).unwrap(); |
212 | | |
213 | | // first, validate that the first two items meet spec |
214 | 0 | if v1 > &two { |
215 | 0 | return Err(ASN1EncodeErr::ObjectIdentVal1TooLarge); |
216 | 0 | } |
217 | | |
218 | 0 | let u175 = BigUint::from_u8(175).unwrap(); |
219 | 0 | let u39 = BigUint::from_u8(39).unwrap(); |
220 | 0 | let bound = if v1 == &two { u175 } else { u39 }; |
221 | | |
222 | 0 | if v2 > &bound { |
223 | 0 | return Err(ASN1EncodeErr::ObjectIdentVal2TooLarge); |
224 | 0 | } |
225 | | |
226 | | // the following unwraps must be safe, based on the |
227 | | // validation above. |
228 | 0 | let value1 = v1.to_u8().unwrap(); |
229 | 0 | let value2 = v2.to_u8().unwrap(); |
230 | 0 | let byte1 = (value1 * 40) + value2; |
231 | | |
232 | | // now we can build all the rest of the body |
233 | 0 | let mut body = vec![byte1]; |
234 | 0 | for num in self.0.iter().skip(2) { |
235 | 0 | let mut local = encode_base127(num); |
236 | 0 | body.append(&mut local); |
237 | 0 | } |
238 | | |
239 | 0 | Ok(body) |
240 | | } |
241 | 0 | _ => Err(ASN1EncodeErr::ObjectIdentHasTooFewFields), |
242 | | } |
243 | 0 | } |
244 | | |
245 | 0 | pub fn as_vec<'a, T: TryFrom<&'a BigUint>>(&'a self) -> Result<Vec<T>, ASN1DecodeErr> { |
246 | 0 | let mut vec = Vec::new(); |
247 | 0 | for val in self.0.iter() { |
248 | 0 | let ul = match T::try_from(val) { |
249 | 0 | Ok(a) => a, |
250 | 0 | Err(_) => return Err(ASN1DecodeErr::Overflow), |
251 | | }; |
252 | 0 | vec.push(ul); |
253 | | } |
254 | | |
255 | 0 | Ok(vec) |
256 | 0 | } |
257 | | } |
258 | | |
259 | | impl<'a> PartialEq<OID> for &'a OID { |
260 | 0 | fn eq(&self, v2: &OID) -> bool { |
261 | 0 | let &&OID(ref vec1) = self; |
262 | 0 | let &OID(ref vec2) = v2; |
263 | | |
264 | 0 | if vec1.len() != vec2.len() { |
265 | 0 | return false; |
266 | 0 | } |
267 | | |
268 | 0 | for i in 0..vec1.len() { |
269 | 0 | if vec1[i] != vec2[i] { |
270 | 0 | return false; |
271 | 0 | } |
272 | | } |
273 | | |
274 | 0 | true |
275 | 0 | } |
276 | | } |
277 | | |
278 | | /// A handy macro for generating OIDs from a sequence of `u64`s. |
279 | | /// |
280 | | /// Usage: oid!(1,2,840,113549,1,1,1) creates an OID that matches |
281 | | /// 1.2.840.113549.1.1.1. (Coincidentally, this is RSA.) |
282 | | #[macro_export] |
283 | | macro_rules! oid { |
284 | | ( $( $e: expr ),* ) => {{ |
285 | | $crate::OID::new(vec![$($crate::BigUint::from($e as u64)),*]) |
286 | | }}; |
287 | | } |
288 | | |
289 | | const PRINTABLE_CHARS: &str = |
290 | | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'()+,-./:=? "; |
291 | | |
292 | | #[cfg(test)] |
293 | | const KNOWN_TAGS: &[u8] = &[ |
294 | | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0c, 0x10, 0x11, 0x13, 0x14, 0x16, 0x17, 0x18, 0x1c, 0x1e, |
295 | | ]; |
296 | | |
297 | | /// An error that can arise decoding ASN.1 primitive blocks. |
298 | | #[derive(Clone, Debug, Error, PartialEq)] |
299 | | pub enum ASN1DecodeErr { |
300 | | #[error("Encountered an empty buffer decoding ASN1 block.")] |
301 | | EmptyBuffer, |
302 | | #[error("Bad length field in boolean block: {0}")] |
303 | | BadBooleanLength(usize), |
304 | | #[error("Length field too large for object type: {0}")] |
305 | | LengthTooLarge(usize), |
306 | | #[error("UTF8 string failed to properly decode: {0}")] |
307 | | UTF8DecodeFailure(Utf8Error), |
308 | | #[error("Printable string failed to properly decode.")] |
309 | | PrintableStringDecodeFailure, |
310 | | #[error("Invalid date value: {0}")] |
311 | | InvalidDateValue(String), |
312 | | #[error("Invalid length of bit string: {0}")] |
313 | | InvalidBitStringLength(isize), |
314 | | /// Not a valid ASN.1 class |
315 | | #[error("Invalid class value: {0}")] |
316 | | InvalidClass(u8), |
317 | | /// Expected more input |
318 | | /// |
319 | | /// Invalid ASN.1 input can lead to this error. |
320 | | #[error("Incomplete data or invalid ASN1")] |
321 | | Incomplete, |
322 | | #[error("Value overflow")] |
323 | | Overflow, |
324 | | } |
325 | | |
326 | | /// An error that can arise encoding ASN.1 primitive blocks. |
327 | | #[derive(Clone, Debug, Error, PartialEq)] |
328 | | pub enum ASN1EncodeErr { |
329 | | #[error("ASN1 object identifier has too few fields.")] |
330 | | ObjectIdentHasTooFewFields, |
331 | | #[error("First value in ASN1 OID is too big.")] |
332 | | ObjectIdentVal1TooLarge, |
333 | | #[error("Second value in ASN1 OID is too big.")] |
334 | | ObjectIdentVal2TooLarge, |
335 | | } |
336 | | |
337 | | /// Translate a binary blob into a series of `ASN1Block`s, or provide an |
338 | | /// error if it didn't work. |
339 | 0 | pub fn from_der(i: &[u8]) -> Result<Vec<ASN1Block>, ASN1DecodeErr> { |
340 | 0 | from_der_(i, 0) |
341 | 0 | } |
342 | | |
343 | 0 | fn from_der_(i: &[u8], start_offset: usize) -> Result<Vec<ASN1Block>, ASN1DecodeErr> { |
344 | 0 | let mut result: Vec<ASN1Block> = Vec::new(); |
345 | 0 | let mut index: usize = 0; |
346 | 0 | let len = i.len(); |
347 | | |
348 | 0 | while index < len { |
349 | 0 | let soff = start_offset + index; |
350 | 0 | let (tag, constructed, class) = decode_tag(i, &mut index)?; |
351 | 0 | let len = decode_length(i, &mut index)?; |
352 | 0 | let checklen = index |
353 | 0 | .checked_add(len) |
354 | 0 | .ok_or(ASN1DecodeErr::LengthTooLarge(len))?; |
355 | 0 | if checklen > i.len() { |
356 | 0 | return Err(ASN1DecodeErr::Incomplete); |
357 | 0 | } |
358 | 0 | let body = &i[index..(index + len)]; |
359 | | |
360 | 0 | if class != ASN1Class::Universal { |
361 | 0 | if constructed { |
362 | | // Try to read as explicitly tagged |
363 | 0 | if let Ok(mut items) = from_der_(body, start_offset + index) { |
364 | 0 | if items.len() == 1 { |
365 | 0 | result.push(ASN1Block::Explicit( |
366 | 0 | class, |
367 | 0 | soff, |
368 | 0 | tag, |
369 | 0 | Box::new(items.remove(0)), |
370 | 0 | )); |
371 | 0 | index += len; |
372 | 0 | continue; |
373 | 0 | } |
374 | 0 | } |
375 | 0 | } |
376 | 0 | result.push(ASN1Block::Unknown( |
377 | 0 | class, |
378 | 0 | constructed, |
379 | 0 | soff, |
380 | 0 | tag, |
381 | 0 | body.to_vec(), |
382 | 0 | )); |
383 | 0 | index += len; |
384 | 0 | continue; |
385 | 0 | } |
386 | | |
387 | | // Universal class |
388 | 0 | match tag.to_u8() { |
389 | | // BOOLEAN |
390 | | Some(0x01) => { |
391 | 0 | if len != 1 { |
392 | 0 | return Err(ASN1DecodeErr::BadBooleanLength(len)); |
393 | 0 | } |
394 | 0 | result.push(ASN1Block::Boolean(soff, body[0] != 0)); |
395 | | } |
396 | | // INTEGER |
397 | 0 | Some(0x02) => { |
398 | 0 | let res = BigInt::from_signed_bytes_be(body); |
399 | 0 | result.push(ASN1Block::Integer(soff, res)); |
400 | 0 | } |
401 | | // BIT STRING |
402 | 0 | Some(0x03) if body.is_empty() => result.push(ASN1Block::BitString(soff, 0, Vec::new())), |
403 | | Some(0x03) => { |
404 | 0 | let bits = (&body[1..]).to_vec(); |
405 | 0 | let bitcount = bits.len() * 8; |
406 | 0 | let rest = body[0] as usize; |
407 | 0 | if bitcount < rest { |
408 | 0 | return Err(ASN1DecodeErr::InvalidBitStringLength( |
409 | 0 | bitcount as isize - rest as isize, |
410 | 0 | )); |
411 | 0 | } |
412 | | |
413 | 0 | let nbits = bitcount - (body[0] as usize); |
414 | 0 | result.push(ASN1Block::BitString(soff, nbits, bits)) |
415 | | } |
416 | | // OCTET STRING |
417 | 0 | Some(0x04) => result.push(ASN1Block::OctetString(soff, body.to_vec())), |
418 | | // NULL |
419 | 0 | Some(0x05) => { |
420 | 0 | result.push(ASN1Block::Null(soff)); |
421 | 0 | } |
422 | | // OBJECT IDENTIFIER |
423 | | Some(0x06) => { |
424 | 0 | let mut value1 = BigUint::zero(); |
425 | 0 | if body.is_empty() { |
426 | 0 | return Err(ASN1DecodeErr::Incomplete); |
427 | 0 | } |
428 | 0 | let mut value2 = BigUint::from_u8(body[0]).unwrap(); |
429 | 0 | let mut oidres = Vec::new(); |
430 | 0 | let mut bindex = 1; |
431 | | |
432 | 0 | if body[0] >= 40 { |
433 | 0 | if body[0] < 80 { |
434 | 0 | value1 = BigUint::one(); |
435 | 0 | value2 -= BigUint::from_u8(40).unwrap(); |
436 | 0 | } else { |
437 | 0 | value1 = BigUint::from_u8(2).unwrap(); |
438 | 0 | value2 -= BigUint::from_u8(80).unwrap(); |
439 | 0 | } |
440 | 0 | } |
441 | | |
442 | 0 | oidres.push(value1); |
443 | 0 | oidres.push(value2); |
444 | 0 | while bindex < body.len() { |
445 | 0 | oidres.push(decode_base127(body, &mut bindex)?); |
446 | | } |
447 | 0 | let res = OID(oidres); |
448 | | |
449 | 0 | result.push(ASN1Block::ObjectIdentifier(soff, res)) |
450 | | } |
451 | | // UTF8STRING |
452 | 0 | Some(0x0C) => match String::from_utf8(body.to_vec()) { |
453 | 0 | Ok(v) => result.push(ASN1Block::UTF8String(soff, v)), |
454 | 0 | Err(e) => return Err(ASN1DecodeErr::UTF8DecodeFailure(e.utf8_error())), |
455 | | }, |
456 | | // SEQUENCE |
457 | 0 | Some(0x10) => match from_der_(body, start_offset + index) { |
458 | 0 | Ok(items) => result.push(ASN1Block::Sequence(soff, items)), |
459 | 0 | Err(e) => return Err(e), |
460 | | }, |
461 | | // SET |
462 | 0 | Some(0x11) => match from_der_(body, start_offset + index) { |
463 | 0 | Ok(items) => result.push(ASN1Block::Set(soff, items)), |
464 | 0 | Err(e) => return Err(e), |
465 | | }, |
466 | | // PRINTABLE STRING |
467 | | Some(0x13) => { |
468 | 0 | let mut res = String::new(); |
469 | 0 | let val = body.iter().map(|x| *x as char); |
470 | | |
471 | 0 | for c in val { |
472 | 0 | if PRINTABLE_CHARS.contains(c) { |
473 | 0 | res.push(c); |
474 | 0 | } else { |
475 | 0 | return Err(ASN1DecodeErr::PrintableStringDecodeFailure); |
476 | | } |
477 | | } |
478 | 0 | result.push(ASN1Block::PrintableString(soff, res)); |
479 | | } |
480 | | // TELETEX STRINGS |
481 | 0 | Some(0x14) => match String::from_utf8(body.to_vec()) { |
482 | 0 | Ok(v) => result.push(ASN1Block::TeletexString(soff, v)), |
483 | 0 | Err(e) => return Err(ASN1DecodeErr::UTF8DecodeFailure(e.utf8_error())), |
484 | | }, |
485 | | // IA5 (ASCII) STRING |
486 | | Some(0x16) => { |
487 | 0 | let val = body.iter().map(|x| *x as char); |
488 | 0 | let res = String::from_iter(val); |
489 | 0 | result.push(ASN1Block::IA5String(soff, res)) |
490 | | } |
491 | | // UTCTime |
492 | | Some(0x17) => { |
493 | 0 | if body.len() != 13 { |
494 | 0 | return Err(ASN1DecodeErr::InvalidDateValue(format!("{}", body.len()))); |
495 | 0 | } |
496 | | |
497 | 0 | let v = String::from_iter(body.iter().map(|x| *x as char)); |
498 | | |
499 | 0 | let y = match v.get(0..2) { |
500 | 0 | Some(yy) => yy, |
501 | | None => { |
502 | | // This wasn't a valid character boundrary. |
503 | 0 | return Err(ASN1DecodeErr::InvalidDateValue(v)); |
504 | | } |
505 | | }; |
506 | | |
507 | 0 | let y_prefix = match y.parse::<u8>() { |
508 | 0 | Err(_) => return Err(ASN1DecodeErr::InvalidDateValue(v)), |
509 | 0 | Ok(y) => { |
510 | 0 | if y >= 50 { |
511 | 0 | "19" |
512 | | } else { |
513 | 0 | "20" |
514 | | } |
515 | | } |
516 | | }; |
517 | | |
518 | 0 | let v = format!("{}{}", y_prefix, v); |
519 | | |
520 | 0 | let format = time::format_description::parse( |
521 | 0 | "[year][month][day][hour repr:24][minute][second]Z", |
522 | | ) |
523 | 0 | .unwrap(); |
524 | | |
525 | 0 | match PrimitiveDateTime::parse(&v, &format) { |
526 | 0 | Err(_) => return Err(ASN1DecodeErr::InvalidDateValue(v)), |
527 | 0 | Ok(t) => result.push(ASN1Block::UTCTime(soff, t)), |
528 | | } |
529 | | } |
530 | | // GeneralizedTime |
531 | | Some(0x18) => { |
532 | 0 | if body.len() < 15 { |
533 | 0 | return Err(ASN1DecodeErr::InvalidDateValue(format!("{}", body.len()))); |
534 | 0 | } |
535 | | |
536 | 0 | let mut v: String = String::from_utf8(body.to_vec()) |
537 | 0 | .map_err(|e| ASN1DecodeErr::UTF8DecodeFailure(e.utf8_error()))?; |
538 | | // Make sure the string is ascii, otherwise we cannot insert |
539 | | // chars at specific bytes. |
540 | 0 | if !v.is_ascii() { |
541 | 0 | return Err(ASN1DecodeErr::InvalidDateValue(v)); |
542 | 0 | } |
543 | | |
544 | | // We need to add padding back to the string if it's not there. |
545 | 0 | if !v.contains('.') { |
546 | 0 | v.insert(14, '.') |
547 | 0 | } |
548 | 0 | while v.len() < 25 { |
549 | 0 | let idx = v.len() - 1; |
550 | 0 | v.insert(idx, '0'); |
551 | 0 | } |
552 | | |
553 | 0 | let format = time::format_description::parse( |
554 | 0 | "[year][month][day][hour repr:24][minute][second].[subsecond]Z", |
555 | | ) |
556 | 0 | .unwrap(); |
557 | | |
558 | 0 | match PrimitiveDateTime::parse(&v, &format) { |
559 | 0 | Err(_) => return Err(ASN1DecodeErr::InvalidDateValue(v)), |
560 | 0 | Ok(t) => result.push(ASN1Block::GeneralizedTime(soff, t)), |
561 | | } |
562 | | } |
563 | | // UNIVERSAL STRINGS |
564 | 0 | Some(0x1C) => match String::from_utf8(body.to_vec()) { |
565 | 0 | Ok(v) => result.push(ASN1Block::UniversalString(soff, v)), |
566 | 0 | Err(e) => return Err(ASN1DecodeErr::UTF8DecodeFailure(e.utf8_error())), |
567 | | }, |
568 | | // UNIVERSAL STRINGS |
569 | 0 | Some(0x1E) => match String::from_utf8(body.to_vec()) { |
570 | 0 | Ok(v) => result.push(ASN1Block::BMPString(soff, v)), |
571 | 0 | Err(e) => return Err(ASN1DecodeErr::UTF8DecodeFailure(e.utf8_error())), |
572 | | }, |
573 | | // Dunno. |
574 | 0 | _ => { |
575 | 0 | result.push(ASN1Block::Unknown( |
576 | 0 | class, |
577 | 0 | constructed, |
578 | 0 | soff, |
579 | 0 | tag, |
580 | 0 | body.to_vec(), |
581 | 0 | )); |
582 | 0 | } |
583 | | } |
584 | 0 | index += len; |
585 | | } |
586 | | |
587 | 0 | if result.is_empty() { |
588 | 0 | Err(ASN1DecodeErr::EmptyBuffer) |
589 | | } else { |
590 | 0 | Ok(result) |
591 | | } |
592 | 0 | } |
593 | | |
594 | | /// Returns the tag, if the type is constructed and the class. |
595 | 0 | fn decode_tag(i: &[u8], index: &mut usize) -> Result<(BigUint, bool, ASN1Class), ASN1DecodeErr> { |
596 | 0 | if *index >= i.len() { |
597 | 0 | return Err(ASN1DecodeErr::Incomplete); |
598 | 0 | } |
599 | 0 | let tagbyte = i[*index]; |
600 | 0 | let constructed = (tagbyte & 0b0010_0000) != 0; |
601 | 0 | let class = decode_class(tagbyte)?; |
602 | 0 | let basetag = tagbyte & 0b1_1111; |
603 | | |
604 | 0 | *index += 1; |
605 | | |
606 | 0 | if basetag == 0b1_1111 { |
607 | 0 | let res = decode_base127(i, index)?; |
608 | 0 | Ok((res, constructed, class)) |
609 | | } else { |
610 | 0 | Ok((BigUint::from(basetag), constructed, class)) |
611 | | } |
612 | 0 | } |
613 | | |
614 | 0 | fn decode_base127(i: &[u8], index: &mut usize) -> Result<BigUint, ASN1DecodeErr> { |
615 | 0 | let mut res = BigUint::zero(); |
616 | | |
617 | | loop { |
618 | 0 | if *index >= i.len() { |
619 | 0 | return Err(ASN1DecodeErr::Incomplete); |
620 | 0 | } |
621 | | |
622 | 0 | let nextbyte = i[*index]; |
623 | | |
624 | 0 | *index += 1; |
625 | 0 | res = (res << 7) + BigUint::from(nextbyte & 0x7f); |
626 | 0 | if (nextbyte & 0x80) == 0 { |
627 | 0 | return Ok(res); |
628 | 0 | } |
629 | | } |
630 | 0 | } |
631 | | |
632 | 0 | fn decode_class(i: u8) -> Result<ASN1Class, ASN1DecodeErr> { |
633 | 0 | match i >> 6 { |
634 | 0 | 0b00 => Ok(ASN1Class::Universal), |
635 | 0 | 0b01 => Ok(ASN1Class::Application), |
636 | 0 | 0b10 => Ok(ASN1Class::ContextSpecific), |
637 | 0 | 0b11 => Ok(ASN1Class::Private), |
638 | 0 | _ => Err(ASN1DecodeErr::InvalidClass(i)), |
639 | | } |
640 | 0 | } |
641 | | |
642 | 0 | fn decode_length(i: &[u8], index: &mut usize) -> Result<usize, ASN1DecodeErr> { |
643 | 0 | if *index >= i.len() { |
644 | 0 | return Err(ASN1DecodeErr::Incomplete); |
645 | 0 | } |
646 | 0 | let startbyte = i[*index]; |
647 | | |
648 | | // NOTE: Technically, this size can be much larger than a usize. |
649 | | // However, our whole universe starts to break down if we get |
650 | | // things that big. So we're boring, and only accept lengths |
651 | | // that fit within a usize. |
652 | 0 | *index += 1; |
653 | 0 | if startbyte >= 0x80 { |
654 | 0 | let mut lenlen = (startbyte & 0x7f) as usize; |
655 | 0 | let mut res = 0; |
656 | | |
657 | 0 | if lenlen > size_of::<usize>() { |
658 | 0 | return Err(ASN1DecodeErr::LengthTooLarge(lenlen)); |
659 | 0 | } |
660 | | |
661 | 0 | while lenlen > 0 { |
662 | 0 | if *index >= i.len() { |
663 | 0 | return Err(ASN1DecodeErr::Incomplete); |
664 | 0 | } |
665 | | |
666 | 0 | res = (res << 8) + (i[*index] as usize); |
667 | | |
668 | 0 | *index += 1; |
669 | 0 | lenlen -= 1; |
670 | | } |
671 | | |
672 | 0 | Ok(res) |
673 | | } else { |
674 | 0 | Ok(startbyte as usize) |
675 | | } |
676 | 0 | } |
677 | | |
678 | | /// Given an `ASN1Block`, covert it to its DER encoding, or return an error |
679 | | /// if something broke along the way. |
680 | 0 | pub fn to_der(i: &ASN1Block) -> Result<Vec<u8>, ASN1EncodeErr> { |
681 | 0 | match *i { |
682 | | // BOOLEAN |
683 | 0 | ASN1Block::Boolean(_, val) => { |
684 | 0 | let inttag = BigUint::one(); |
685 | 0 | let mut tagbytes = encode_tag(ASN1Class::Universal, false, &inttag); |
686 | 0 | tagbytes.push(1); |
687 | 0 | tagbytes.push(if val { 0xFF } else { 0x00 }); |
688 | 0 | Ok(tagbytes) |
689 | | } |
690 | | // INTEGER |
691 | 0 | ASN1Block::Integer(_, ref int) => { |
692 | 0 | let mut base = int.to_signed_bytes_be(); |
693 | 0 | let mut lenbytes = encode_len(base.len()); |
694 | 0 | let inttag = BigUint::from_u8(0x02).unwrap(); |
695 | 0 | let mut tagbytes = encode_tag(ASN1Class::Universal, false, &inttag); |
696 | | |
697 | 0 | let mut result = Vec::new(); |
698 | 0 | result.append(&mut tagbytes); |
699 | 0 | result.append(&mut lenbytes); |
700 | 0 | result.append(&mut base); |
701 | 0 | Ok(result) |
702 | | } |
703 | | // BIT STRING |
704 | 0 | ASN1Block::BitString(_, bits, ref vs) => { |
705 | 0 | let inttag = BigUint::from_u8(0x03).unwrap(); |
706 | 0 | let mut tagbytes = encode_tag(ASN1Class::Universal, false, &inttag); |
707 | | |
708 | 0 | if bits == 0 { |
709 | 0 | tagbytes.push(0); |
710 | 0 | Ok(tagbytes) |
711 | | } else { |
712 | 0 | let mut lenbytes = encode_len(vs.len() + 1); |
713 | 0 | let nbits = (vs.len() * 8) - bits; |
714 | | |
715 | 0 | let mut result = Vec::new(); |
716 | 0 | result.append(&mut tagbytes); |
717 | 0 | result.append(&mut lenbytes); |
718 | 0 | result.push(nbits as u8); |
719 | 0 | result.extend_from_slice(vs); |
720 | 0 | Ok(result) |
721 | | } |
722 | | } |
723 | | // OCTET STRING |
724 | 0 | ASN1Block::OctetString(_, ref bytes) => { |
725 | 0 | let inttag = BigUint::from_u8(0x04).unwrap(); |
726 | 0 | let mut tagbytes = encode_tag(ASN1Class::Universal, false, &inttag); |
727 | 0 | let mut lenbytes = encode_len(bytes.len()); |
728 | | |
729 | 0 | let mut result = Vec::new(); |
730 | 0 | result.append(&mut tagbytes); |
731 | 0 | result.append(&mut lenbytes); |
732 | 0 | result.extend_from_slice(bytes); |
733 | 0 | Ok(result) |
734 | | } |
735 | | // NULL |
736 | | ASN1Block::Null(_) => { |
737 | 0 | let inttag = BigUint::from_u8(0x05).unwrap(); |
738 | 0 | let mut result = encode_tag(ASN1Class::Universal, false, &inttag); |
739 | 0 | result.push(0); |
740 | 0 | Ok(result) |
741 | | } |
742 | | // OBJECT IDENTIFIER |
743 | 0 | ASN1Block::ObjectIdentifier(_, OID(ref nums)) => { |
744 | 0 | match (nums.get(0), nums.get(1)) { |
745 | 0 | (Some(v1), Some(v2)) => { |
746 | 0 | let two = BigUint::from_u8(2).unwrap(); |
747 | | |
748 | | // first, validate that the first two items meet spec |
749 | 0 | if v1 > &two { |
750 | 0 | return Err(ASN1EncodeErr::ObjectIdentVal1TooLarge); |
751 | 0 | } |
752 | | |
753 | 0 | let u175 = BigUint::from_u8(175).unwrap(); |
754 | 0 | let u39 = BigUint::from_u8(39).unwrap(); |
755 | 0 | let bound = if v1 == &two { u175 } else { u39 }; |
756 | | |
757 | 0 | if v2 > &bound { |
758 | 0 | return Err(ASN1EncodeErr::ObjectIdentVal2TooLarge); |
759 | 0 | } |
760 | | |
761 | | // the following unwraps must be safe, based on the |
762 | | // validation above. |
763 | 0 | let value1 = v1.to_u8().unwrap(); |
764 | 0 | let value2 = v2.to_u8().unwrap(); |
765 | 0 | let byte1 = (value1 * 40) + value2; |
766 | | |
767 | | // now we can build all the rest of the body |
768 | 0 | let mut body = vec![byte1]; |
769 | 0 | for num in nums.iter().skip(2) { |
770 | 0 | let mut local = encode_base127(num); |
771 | 0 | body.append(&mut local); |
772 | 0 | } |
773 | | |
774 | | // now that we have the body, we can build the header |
775 | 0 | let inttag = BigUint::from_u8(0x06).unwrap(); |
776 | 0 | let mut result = encode_tag(ASN1Class::Universal, false, &inttag); |
777 | 0 | let mut lenbytes = encode_len(body.len()); |
778 | | |
779 | 0 | result.append(&mut lenbytes); |
780 | 0 | result.append(&mut body); |
781 | | |
782 | 0 | Ok(result) |
783 | | } |
784 | 0 | _ => Err(ASN1EncodeErr::ObjectIdentHasTooFewFields), |
785 | | } |
786 | | } |
787 | | // SEQUENCE |
788 | 0 | ASN1Block::Sequence(_, ref items) => { |
789 | 0 | let mut body = Vec::new(); |
790 | | |
791 | | // put all the subsequences into a block |
792 | 0 | for x in items.iter() { |
793 | 0 | let mut bytes = to_der(x)?; |
794 | 0 | body.append(&mut bytes); |
795 | | } |
796 | | |
797 | 0 | let inttag = BigUint::from_u8(0x10).unwrap(); |
798 | 0 | let mut lenbytes = encode_len(body.len()); |
799 | | // SEQUENCE and SET mut have the constructed encoding form (bit 5) set |
800 | | // See: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-encoded-tag-bytes |
801 | 0 | let mut tagbytes = encode_tag(ASN1Class::Universal, true, &inttag); |
802 | | |
803 | 0 | let mut res = Vec::new(); |
804 | 0 | res.append(&mut tagbytes); |
805 | 0 | res.append(&mut lenbytes); |
806 | 0 | res.append(&mut body); |
807 | 0 | Ok(res) |
808 | | } |
809 | | // SET |
810 | 0 | ASN1Block::Set(_, ref items) => { |
811 | 0 | let mut body = Vec::new(); |
812 | | |
813 | | // put all the subsequences into a block |
814 | 0 | for x in items.iter() { |
815 | 0 | let mut bytes = to_der(x)?; |
816 | 0 | body.append(&mut bytes); |
817 | | } |
818 | | |
819 | 0 | let inttag = BigUint::from_u8(0x11).unwrap(); |
820 | 0 | let mut lenbytes = encode_len(body.len()); |
821 | | // SEQUENCE and SET mut have the constructed encoding form (bit 5) set |
822 | | // See: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-encoded-tag-bytes |
823 | 0 | let mut tagbytes = encode_tag(ASN1Class::Universal, true, &inttag); |
824 | | |
825 | 0 | let mut res = Vec::new(); |
826 | 0 | res.append(&mut tagbytes); |
827 | 0 | res.append(&mut lenbytes); |
828 | 0 | res.append(&mut body); |
829 | 0 | Ok(res) |
830 | | } |
831 | 0 | ASN1Block::UTCTime(_, ref time) => { |
832 | 0 | let format = time::format_description::parse( |
833 | 0 | "[year][month][day][hour repr:24][minute][second]Z", |
834 | | ) |
835 | 0 | .unwrap(); |
836 | 0 | let mut body = time.format(&format).unwrap().into_bytes(); |
837 | 0 | body.drain(0..2); |
838 | 0 | let inttag = BigUint::from_u8(0x17).unwrap(); |
839 | 0 | let mut lenbytes = encode_len(body.len()); |
840 | 0 | let mut tagbytes = encode_tag(ASN1Class::Universal, false, &inttag); |
841 | | |
842 | 0 | let mut res = Vec::new(); |
843 | 0 | res.append(&mut tagbytes); |
844 | 0 | res.append(&mut lenbytes); |
845 | 0 | res.append(&mut body); |
846 | 0 | Ok(res) |
847 | | } |
848 | 0 | ASN1Block::GeneralizedTime(_, ref time) => { |
849 | 0 | let format = time::format_description::parse( |
850 | 0 | "[year][month][day][hour repr:24][minute][second].[subsecond]", |
851 | | ) |
852 | 0 | .unwrap(); |
853 | 0 | let base = time.format(&format).unwrap(); |
854 | 0 | let zclear = base.trim_end_matches('0'); |
855 | 0 | let dclear = zclear.trim_end_matches('.'); |
856 | 0 | let mut body = format!("{}Z", dclear).into_bytes(); |
857 | | |
858 | 0 | let inttag = BigUint::from_u8(0x18).unwrap(); |
859 | 0 | let mut lenbytes = encode_len(body.len()); |
860 | 0 | let mut tagbytes = encode_tag(ASN1Class::Universal, false, &inttag); |
861 | | |
862 | 0 | let mut res = Vec::new(); |
863 | 0 | res.append(&mut tagbytes); |
864 | 0 | res.append(&mut lenbytes); |
865 | 0 | res.append(&mut body); |
866 | 0 | Ok(res) |
867 | | } |
868 | 0 | ASN1Block::UTF8String(_, ref str) => { |
869 | 0 | encode_asn1_string(0x0c, false, ASN1Class::Universal, str) |
870 | | } |
871 | 0 | ASN1Block::PrintableString(_, ref str) => { |
872 | 0 | encode_asn1_string(0x13, true, ASN1Class::Universal, str) |
873 | | } |
874 | 0 | ASN1Block::TeletexString(_, ref str) => { |
875 | 0 | encode_asn1_string(0x14, false, ASN1Class::Universal, str) |
876 | | } |
877 | 0 | ASN1Block::UniversalString(_, ref str) => { |
878 | 0 | encode_asn1_string(0x1c, false, ASN1Class::Universal, str) |
879 | | } |
880 | 0 | ASN1Block::IA5String(_, ref str) => { |
881 | 0 | encode_asn1_string(0x16, true, ASN1Class::Universal, str) |
882 | | } |
883 | 0 | ASN1Block::BMPString(_, ref str) => { |
884 | 0 | encode_asn1_string(0x1e, false, ASN1Class::Universal, str) |
885 | | } |
886 | 0 | ASN1Block::Explicit(class, _, ref tag, ref item) => { |
887 | 0 | let mut tagbytes = encode_tag(class, true, tag); |
888 | 0 | let mut bytes = to_der(item)?; |
889 | 0 | let mut lenbytes = encode_len(bytes.len()); |
890 | | |
891 | 0 | let mut res = Vec::new(); |
892 | 0 | res.append(&mut tagbytes); |
893 | 0 | res.append(&mut lenbytes); |
894 | 0 | res.append(&mut bytes); |
895 | 0 | Ok(res) |
896 | | } |
897 | | // Unknown blocks |
898 | 0 | ASN1Block::Unknown(class, c, _, ref tag, ref bytes) => { |
899 | 0 | let mut tagbytes = encode_tag(class, c, tag); |
900 | 0 | let mut lenbytes = encode_len(bytes.len()); |
901 | | |
902 | 0 | let mut res = Vec::new(); |
903 | 0 | res.append(&mut tagbytes); |
904 | 0 | res.append(&mut lenbytes); |
905 | 0 | res.extend_from_slice(bytes); |
906 | 0 | Ok(res) |
907 | | } |
908 | | } |
909 | 0 | } |
910 | | |
911 | 0 | fn encode_asn1_string( |
912 | 0 | tag: u8, |
913 | 0 | force_chars: bool, |
914 | 0 | c: ASN1Class, |
915 | 0 | s: &str, |
916 | 0 | ) -> Result<Vec<u8>, ASN1EncodeErr> { |
917 | 0 | let mut body = { |
918 | 0 | if force_chars { |
919 | 0 | let mut out = Vec::new(); |
920 | | |
921 | 0 | for c in s.chars() { |
922 | 0 | out.push(c as u8); |
923 | 0 | } |
924 | 0 | out |
925 | | } else { |
926 | 0 | s.to_string().into_bytes() |
927 | | } |
928 | | }; |
929 | 0 | let inttag = BigUint::from_u8(tag).unwrap(); |
930 | 0 | let mut lenbytes = encode_len(body.len()); |
931 | 0 | let mut tagbytes = encode_tag(c, false, &inttag); |
932 | | |
933 | 0 | let mut res = Vec::new(); |
934 | 0 | res.append(&mut tagbytes); |
935 | 0 | res.append(&mut lenbytes); |
936 | 0 | res.append(&mut body); |
937 | 0 | Ok(res) |
938 | 0 | } |
939 | | |
940 | 0 | fn encode_tag(c: ASN1Class, constructed: bool, t: &BigUint) -> Vec<u8> { |
941 | 0 | let cbyte = encode_class(c); |
942 | | |
943 | 0 | match t.to_u8() { |
944 | 0 | Some(mut x) if x < 31 => { |
945 | 0 | if constructed { |
946 | 0 | x |= 0b0010_0000; |
947 | 0 | } |
948 | 0 | vec![cbyte | x] |
949 | | } |
950 | | _ => { |
951 | 0 | let mut res = encode_base127(t); |
952 | 0 | let mut x = cbyte | 0b0001_1111; |
953 | 0 | if constructed { |
954 | 0 | x |= 0b0010_0000; |
955 | 0 | } |
956 | 0 | res.insert(0, x); |
957 | 0 | res |
958 | | } |
959 | | } |
960 | 0 | } |
961 | | |
962 | 0 | fn encode_base127(v: &BigUint) -> Vec<u8> { |
963 | 0 | let mut acc = v.clone(); |
964 | 0 | let mut res = Vec::new(); |
965 | 0 | let u128 = BigUint::from_u8(128).unwrap(); |
966 | 0 | let zero = BigUint::zero(); |
967 | | |
968 | 0 | if acc == zero { |
969 | 0 | res.push(0); |
970 | 0 | return res; |
971 | 0 | } |
972 | | |
973 | 0 | while acc > zero { |
974 | | // we build this vector backwards |
975 | 0 | let digit = &acc % &u128; |
976 | 0 | acc >>= 7; |
977 | | |
978 | 0 | match digit.to_u8() { |
979 | 0 | None => panic!("7 bits don't fit into 8, cause ..."), |
980 | 0 | Some(x) if res.is_empty() => res.push(x), |
981 | 0 | Some(x) => res.push(x | 0x80), |
982 | | } |
983 | | } |
984 | | |
985 | 0 | res.reverse(); |
986 | 0 | res |
987 | 0 | } |
988 | | |
989 | 0 | fn encode_class(c: ASN1Class) -> u8 { |
990 | 0 | match c { |
991 | 0 | ASN1Class::Universal => 0b0000_0000, |
992 | 0 | ASN1Class::Application => 0b0100_0000, |
993 | 0 | ASN1Class::ContextSpecific => 0b1000_0000, |
994 | 0 | ASN1Class::Private => 0b1100_0000, |
995 | | } |
996 | 0 | } |
997 | | |
998 | 0 | fn encode_len(x: usize) -> Vec<u8> { |
999 | 0 | if x < 128 { |
1000 | 0 | vec![x as u8] |
1001 | | } else { |
1002 | 0 | let mut bstr = Vec::new(); |
1003 | 0 | let mut work = x; |
1004 | | |
1005 | | // convert this into bytes, backwards |
1006 | 0 | while work > 0 { |
1007 | 0 | bstr.push(work as u8); |
1008 | 0 | work >>= 8; |
1009 | 0 | } |
1010 | | |
1011 | | // encode the front of the length |
1012 | 0 | let len = bstr.len() as u8; |
1013 | 0 | bstr.push(len | 0x80); |
1014 | | |
1015 | | // and then reverse it into the right order |
1016 | 0 | bstr.reverse(); |
1017 | 0 | bstr |
1018 | | } |
1019 | 0 | } |
1020 | | |
1021 | | // ---------------------------------------------------------------------------- |
1022 | | |
1023 | | /// A trait defining types that can be decoded from an `ASN1Block` stream, |
1024 | | /// assuming they also have access to the underlying bytes making up the |
1025 | | /// stream. |
1026 | | pub trait FromASN1WithBody: Sized { |
1027 | | type Error: From<ASN1DecodeErr>; |
1028 | | |
1029 | | fn from_asn1_with_body<'a>( |
1030 | | v: &'a [ASN1Block], |
1031 | | _b: &[u8], |
1032 | | ) -> Result<(Self, &'a [ASN1Block]), Self::Error>; |
1033 | | } |
1034 | | |
1035 | | /// A trait defining types that can be decoded from an `ASN1Block` stream. |
1036 | | /// Any member of this trait is also automatically a member of |
1037 | | /// `FromASN1WithBody`, as it can obviously just ignore the body. |
1038 | | pub trait FromASN1: Sized { |
1039 | | type Error: From<ASN1DecodeErr>; |
1040 | | |
1041 | | fn from_asn1(v: &[ASN1Block]) -> Result<(Self, &[ASN1Block]), Self::Error>; |
1042 | | } |
1043 | | |
1044 | | impl<T: FromASN1> FromASN1WithBody for T { |
1045 | | type Error = T::Error; |
1046 | | |
1047 | 0 | fn from_asn1_with_body<'a>( |
1048 | 0 | v: &'a [ASN1Block], |
1049 | 0 | _b: &[u8], |
1050 | 0 | ) -> Result<(T, &'a [ASN1Block]), T::Error> { |
1051 | 0 | T::from_asn1(v) |
1052 | 0 | } |
1053 | | } |
1054 | | |
1055 | | /// Automatically decode a type via DER encoding, assuming that the type |
1056 | | /// is a member of `FromASN1` or `FromASN1WithBody`. |
1057 | 0 | pub fn der_decode<T: FromASN1WithBody>(v: &[u8]) -> Result<T, T::Error> { |
1058 | 0 | let vs = from_der(v)?; |
1059 | 0 | T::from_asn1_with_body(&vs, v).map(|(a, _)| a) |
1060 | 0 | } |
1061 | | |
1062 | | /// The set of types that can automatically converted into a sequence |
1063 | | /// of `ASN1Block`s. You should probably use to_asn1() but implement |
1064 | | /// to_asn1_class(). The former has a default implementation that passes |
1065 | | /// `ASN1Class::Universal` as the tag to use, which should be good for |
1066 | | /// most people. |
1067 | | pub trait ToASN1 { |
1068 | | type Error: From<ASN1EncodeErr>; |
1069 | | |
1070 | 0 | fn to_asn1(&self) -> Result<Vec<ASN1Block>, Self::Error> { |
1071 | 0 | self.to_asn1_class(ASN1Class::Universal) |
1072 | 0 | } |
1073 | | fn to_asn1_class(&self, c: ASN1Class) -> Result<Vec<ASN1Block>, Self::Error>; |
1074 | | } |
1075 | | |
1076 | | /// Automatically encode a type into binary via DER encoding, assuming |
1077 | | /// that the type is a member of `ToASN1`. |
1078 | 0 | pub fn der_encode<T: ToASN1>(v: &T) -> Result<Vec<u8>, T::Error> { |
1079 | 0 | let blocks = T::to_asn1(v)?; |
1080 | 0 | let mut res = Vec::new(); |
1081 | | |
1082 | 0 | for block in blocks { |
1083 | 0 | let mut x = to_der(&block)?; |
1084 | 0 | res.append(&mut x); |
1085 | | } |
1086 | | |
1087 | 0 | Ok(res) |
1088 | 0 | } |
1089 | | |
1090 | | // ---------------------------------------------------------------------------- |
1091 | | |
1092 | | #[cfg(test)] |
1093 | | mod tests { |
1094 | | use super::*; |
1095 | | use quickcheck::{Arbitrary, Gen}; |
1096 | | use std::fs::File; |
1097 | | use std::io::Read; |
1098 | | use time::{Date, Month, Time}; |
1099 | | |
1100 | | impl Arbitrary for ASN1Class { |
1101 | | fn arbitrary(g: &mut Gen) -> ASN1Class { |
1102 | | match u8::arbitrary(g) % 4 { |
1103 | | 0 => ASN1Class::Private, |
1104 | | 1 => ASN1Class::ContextSpecific, |
1105 | | 2 => ASN1Class::Universal, |
1106 | | 3 => ASN1Class::Application, |
1107 | | _ => panic!("I weep for a broken life."), |
1108 | | } |
1109 | | } |
1110 | | } |
1111 | | |
1112 | | quickcheck! { |
1113 | | fn class_encdec_roundtrips(c: ASN1Class) -> bool { |
1114 | | c == decode_class(encode_class(c.clone())).unwrap() |
1115 | | } |
1116 | | |
1117 | | fn class_decenc_roundtrips(v: u8) -> bool { |
1118 | | (v & 0b11000000) == encode_class(decode_class(v).unwrap()) |
1119 | | } |
1120 | | } |
1121 | | |
1122 | | #[derive(Clone, Debug)] |
1123 | | struct RandomUint { |
1124 | | x: BigUint, |
1125 | | } |
1126 | | |
1127 | | impl Arbitrary for RandomUint { |
1128 | | fn arbitrary(g: &mut Gen) -> RandomUint { |
1129 | | let v = BigUint::from_u32(u32::arbitrary(g)).unwrap(); |
1130 | | RandomUint { x: v } |
1131 | | } |
1132 | | } |
1133 | | |
1134 | | quickcheck! { |
1135 | | fn tags_encdec_roundtrips(c: ASN1Class, con: bool, t: RandomUint) -> bool { |
1136 | | let bytes = encode_tag(c, con, &t.x); |
1137 | | let mut zero = 0; |
1138 | | let (t2, con2, c2) = decode_tag(&bytes[..], &mut zero).unwrap(); |
1139 | | (c == c2) && (con == con2) && (t.x == t2) |
1140 | | } |
1141 | | |
1142 | | fn len_encdec_roundtrips(l: usize) -> bool { |
1143 | | let bytes = encode_len(l); |
1144 | | let mut zero = 0; |
1145 | | match decode_length(&bytes[..], &mut zero) { |
1146 | | Err(_) => false, |
1147 | | Ok(l2) => l == l2 |
1148 | | } |
1149 | | } |
1150 | | } |
1151 | | |
1152 | | #[derive(Clone, Debug)] |
1153 | | struct RandomInt { |
1154 | | x: BigInt, |
1155 | | } |
1156 | | |
1157 | | impl Arbitrary for RandomInt { |
1158 | | fn arbitrary(g: &mut Gen) -> RandomInt { |
1159 | | let v = BigInt::from_i64(i64::arbitrary(g)).unwrap(); |
1160 | | RandomInt { x: v } |
1161 | | } |
1162 | | } |
1163 | | |
1164 | | #[allow(type_alias_bounds)] |
1165 | | type ASN1BlockGen = fn(&mut Gen, usize) -> ASN1Block; |
1166 | | |
1167 | | fn arb_boolean(g: &mut Gen, _d: usize) -> ASN1Block { |
1168 | | let v = bool::arbitrary(g); |
1169 | | ASN1Block::Boolean(0, v) |
1170 | | } |
1171 | | |
1172 | | fn arb_integer(g: &mut Gen, _d: usize) -> ASN1Block { |
1173 | | let d = RandomInt::arbitrary(g); |
1174 | | ASN1Block::Integer(0, d.x) |
1175 | | } |
1176 | | |
1177 | | fn arb_bitstr(g: &mut Gen, _d: usize) -> ASN1Block { |
1178 | | let size = u16::arbitrary(g) as usize % 16; |
1179 | | let maxbits = (size as usize) * 8; |
1180 | | let modbits = u8::arbitrary(g) as usize % 8; |
1181 | | let nbits = if modbits > maxbits { |
1182 | | maxbits |
1183 | | } else { |
1184 | | maxbits - modbits |
1185 | | }; |
1186 | | |
1187 | | let mut bytes = Vec::with_capacity(size); |
1188 | | while bytes.len() < size { |
1189 | | bytes.push(u8::arbitrary(g)); |
1190 | | } |
1191 | | |
1192 | | ASN1Block::BitString(0, nbits, bytes) |
1193 | | } |
1194 | | |
1195 | | fn arb_octstr(g: &mut Gen, _d: usize) -> ASN1Block { |
1196 | | let size = usize::arbitrary(g) % 16; |
1197 | | let mut bytes = Vec::with_capacity(size); |
1198 | | |
1199 | | while bytes.len() < size { |
1200 | | bytes.push(u8::arbitrary(g)); |
1201 | | } |
1202 | | |
1203 | | ASN1Block::OctetString(0, bytes) |
1204 | | } |
1205 | | |
1206 | | fn arb_null(_g: &mut Gen, _d: usize) -> ASN1Block { |
1207 | | ASN1Block::Null(0) |
1208 | | } |
1209 | | |
1210 | | impl Arbitrary for OID { |
1211 | | fn arbitrary(g: &mut Gen) -> OID { |
1212 | | let count = usize::arbitrary(g) % 40; |
1213 | | let val1 = u8::arbitrary(g) % 3; |
1214 | | let v2mod = if val1 == 2 { 176 } else { 40 }; |
1215 | | let val2 = u8::arbitrary(g) % v2mod; |
1216 | | let v1 = BigUint::from_u8(val1).unwrap(); |
1217 | | let v2 = BigUint::from_u8(val2).unwrap(); |
1218 | | let mut nums = vec![v1, v2]; |
1219 | | |
1220 | | for _ in 0..count { |
1221 | | let num = RandomUint::arbitrary(g); |
1222 | | nums.push(num.x); |
1223 | | } |
1224 | | |
1225 | | OID(nums) |
1226 | | } |
1227 | | } |
1228 | | |
1229 | | fn arb_objid(g: &mut Gen, _d: usize) -> ASN1Block { |
1230 | | let oid = OID::arbitrary(g); |
1231 | | ASN1Block::ObjectIdentifier(0, oid) |
1232 | | } |
1233 | | |
1234 | | fn arb_seq(g: &mut Gen, d: usize) -> ASN1Block { |
1235 | | let count = usize::arbitrary(g) % 63 + 1; |
1236 | | let mut items = Vec::new(); |
1237 | | |
1238 | | for _ in 0..count { |
1239 | | items.push(limited_arbitrary(g, d - 1)); |
1240 | | } |
1241 | | |
1242 | | ASN1Block::Sequence(0, items) |
1243 | | } |
1244 | | |
1245 | | fn arb_set(g: &mut Gen, d: usize) -> ASN1Block { |
1246 | | let count = usize::arbitrary(g) % 63 + 1; |
1247 | | let mut items = Vec::new(); |
1248 | | |
1249 | | for _ in 0..count { |
1250 | | items.push(limited_arbitrary(g, d - 1)); |
1251 | | } |
1252 | | |
1253 | | ASN1Block::Set(0, items) |
1254 | | } |
1255 | | |
1256 | | fn arb_print(g: &mut Gen, _d: usize) -> ASN1Block { |
1257 | | let count = usize::arbitrary(g) % 384; |
1258 | | let mut items = Vec::new(); |
1259 | | |
1260 | | for _ in 0..count { |
1261 | | let v = g.choose(PRINTABLE_CHARS.as_bytes()); |
1262 | | items.push(*v.unwrap() as char); |
1263 | | } |
1264 | | |
1265 | | ASN1Block::PrintableString(0, String::from_iter(items.iter())) |
1266 | | } |
1267 | | |
1268 | | fn arb_ia5(g: &mut Gen, _d: usize) -> ASN1Block { |
1269 | | let count = usize::arbitrary(g) % 384; |
1270 | | let mut items = Vec::new(); |
1271 | | |
1272 | | for _ in 0..count { |
1273 | | items.push(u8::arbitrary(g) as char); |
1274 | | } |
1275 | | |
1276 | | ASN1Block::IA5String(0, String::from_iter(items.iter())) |
1277 | | } |
1278 | | |
1279 | | fn arb_utf8(g: &mut Gen, _d: usize) -> ASN1Block { |
1280 | | let val = String::arbitrary(g); |
1281 | | ASN1Block::UTF8String(0, val) |
1282 | | } |
1283 | | |
1284 | | fn arb_tele(g: &mut Gen, _d: usize) -> ASN1Block { |
1285 | | let val = String::arbitrary(g); |
1286 | | ASN1Block::TeletexString(0, val) |
1287 | | } |
1288 | | |
1289 | | fn arb_uni(g: &mut Gen, _d: usize) -> ASN1Block { |
1290 | | let val = String::arbitrary(g); |
1291 | | ASN1Block::UniversalString(0, val) |
1292 | | } |
1293 | | |
1294 | | fn arb_bmp(g: &mut Gen, _d: usize) -> ASN1Block { |
1295 | | let val = String::arbitrary(g); |
1296 | | ASN1Block::BMPString(0, val) |
1297 | | } |
1298 | | |
1299 | | fn arb_utc(g: &mut Gen, _d: usize) -> ASN1Block { |
1300 | | let min = Date::from_calendar_date(1950, Month::January, 01) |
1301 | | .unwrap() |
1302 | | .to_julian_day(); |
1303 | | let max = Date::from_calendar_date(2049, Month::December, 31) |
1304 | | .unwrap() |
1305 | | .to_julian_day(); |
1306 | | let date = |
1307 | | Date::from_julian_day(i32::arbitrary(g).rem_euclid(max - min + 1) + min).unwrap(); |
1308 | | |
1309 | | let h = u8::arbitrary(g).rem_euclid(24); |
1310 | | let m = u8::arbitrary(g).rem_euclid(60); |
1311 | | let s = u8::arbitrary(g).rem_euclid(60); |
1312 | | let time = Time::from_hms(h, m, s).unwrap(); |
1313 | | |
1314 | | let t = PrimitiveDateTime::new(date, time); |
1315 | | ASN1Block::UTCTime(0, t) |
1316 | | } |
1317 | | |
1318 | | fn arb_time(g: &mut Gen, _d: usize) -> ASN1Block { |
1319 | | let min = Date::from_calendar_date(0, Month::January, 01) |
1320 | | .unwrap() |
1321 | | .to_julian_day(); |
1322 | | let max = Date::from_calendar_date(9999, Month::December, 31) |
1323 | | .unwrap() |
1324 | | .to_julian_day(); |
1325 | | let date = |
1326 | | Date::from_julian_day(i32::arbitrary(g).rem_euclid(max - min + 1) + min).unwrap(); |
1327 | | |
1328 | | let time = Time::arbitrary(g); |
1329 | | |
1330 | | let t = PrimitiveDateTime::new(date, time); |
1331 | | ASN1Block::GeneralizedTime(0, t) |
1332 | | } |
1333 | | |
1334 | | fn arb_explicit(g: &mut Gen, d: usize) -> ASN1Block { |
1335 | | let mut class = ASN1Class::arbitrary(g); |
1336 | | if class == ASN1Class::Universal { |
1337 | | // Universal is invalid for an explicitly tagged block |
1338 | | class = ASN1Class::ContextSpecific; |
1339 | | } |
1340 | | let tag = RandomUint::arbitrary(g); |
1341 | | let item = limited_arbitrary(g, d - 1); |
1342 | | |
1343 | | ASN1Block::Explicit(class, 0, tag.x, Box::new(item)) |
1344 | | } |
1345 | | |
1346 | | fn arb_unknown(g: &mut Gen, _d: usize) -> ASN1Block { |
1347 | | let class = ASN1Class::arbitrary(g); |
1348 | | let tag = loop { |
1349 | | let potential = RandomUint::arbitrary(g); |
1350 | | match potential.x.to_u8() { |
1351 | | None => break potential, |
1352 | | Some(x) if KNOWN_TAGS.contains(&x) => {} |
1353 | | Some(_) => break potential, |
1354 | | } |
1355 | | }; |
1356 | | let size = usize::arbitrary(g) % 128; |
1357 | | let mut items = Vec::with_capacity(size); |
1358 | | |
1359 | | while items.len() < size { |
1360 | | items.push(u8::arbitrary(g)); |
1361 | | } |
1362 | | |
1363 | | ASN1Block::Unknown(class, false, 0, tag.x, items) |
1364 | | } |
1365 | | |
1366 | | fn limited_arbitrary(g: &mut Gen, d: usize) -> ASN1Block { |
1367 | | let mut possibles: Vec<ASN1BlockGen> = vec![ |
1368 | | arb_boolean, |
1369 | | arb_integer, |
1370 | | arb_bitstr, |
1371 | | arb_octstr, |
1372 | | arb_null, |
1373 | | arb_objid, |
1374 | | arb_utf8, |
1375 | | arb_print, |
1376 | | arb_tele, |
1377 | | arb_uni, |
1378 | | arb_ia5, |
1379 | | arb_utc, |
1380 | | arb_time, |
1381 | | arb_bmp, |
1382 | | arb_unknown, |
1383 | | ]; |
1384 | | |
1385 | | if d > 0 { |
1386 | | possibles.push(arb_seq); |
1387 | | possibles.push(arb_set); |
1388 | | possibles.push(arb_explicit); |
1389 | | } |
1390 | | |
1391 | | match g.choose(&possibles[..]) { |
1392 | | Some(f) => f(g, d), |
1393 | | None => panic!("Couldn't generate arbitrary value."), |
1394 | | } |
1395 | | } |
1396 | | |
1397 | | impl Arbitrary for ASN1Block { |
1398 | | fn arbitrary(g: &mut Gen) -> ASN1Block { |
1399 | | limited_arbitrary(g, 2) |
1400 | | } |
1401 | | } |
1402 | | |
1403 | | quickcheck! { |
1404 | | fn encode_decode_roundtrips(v: ASN1Block) -> bool { |
1405 | | match to_der(&v) { |
1406 | | Err(e) => { |
1407 | | println!("Serialization error: {:?}", e); |
1408 | | false |
1409 | | } |
1410 | | Ok(bytes) => |
1411 | | match from_der(&bytes[..]) { |
1412 | | Err(e) => { |
1413 | | println!("Parse error: {:?}", e); |
1414 | | false |
1415 | | } |
1416 | | Ok(ref rvec) if rvec.len() == 1 => { |
1417 | | let v2 = rvec.get(0).unwrap(); |
1418 | | if &v != v2 { |
1419 | | println!("Original: {:?}", v); |
1420 | | println!("Constructed: {:?}", v2); |
1421 | | } |
1422 | | &v == v2 |
1423 | | } |
1424 | | Ok(_) => { |
1425 | | println!("Too many results returned."); |
1426 | | false |
1427 | | } |
1428 | | } |
1429 | | } |
1430 | | } |
1431 | | } |
1432 | | |
1433 | | fn result_int(v: i16) -> Result<Vec<ASN1Block>, ASN1DecodeErr> { |
1434 | | let val = BigInt::from(v); |
1435 | | Ok(vec![ASN1Block::Integer(0, val)]) |
1436 | | } |
1437 | | |
1438 | | #[test] |
1439 | | fn utc_time_tests() { |
1440 | | // Check for a regression against issue #27, in which this would |
1441 | | // cause a panic. |
1442 | | let input = [ |
1443 | | 55, 13, 13, 133, 13, 13, 50, 13, 13, 133, 13, 13, 50, 13, 133, |
1444 | | ]; |
1445 | | let output = from_der(&input); |
1446 | | assert!(output.is_err()); |
1447 | | } |
1448 | | |
1449 | | #[test] |
1450 | | fn generalized_time_tests() { |
1451 | | check_spec( |
1452 | | &PrimitiveDateTime::new( |
1453 | | Date::from_calendar_date(1992, Month::May, 21).unwrap(), |
1454 | | Time::from_hms(0, 0, 0).unwrap(), |
1455 | | ), |
1456 | | "19920521000000Z".to_string(), |
1457 | | ); |
1458 | | check_spec( |
1459 | | &PrimitiveDateTime::new( |
1460 | | Date::from_calendar_date(1992, Month::June, 22).unwrap(), |
1461 | | Time::from_hms(12, 34, 21).unwrap(), |
1462 | | ), |
1463 | | "19920622123421Z".to_string(), |
1464 | | ); |
1465 | | check_spec( |
1466 | | &PrimitiveDateTime::new( |
1467 | | Date::from_calendar_date(1992, Month::July, 22).unwrap(), |
1468 | | Time::from_hms_milli(13, 21, 00, 300).unwrap(), |
1469 | | ), |
1470 | | "19920722132100.3Z".to_string(), |
1471 | | ); |
1472 | | } |
1473 | | |
1474 | | fn check_spec(d: &PrimitiveDateTime, s: String) { |
1475 | | let b = ASN1Block::GeneralizedTime(0, d.clone()); |
1476 | | match to_der(&b) { |
1477 | | Err(_) => assert_eq!(format!("Broken: {}", d), s), |
1478 | | Ok(ref vec) => { |
1479 | | let mut resvec = vec.clone(); |
1480 | | resvec.remove(0); |
1481 | | resvec.remove(0); |
1482 | | assert_eq!(String::from_utf8(resvec).unwrap(), s); |
1483 | | match from_der_(vec, 0) { |
1484 | | Err(_) => assert_eq!(format!("Broken [reparse]: {}", d), s), |
1485 | | Ok(mut vec) => { |
1486 | | assert_eq!(vec.len(), 1); |
1487 | | match vec.pop() { |
1488 | | None => assert!(false, "The world's gone mad again."), |
1489 | | Some(ASN1Block::GeneralizedTime(_, d2)) => assert_eq!(&d2, d), |
1490 | | Some(_) => assert!(false, "Bad reparse of GeneralizedTime."), |
1491 | | } |
1492 | | } |
1493 | | } |
1494 | | } |
1495 | | } |
1496 | | } |
1497 | | |
1498 | | #[test] |
1499 | | fn base_integer_tests() { |
1500 | | assert_eq!(from_der(&vec![0x02, 0x01, 0x00]), result_int(0)); |
1501 | | assert_eq!(from_der(&vec![0x02, 0x01, 0x7F]), result_int(127)); |
1502 | | assert_eq!(from_der(&vec![0x02, 0x02, 0x00, 0x80]), result_int(128)); |
1503 | | assert_eq!(from_der(&vec![0x02, 0x02, 0x01, 0x00]), result_int(256)); |
1504 | | assert_eq!(from_der(&vec![0x02, 0x01, 0x80]), result_int(-128)); |
1505 | | assert_eq!(from_der(&vec![0x02, 0x02, 0xFF, 0x7F]), result_int(-129)); |
1506 | | } |
1507 | | |
1508 | | fn can_parse(f: &str) -> Result<Vec<ASN1Block>, ASN1DecodeErr> { |
1509 | | let mut fd = File::open(f).unwrap(); |
1510 | | let mut buffer = Vec::new(); |
1511 | | let _amt = fd.read_to_end(&mut buffer); |
1512 | | from_der(&buffer[..]) |
1513 | | } |
1514 | | |
1515 | | #[test] |
1516 | | fn x509_tests() { |
1517 | | can_parse("test/server.bin").unwrap(); |
1518 | | can_parse("test/key.bin").unwrap(); |
1519 | | } |
1520 | | |
1521 | | #[test] |
1522 | | fn encode_base127_zero() { |
1523 | | let zero = BigUint::from(0 as u64); |
1524 | | let encoded = encode_base127(&zero); |
1525 | | let expected: Vec<u8> = vec![0x0]; |
1526 | | assert_eq!(expected, encoded); |
1527 | | } |
1528 | | |
1529 | | #[test] |
1530 | | fn raw_oid_eq() { |
1531 | | // data taken from https://tools.ietf.org/html/rfc4880 |
1532 | | // ( OID as vector of unsigned integers , asn1 encoded block) |
1533 | | |
1534 | | // comparision is not done against the full length, but only for |
1535 | | // the actually encoded OID part (see the expect statement further down) |
1536 | | let md5 = ( |
1537 | | oid!(1, 2, 840, 113549, 2, 5), |
1538 | | vec![ |
1539 | | 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, |
1540 | | 0x05, 0x00, 0x04, 0x10, |
1541 | | ], |
1542 | | ); |
1543 | | |
1544 | | let ripmed160 = ( |
1545 | | oid!(1, 3, 36, 3, 2, 1), |
1546 | | vec![ |
1547 | | 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, |
1548 | | 0x14, |
1549 | | ], |
1550 | | ); |
1551 | | |
1552 | | let sha1 = ( |
1553 | | oid!(1, 3, 14, 3, 2, 26), |
1554 | | vec![ |
1555 | | 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, |
1556 | | 0x14, |
1557 | | ], |
1558 | | ); |
1559 | | |
1560 | | let sha224 = ( |
1561 | | oid!(2, 16, 840, 1, 101, 3, 4, 2, 4), |
1562 | | vec![ |
1563 | | 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, |
1564 | | 0x04, 0x05, 0x00, 0x04, 0x1C, |
1565 | | ], |
1566 | | ); |
1567 | | |
1568 | | let sha256 = ( |
1569 | | oid!(2, 16, 840, 1, 101, 3, 4, 2, 1), |
1570 | | vec![ |
1571 | | 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, |
1572 | | 0x01, 0x05, 0x00, 0x04, 0x20, |
1573 | | ], |
1574 | | ); |
1575 | | |
1576 | | let sha384 = ( |
1577 | | oid!(2, 16, 840, 1, 101, 3, 4, 2, 2), |
1578 | | vec![ |
1579 | | 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, |
1580 | | 0x02, 0x05, 0x00, 0x04, 0x30, |
1581 | | ], |
1582 | | ); |
1583 | | |
1584 | | let sha512 = ( |
1585 | | oid!(2, 16, 840, 1, 101, 3, 4, 2, 3), |
1586 | | vec![ |
1587 | | 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, |
1588 | | 0x03, 0x05, 0x00, 0x04, 0x40, |
1589 | | ], |
1590 | | ); |
1591 | | |
1592 | | let tests: Vec<(OID, Vec<u8>)> = vec![md5, ripmed160, sha1, sha224, sha256, sha384, sha512]; |
1593 | | |
1594 | | for test in tests { |
1595 | | let expected = test.1; |
1596 | | let raw_oid = test.0.as_raw().expect("Failed to convert OID to raw"); |
1597 | | assert_eq!(raw_oid, &expected[6..(expected.len() - 4)]); |
1598 | | } |
1599 | | } |
1600 | | |
1601 | | #[test] |
1602 | | fn vec_oid() { |
1603 | | let vec_u64: Vec<u64> = vec![1, 2, 840, 10045, 4, 3, 2]; |
1604 | | let vec_i64: Vec<i64> = vec![1, 2, 840, 10045, 4, 3, 2]; |
1605 | | let vec_usize: Vec<usize> = vec![1, 2, 840, 10045, 4, 3, 2]; |
1606 | | |
1607 | | let mut o = Vec::new(); |
1608 | | for val in vec_u64.iter() { |
1609 | | o.push(BigUint::from(*val)); |
1610 | | } |
1611 | | |
1612 | | let oid = OID::new(o); |
1613 | | |
1614 | | assert_eq!(Ok(vec_u64), oid.as_vec()); |
1615 | | assert_eq!(Ok(vec_i64), oid.as_vec()); |
1616 | | assert_eq!(Ok(vec_usize), oid.as_vec()); |
1617 | | assert_eq!(Err(ASN1DecodeErr::Overflow), oid.as_vec::<u8>()); |
1618 | | } |
1619 | | } |