1 //! Object ID (OID) representations.
2 //!
3 //! The parser does not copy oids when parsing. The [Oid struct](struct.Oid.html)
4 //! only has a reference to the DER encoded form of the oid.
5 //!
6 //! # The `der_parser::oid!` macro
7 //!
8 //! Since the DER encoded oids are not very readable we provide a
9 //! procedural macro `oid!`. The macro can be used the following ways:
10 //!
11 //! - `oid!(1.4.42.23)`: Create a const expression for the corresponding `Oid<'static>`
12 //! - `oid!(rel 42.23)`: Create a const expression for the corresponding relative `Oid<'static>`
13 //! - `oid!(raw 1.4.42.23)`/`oid!(raw rel 42.23)`: Obtain the DER encoded form as a byte array.
14 //!
15 //! # Comparing oids
16 //!
17 //! Comparing a parsed oid to a static oid is probably the most common
18 //! thing done with oids in your code. The `oid!` macro can be used in expression positions for
19 //! this purpose. For example
20 //! ```
21 //! use der_parser::{oid, oid::Oid};
22 //!
23 //! # let some_oid: Oid<'static> = oid!(1.2.456);
24 //! const SOME_STATIC_OID: Oid<'static> = oid!(1.2.456);
25 //! assert_eq!(some_oid, SOME_STATIC_OID)
26 //! ```
27 //! To get a relative Oid use `oid!(rel 1.2)`.
28 //!
29 //! Because of limitations for procedural macros ([rust issue](https://github.com/rust-lang/rust/issues/54727))
30 //! and constants used in patterns ([rust issue](https://github.com/rust-lang/rust/issues/31434))
31 //! the `oid` macro can not directly be used in patterns, also not through constants.
32 //! You can do this, though:
33 //! ```
34 //! # use der_parser::{oid, oid::Oid};
35 //! # let some_oid: Oid<'static> = oid!(1.2.456);
36 //! const SOME_OID: Oid<'static> = oid!(1.2.456);
37 //! if some_oid == SOME_OID || some_oid == oid!(1.2.456) {
38 //! println!("match");
39 //! }
40 //!
41 //! // Alternatively, compare the DER encoded form directly:
42 //! const SOME_OID_RAW: &[u8] = &oid!(raw 1.2.456);
43 //! match some_oid.bytes() {
44 //! SOME_OID_RAW => println!("match"),
45 //! _ => panic!("no match"),
46 //! }
47 //! ```
48 //! *Attention*, be aware that the latter version might not handle the case of a relative oid correctly. An
49 //! extra check might be necessary.
50 use alloc::borrow::Cow;
51 use alloc::fmt;
52 use alloc::str::FromStr;
53 use alloc::string::{String, ToString};
54 use alloc::vec::Vec;
55 use core::convert::From;
56 use core::iter::{ExactSizeIterator, FusedIterator, Iterator};
57 use core::ops::Shl;
58
59 #[cfg(feature = "bigint")]
60 use num_bigint::BigUint;
61 use num_traits::Num;
62
63 #[cfg(not(feature = "std"))]
64 use alloc::format;
65
66 #[derive(Debug)]
67 pub enum ParseError {
68 TooShort,
69 /// Signalizes that the first or second component is too large.
70 /// The first must be within the range 0 to 6 (inclusive).
71 /// The second component must be less than 40.
72 FirstComponentsTooLarge,
73 ParseIntError,
74 }
75
76 /// Object ID (OID) representation which can be relative or non-relative.
77 /// An example for an oid in string representation is "1.2.840.113549.1.1.5".
78 ///
79 /// For non-relative oids restrictions apply to the first two components.
80 ///
81 /// This library contains a procedural macro `oid` which can be used to
82 /// create oids. For example `oid!(1.2.44.233)` or `oid!(rel 44.233)`
83 /// for relative oids. See the [module documentation](index.html) for more information.
84 #[derive(Hash, PartialEq, Eq, Clone)]
85 pub struct Oid<'a> {
86 asn1: Cow<'a, [u8]>,
87 pub relative: bool,
88 }
89
encode_relative(ids: &'_ [u64]) -> impl Iterator<Item = u8> + '_90 fn encode_relative(ids: &'_ [u64]) -> impl Iterator<Item = u8> + '_ {
91 ids.iter()
92 .map(|id| {
93 let bit_count = 64 - id.leading_zeros();
94 let octets_needed = ((bit_count + 6) / 7).max(1);
95 (0..octets_needed).map(move |i| {
96 let flag = if i == octets_needed - 1 { 0 } else { 1 << 7 };
97 ((id >> (7 * (octets_needed - 1 - i))) & 0b111_1111) as u8 | flag
98 })
99 })
100 .flatten()
101 }
102
103 impl<'a> Oid<'a> {
104 /// Create an OID from the ASN.1 DER encoded form. See the [module documentation](index.html)
105 /// for other ways to create oids.
new(asn1: Cow<'a, [u8]>) -> Oid106 pub const fn new(asn1: Cow<'a, [u8]>) -> Oid {
107 Oid {
108 asn1,
109 relative: false,
110 }
111 }
112
113 /// Create a relative OID from the ASN.1 DER encoded form. See the [module documentation](index.html)
114 /// for other ways to create relative oids.
new_relative(asn1: Cow<'a, [u8]>) -> Oid115 pub const fn new_relative(asn1: Cow<'a, [u8]>) -> Oid {
116 Oid {
117 asn1,
118 relative: true,
119 }
120 }
121
122 /// Build an OID from an array of object identifier components.
123 /// This method allocates memory on the heap.
124 // we do not use .copied() for compatibility with 1.34
125 #[allow(clippy::map_clone)]
from<'b>(s: &'b [u64]) -> Result<Oid<'static>, ParseError>126 pub fn from<'b>(s: &'b [u64]) -> Result<Oid<'static>, ParseError> {
127 if s.len() < 2 {
128 if s.len() == 1 && s[0] == 0 {
129 return Ok(Oid {
130 asn1: Cow::Borrowed(&[0]),
131 relative: false,
132 });
133 }
134 return Err(ParseError::TooShort);
135 }
136 if s[0] >= 7 || s[1] >= 40 {
137 return Err(ParseError::FirstComponentsTooLarge);
138 }
139 let asn1_encoded: Vec<u8> = [(s[0] * 40 + s[1]) as u8]
140 .iter()
141 .map(|&x| x)
142 .chain(encode_relative(&s[2..]))
143 .collect();
144 Ok(Oid {
145 asn1: Cow::from(asn1_encoded),
146 relative: false,
147 })
148 }
149
150 /// Build a relative OID from an array of object identifier components.
from_relative<'b>(s: &'b [u64]) -> Result<Oid<'static>, ParseError>151 pub fn from_relative<'b>(s: &'b [u64]) -> Result<Oid<'static>, ParseError> {
152 if s.is_empty() {
153 return Err(ParseError::TooShort);
154 }
155 let asn1_encoded: Vec<u8> = encode_relative(s).collect();
156 Ok(Oid {
157 asn1: Cow::from(asn1_encoded),
158 relative: true,
159 })
160 }
161
162 /// Create a deep copy of the oid.
163 ///
164 /// This method allocates data on the heap. The returned oid
165 /// can be used without keeping the ASN.1 representation around.
166 ///
167 /// Cloning the returned oid does again allocate data.
to_owned(&self) -> Oid<'static>168 pub fn to_owned(&self) -> Oid<'static> {
169 Oid {
170 asn1: Cow::from(self.asn1.to_vec()),
171 relative: self.relative,
172 }
173 }
174
175 /// Get the encoded oid without the header.
bytes(&self) -> &[u8]176 pub fn bytes(&self) -> &[u8] {
177 self.asn1.as_ref()
178 }
179
180 /// Convert the OID to a string representation.
181 /// The string contains the IDs separated by dots, for ex: "1.2.840.113549.1.1.5"
182 #[cfg(feature = "bigint")]
to_id_string(&self) -> String183 pub fn to_id_string(&self) -> String {
184 let ints: Vec<String> = self.iter_bigint().map(|i| i.to_string()).collect();
185 ints.join(".")
186 }
187
188 #[cfg(not(feature = "bigint"))]
189 /// Convert the OID to a string representation.
190 ///
191 /// If every arc fits into a u64 a string like "1.2.840.113549.1.1.5"
192 /// is returned, otherwise a hex representation.
193 ///
194 /// See also the "bigint" feature of this crate.
to_id_string(&self) -> String195 pub fn to_id_string(&self) -> String {
196 if let Some(arcs) = self.iter() {
197 let ints: Vec<String> = arcs.map(|i| i.to_string()).collect();
198 ints.join(".")
199 } else {
200 let mut ret = String::with_capacity(self.asn1.len() * 3);
201 for (i, o) in self.asn1.iter().enumerate() {
202 ret.push_str(&format!("{:02x}", o));
203 if i + 1 != self.asn1.len() {
204 ret.push(' ');
205 }
206 }
207 ret
208 }
209 }
210
211 /// Return an iterator over the sub-identifiers (arcs).
212 #[cfg(feature = "bigint")]
iter_bigint( &'_ self, ) -> impl Iterator<Item = BigUint> + FusedIterator + ExactSizeIterator + '_213 pub fn iter_bigint(
214 &'_ self,
215 ) -> impl Iterator<Item = BigUint> + FusedIterator + ExactSizeIterator + '_ {
216 SubIdentifierIterator {
217 oid: self,
218 pos: 0,
219 first: false,
220 n: core::marker::PhantomData,
221 }
222 }
223
224 /// Return an iterator over the sub-identifiers (arcs).
225 /// Returns `None` if at least one arc does not fit into `u64`.
iter( &'_ self, ) -> Option<impl Iterator<Item = u64> + FusedIterator + ExactSizeIterator + '_>226 pub fn iter(
227 &'_ self,
228 ) -> Option<impl Iterator<Item = u64> + FusedIterator + ExactSizeIterator + '_> {
229 // Check that every arc fits into u64
230 let bytes = if self.relative {
231 &self.asn1
232 } else if self.asn1.is_empty() {
233 &[]
234 } else {
235 &self.asn1[1..]
236 };
237 let max_bits = bytes
238 .iter()
239 .fold((0usize, 0usize), |(max, cur), c| {
240 let is_end = (c >> 7) == 0u8;
241 if is_end {
242 (max.max(cur + 7), 0)
243 } else {
244 (max, cur + 7)
245 }
246 })
247 .0;
248 if max_bits > 64 {
249 return None;
250 }
251
252 Some(SubIdentifierIterator {
253 oid: self,
254 pos: 0,
255 first: false,
256 n: core::marker::PhantomData,
257 })
258 }
259 }
260
261 trait Repr: Num + Shl<usize, Output = Self> + From<u8> {}
262 impl<N> Repr for N where N: Num + Shl<usize, Output = N> + From<u8> {}
263
264 struct SubIdentifierIterator<'a, N: Repr> {
265 oid: &'a Oid<'a>,
266 pos: usize,
267 first: bool,
268 n: core::marker::PhantomData<&'a N>,
269 }
270
271 impl<'a, N: Repr> Iterator for SubIdentifierIterator<'a, N> {
272 type Item = N;
273
next(&mut self) -> Option<Self::Item>274 fn next(&mut self) -> Option<Self::Item> {
275 use num_traits::identities::Zero;
276
277 if self.pos == self.oid.asn1.len() {
278 return None;
279 }
280 if !self.oid.relative {
281 if !self.first {
282 debug_assert!(self.pos == 0);
283 self.first = true;
284 return Some((self.oid.asn1[0] / 40).into());
285 } else if self.pos == 0 {
286 self.pos += 1;
287 if self.oid.asn1[0] == 0 && self.oid.asn1.len() == 1 {
288 return None;
289 }
290 return Some((self.oid.asn1[0] % 40).into());
291 }
292 }
293 // decode objet sub-identifier according to the asn.1 standard
294 let mut res = <N as Zero>::zero();
295 for o in self.oid.asn1[self.pos..].iter() {
296 self.pos += 1;
297 res = (res << 7) + (o & 0b111_1111).into();
298 let flag = o >> 7;
299 if flag == 0u8 {
300 break;
301 }
302 }
303 Some(res)
304 }
305 }
306
307 impl<'a, N: Repr> FusedIterator for SubIdentifierIterator<'a, N> {}
308
309 impl<'a, N: Repr> ExactSizeIterator for SubIdentifierIterator<'a, N> {
len(&self) -> usize310 fn len(&self) -> usize {
311 if self.oid.relative {
312 self.oid.asn1.iter().filter(|o| (*o >> 7) == 0u8).count()
313 } else if self.oid.asn1.len() == 0 {
314 0
315 } else if self.oid.asn1.len() == 1 {
316 if self.oid.asn1[0] == 0 {
317 1
318 } else {
319 2
320 }
321 } else {
322 2 + self.oid.asn1[2..]
323 .iter()
324 .filter(|o| (*o >> 7) == 0u8)
325 .count()
326 }
327 }
328
329 #[cfg(feature = "exact_size_is_empty")]
is_empty(&self) -> bool330 fn is_empty(&self) -> bool {
331 self.oid.asn1.is_empty()
332 }
333 }
334
335 impl<'a> fmt::Display for Oid<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result336 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
337 if self.relative {
338 f.write_str("rel. ")?;
339 }
340 f.write_str(&self.to_id_string())
341 }
342 }
343
344 impl<'a> fmt::Debug for Oid<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result345 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
346 f.write_str("OID(")?;
347 <Oid as fmt::Display>::fmt(self, f)?;
348 f.write_str(")")
349 }
350 }
351
352 impl<'a> FromStr for Oid<'a> {
353 type Err = ParseError;
354
from_str(s: &str) -> Result<Self, Self::Err>355 fn from_str(s: &str) -> Result<Self, Self::Err> {
356 let v: Result<Vec<_>, _> = s.split('.').map(|c| c.parse::<u64>()).collect();
357 v.map_err(|_| ParseError::ParseIntError)
358 .and_then(|v| Oid::from(&v))
359 }
360 }
361
362 #[cfg(test)]
363 mod tests {
364 use crate::oid::Oid;
365 use std::borrow::Cow;
366 use std::borrow::ToOwned;
367 use std::str::FromStr;
368
369 #[test]
test_oid_fmt()370 fn test_oid_fmt() {
371 let oid = Oid::from(&[1, 2, 840, 113_549, 1, 1, 5]).unwrap();
372 assert_eq!(format!("{}", oid), "1.2.840.113549.1.1.5".to_owned());
373 assert_eq!(format!("{:?}", oid), "OID(1.2.840.113549.1.1.5)".to_owned());
374
375 let oid = Oid::from_relative(&[840, 113_549, 1, 1, 5]).unwrap();
376 let byte_ref = [0x86, 0x48, 0x86, 0xf7, 0x0d, 1, 1, 5];
377 assert_eq!(byte_ref.as_ref(), oid.asn1.as_ref());
378 assert_eq!(format!("{}", oid), "rel. 840.113549.1.1.5".to_owned());
379 assert_eq!(
380 format!("{:?}", oid),
381 "OID(rel. 840.113549.1.1.5)".to_owned()
382 );
383 }
384
385 #[test]
test_iter_len()386 fn test_iter_len() {
387 #[cfg(feature = "bigint")]
388 {
389 assert_eq!(Oid::new(Cow::Borrowed(&[])).iter_bigint().len(), 0);
390 assert_eq!(Oid::from(&[0]).unwrap().iter_bigint().len(), 1);
391 assert_eq!(Oid::from(&[1, 2]).unwrap().iter_bigint().len(), 2);
392 assert_eq!(
393 Oid::from(&[1, 29, 459, 342]).unwrap().iter_bigint().len(),
394 4
395 );
396 assert_eq!(
397 Oid::from_relative(&[459, 342]).unwrap().iter_bigint().len(),
398 2
399 );
400 }
401 {
402 assert_eq!(Oid::new(Cow::Borrowed(&[])).iter().unwrap().len(), 0);
403 assert_eq!(Oid::from(&[0]).unwrap().iter().unwrap().len(), 1);
404 assert_eq!(Oid::from(&[1, 2]).unwrap().iter().unwrap().len(), 2);
405 assert_eq!(
406 Oid::from(&[1, 29, 459, 342]).unwrap().iter().unwrap().len(),
407 4
408 );
409 assert_eq!(
410 Oid::from_relative(&[459, 342])
411 .unwrap()
412 .iter()
413 .unwrap()
414 .len(),
415 2
416 );
417 }
418 }
419
420 #[test]
test_oid_from_str()421 fn test_oid_from_str() {
422 let oid_ref = Oid::from(&[1, 2, 840, 113_549, 1, 1, 5]).unwrap();
423 let byte_ref = [42, 0x86, 0x48, 0x86, 0xf7, 0x0d, 1, 1, 5];
424 let oid = Oid::from_str("1.2.840.113549.1.1.5").unwrap();
425 assert_eq!(byte_ref.as_ref(), oid.asn1.as_ref());
426 assert_eq!(oid_ref, oid);
427 }
428
429 /// This test case will test an OID beginning with two zero
430 /// subidentifiers (literally: "itu-t recommendation"), as
431 /// used for example in the TCAP (Q.773) specification.
432
433 #[test]
test_itu_t_rec_oid()434 fn test_itu_t_rec_oid() {
435 let oid = Oid::from(&[0, 0, 17, 773, 1, 1, 1]).unwrap();
436 assert_eq!(format!("{}", oid), "0.0.17.773.1.1.1".to_owned());
437 assert_eq!(format!("{:?}", oid), "OID(0.0.17.773.1.1.1)".to_owned());
438 }
439
440 #[test]
test_zero_oid()441 fn test_zero_oid() {
442 #[cfg(feature = "bigint")]
443 {
444 use num_bigint::BigUint;
445 use num_traits::FromPrimitive;
446 use std::vec::Vec;
447
448 let oid_raw = Oid::new(Cow::Borrowed(&[0]));
449 let ids: Vec<BigUint> = oid_raw.iter_bigint().collect();
450 assert_eq!(vec![BigUint::from_u8(0).unwrap()], ids);
451 assert_eq!(oid_raw.iter_bigint().len(), 1);
452 }
453 {
454 use std::vec::Vec;
455 let oid_raw = Oid::new(Cow::Borrowed(&[0]));
456 let ids: Vec<u64> = oid_raw.iter().unwrap().collect();
457 assert_eq!(vec![0], ids);
458 assert_eq!(oid_raw.iter().unwrap().len(), 1);
459 }
460 let oid_from = Oid::from(&[0]).unwrap();
461 assert_eq!(oid_from.asn1.as_ref(), &[0]);
462 }
463 }
464