/rust/registry/src/index.crates.io-6f17d22bba15001f/der-parser-6.0.1/src/oid.rs
Line | Count | Source (jump to first uncovered line) |
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 | | |
90 | 0 | fn encode_relative(ids: &'_ [u64]) -> impl Iterator<Item = u8> + '_ { |
91 | 0 | ids.iter() |
92 | 0 | .map(|id| { |
93 | 0 | let bit_count = 64 - id.leading_zeros(); |
94 | 0 | let octets_needed = ((bit_count + 6) / 7).max(1); |
95 | 0 | (0..octets_needed).map(move |i| { |
96 | 0 | let flag = if i == octets_needed - 1 { 0 } else { 1 << 7 }; |
97 | 0 | ((id >> (7 * (octets_needed - 1 - i))) & 0b111_1111) as u8 | flag |
98 | 0 | }) |
99 | 0 | }) |
100 | 0 | .flatten() |
101 | 0 | } |
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. |
106 | 31.6k | pub const fn new(asn1: Cow<'a, [u8]>) -> Oid { |
107 | 31.6k | Oid { |
108 | 31.6k | asn1, |
109 | 31.6k | relative: false, |
110 | 31.6k | } |
111 | 31.6k | } |
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. |
115 | 7.23k | pub const fn new_relative(asn1: Cow<'a, [u8]>) -> Oid { |
116 | 7.23k | Oid { |
117 | 7.23k | asn1, |
118 | 7.23k | relative: true, |
119 | 7.23k | } |
120 | 7.23k | } |
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)] |
126 | 0 | pub fn from<'b>(s: &'b [u64]) -> Result<Oid<'static>, ParseError> { |
127 | 0 | if s.len() < 2 { |
128 | 0 | if s.len() == 1 && s[0] == 0 { |
129 | 0 | return Ok(Oid { |
130 | 0 | asn1: Cow::Borrowed(&[0]), |
131 | 0 | relative: false, |
132 | 0 | }); |
133 | 0 | } |
134 | 0 | return Err(ParseError::TooShort); |
135 | 0 | } |
136 | 0 | if s[0] >= 7 || s[1] >= 40 { |
137 | 0 | return Err(ParseError::FirstComponentsTooLarge); |
138 | 0 | } |
139 | 0 | let asn1_encoded: Vec<u8> = [(s[0] * 40 + s[1]) as u8] |
140 | 0 | .iter() |
141 | 0 | .map(|&x| x) |
142 | 0 | .chain(encode_relative(&s[2..])) |
143 | 0 | .collect(); |
144 | 0 | Ok(Oid { |
145 | 0 | asn1: Cow::from(asn1_encoded), |
146 | 0 | relative: false, |
147 | 0 | }) |
148 | 0 | } |
149 | | |
150 | | /// Build a relative OID from an array of object identifier components. |
151 | 0 | pub fn from_relative<'b>(s: &'b [u64]) -> Result<Oid<'static>, ParseError> { |
152 | 0 | if s.is_empty() { |
153 | 0 | return Err(ParseError::TooShort); |
154 | 0 | } |
155 | 0 | let asn1_encoded: Vec<u8> = encode_relative(s).collect(); |
156 | 0 | Ok(Oid { |
157 | 0 | asn1: Cow::from(asn1_encoded), |
158 | 0 | relative: true, |
159 | 0 | }) |
160 | 0 | } |
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. |
168 | 0 | pub fn to_owned(&self) -> Oid<'static> { |
169 | 0 | Oid { |
170 | 0 | asn1: Cow::from(self.asn1.to_vec()), |
171 | 0 | relative: self.relative, |
172 | 0 | } |
173 | 0 | } |
174 | | |
175 | | /// Get the encoded oid without the header. |
176 | 0 | pub fn bytes(&self) -> &[u8] { |
177 | 0 | self.asn1.as_ref() |
178 | 0 | } |
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")] |
183 | | 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. |
195 | 6.92k | pub fn to_id_string(&self) -> String { |
196 | 6.92k | if let Some(arcs) = self.iter() { |
197 | 120k | let ints: Vec<String> = arcs.map(|i| i.to_string()).collect(); |
198 | 5.14k | ints.join(".") |
199 | | } else { |
200 | 1.78k | let mut ret = String::with_capacity(self.asn1.len() * 3); |
201 | 104k | for (i, o) in self.asn1.iter().enumerate() { |
202 | 104k | ret.push_str(&format!("{:02x}", o)); |
203 | 104k | if i + 1 != self.asn1.len() { |
204 | 102k | ret.push(' '); |
205 | 102k | } |
206 | | } |
207 | 1.78k | ret |
208 | | } |
209 | 6.92k | } |
210 | | |
211 | | /// Return an iterator over the sub-identifiers (arcs). |
212 | | #[cfg(feature = "bigint")] |
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`. |
226 | 6.92k | pub fn iter( |
227 | 6.92k | &'_ self, |
228 | 6.92k | ) -> Option<impl Iterator<Item = u64> + FusedIterator + ExactSizeIterator + '_> { |
229 | | // Check that every arc fits into u64 |
230 | 6.92k | let bytes = if self.relative { |
231 | 0 | &self.asn1 |
232 | 6.92k | } else if self.asn1.is_empty() { |
233 | 0 | &[] |
234 | | } else { |
235 | 6.92k | &self.asn1[1..] |
236 | | }; |
237 | 6.92k | let max_bits = bytes |
238 | 6.92k | .iter() |
239 | 227k | .fold((0usize, 0usize), |(max, cur), c| { |
240 | 227k | let is_end = (c >> 7) == 0u8; |
241 | 227k | if is_end { |
242 | 175k | (max.max(cur + 7), 0) |
243 | | } else { |
244 | 52.3k | (max, cur + 7) |
245 | | } |
246 | 227k | }) |
247 | 6.92k | .0; |
248 | 6.92k | if max_bits > 64 { |
249 | 1.78k | return None; |
250 | 5.14k | } |
251 | 5.14k | |
252 | 5.14k | Some(SubIdentifierIterator { |
253 | 5.14k | oid: self, |
254 | 5.14k | pos: 0, |
255 | 5.14k | first: false, |
256 | 5.14k | n: core::marker::PhantomData, |
257 | 5.14k | }) |
258 | 6.92k | } |
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 | | |
274 | 125k | fn next(&mut self) -> Option<Self::Item> { |
275 | | use num_traits::identities::Zero; |
276 | | |
277 | 125k | if self.pos == self.oid.asn1.len() { |
278 | 4.78k | return None; |
279 | 120k | } |
280 | 120k | if !self.oid.relative { |
281 | 120k | if !self.first { |
282 | 5.14k | debug_assert!(self.pos == 0); |
283 | 5.14k | self.first = true; |
284 | 5.14k | return Some((self.oid.asn1[0] / 40).into()); |
285 | 115k | } else if self.pos == 0 { |
286 | 5.14k | self.pos += 1; |
287 | 5.14k | if self.oid.asn1[0] == 0 && self.oid.asn1.len() == 1 { |
288 | 355 | return None; |
289 | 4.78k | } |
290 | 4.78k | return Some((self.oid.asn1[0] % 40).into()); |
291 | 110k | } |
292 | 0 | } |
293 | | // decode objet sub-identifier according to the asn.1 standard |
294 | 110k | let mut res = <N as Zero>::zero(); |
295 | 125k | for o in self.oid.asn1[self.pos..].iter() { |
296 | 125k | self.pos += 1; |
297 | 125k | res = (res << 7) + (o & 0b111_1111).into(); |
298 | 125k | let flag = o >> 7; |
299 | 125k | if flag == 0u8 { |
300 | 110k | break; |
301 | 14.9k | } |
302 | | } |
303 | 110k | Some(res) |
304 | 125k | } |
305 | | } |
306 | | |
307 | | impl<'a, N: Repr> FusedIterator for SubIdentifierIterator<'a, N> {} |
308 | | |
309 | | impl<'a, N: Repr> ExactSizeIterator for SubIdentifierIterator<'a, N> { |
310 | 0 | fn len(&self) -> usize { |
311 | 0 | if self.oid.relative { |
312 | 0 | self.oid.asn1.iter().filter(|o| (*o >> 7) == 0u8).count() |
313 | 0 | } else if self.oid.asn1.len() == 0 { |
314 | 0 | 0 |
315 | 0 | } else if self.oid.asn1.len() == 1 { |
316 | 0 | if self.oid.asn1[0] == 0 { |
317 | 0 | 1 |
318 | | } else { |
319 | 0 | 2 |
320 | | } |
321 | | } else { |
322 | 0 | 2 + self.oid.asn1[2..] |
323 | 0 | .iter() |
324 | 0 | .filter(|o| (*o >> 7) == 0u8) |
325 | 0 | .count() |
326 | | } |
327 | 0 | } |
328 | | |
329 | | #[cfg(feature = "exact_size_is_empty")] |
330 | | fn is_empty(&self) -> bool { |
331 | | self.oid.asn1.is_empty() |
332 | | } |
333 | | } |
334 | | |
335 | | impl<'a> fmt::Display for Oid<'a> { |
336 | 6.92k | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
337 | 6.92k | if self.relative { |
338 | 0 | f.write_str("rel. ")?; |
339 | 6.92k | } |
340 | 6.92k | f.write_str(&self.to_id_string()) |
341 | 6.92k | } |
342 | | } |
343 | | |
344 | | impl<'a> fmt::Debug for Oid<'a> { |
345 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
346 | 0 | f.write_str("OID(")?; |
347 | 0 | <Oid as fmt::Display>::fmt(self, f)?; |
348 | 0 | f.write_str(")") |
349 | 0 | } |
350 | | } |
351 | | |
352 | | impl<'a> FromStr for Oid<'a> { |
353 | | type Err = ParseError; |
354 | | |
355 | 0 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
356 | 0 | let v: Result<Vec<_>, _> = s.split('.').map(|c| c.parse::<u64>()).collect(); |
357 | 0 | v.map_err(|_| ParseError::ParseIntError) |
358 | 0 | .and_then(|v| Oid::from(&v)) |
359 | 0 | } |
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] |
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] |
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] |
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] |
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] |
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 | | } |