1use crate::*;
2use alloc::borrow::Cow;
3#[cfg(not(feature = "std"))]
4use alloc::format;
5#[cfg(not(feature = "std"))]
6use alloc::string::{String, ToString};
7#[cfg(not(feature = "std"))]
8use alloc::vec::Vec;
9use core::{
10 convert::TryFrom, fmt, iter::FusedIterator, marker::PhantomData, ops::Shl, str::FromStr,
11};
12use displaydoc::Display;
13use num_traits::Num;
14use thiserror::Error;
15
16#[derive(Clone, Copy, Debug, Display, PartialEq, Eq, Error)]
18pub enum OidParseError {
19 TooShort,
21 FirstComponentsTooLarge,
26 ParseIntError,
28}
29
30#[derive(Hash, PartialEq, Eq, Clone)]
40pub struct Oid<'a> {
41 asn1: Cow<'a, [u8]>,
42 relative: bool,
43}
44
45impl<'a> TryFrom<Any<'a>> for Oid<'a> {
46 type Error = Error;
47
48 fn try_from(any: Any<'a>) -> Result<Self> {
49 TryFrom::try_from(&any)
50 }
51}
52
53impl<'a, 'b> TryFrom<&'b Any<'a>> for Oid<'a> {
54 type Error = Error;
55
56 fn try_from(any: &'b Any<'a>) -> Result<Self> {
57 let asn1 = Cow::Borrowed(any.data);
59 Ok(Oid::new(asn1))
60 }
61}
62
63impl CheckDerConstraints for Oid<'_> {
64 fn check_constraints(any: &Any) -> Result<()> {
65 any.header.assert_primitive()?;
66 any.header.length.assert_definite()?;
67 Ok(())
68 }
69}
70
71impl DerAutoDerive for Oid<'_> {}
72
73impl Tagged for Oid<'_> {
74 const TAG: Tag = Tag::Oid;
75}
76
77#[cfg(feature = "std")]
78impl ToDer for Oid<'_> {
79 fn to_der_len(&self) -> Result<usize> {
80 let header = Header::new(
82 Class::Universal,
83 false,
84 Self::TAG,
85 Length::Definite(self.asn1.len()),
86 );
87 Ok(header.to_der_len()? + self.asn1.len())
88 }
89
90 fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
91 let tag = if self.relative {
92 Tag::RelativeOid
93 } else {
94 Tag::Oid
95 };
96 let header = Header::new(
97 Class::Universal,
98 false,
99 tag,
100 Length::Definite(self.asn1.len()),
101 );
102 header.write_der_header(writer)
103 }
104
105 fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
106 writer.write(&self.asn1).map_err(Into::into)
107 }
108}
109
110fn encode_relative(ids: &'_ [u64]) -> impl Iterator<Item = u8> + '_ {
111 ids.iter().flat_map(|id| {
112 let bit_count = 64 - id.leading_zeros();
113 let octets_needed = ((bit_count + 6) / 7).max(1);
114 (0..octets_needed).map(move |i| {
115 let flag = if i == octets_needed - 1 { 0 } else { 1 << 7 };
116 ((id >> (7 * (octets_needed - 1 - i))) & 0b111_1111) as u8 | flag
117 })
118 })
119}
120
121impl<'a> Oid<'a> {
122 pub const fn new(asn1: Cow<'a, [u8]>) -> Oid<'a> {
125 Oid {
126 asn1,
127 relative: false,
128 }
129 }
130
131 pub const fn new_relative(asn1: Cow<'a, [u8]>) -> Oid<'a> {
134 Oid {
135 asn1,
136 relative: true,
137 }
138 }
139
140 pub fn from(s: &[u64]) -> core::result::Result<Oid<'static>, OidParseError> {
143 if s.len() < 2 {
144 if s.len() == 1 && s[0] == 0 {
145 return Ok(Oid {
146 asn1: Cow::Borrowed(&[0]),
147 relative: false,
148 });
149 }
150 return Err(OidParseError::TooShort);
151 }
152 if s[0] >= 7 || s[1] >= 40 {
153 return Err(OidParseError::FirstComponentsTooLarge);
154 }
155 let asn1_encoded: Vec<u8> = [(s[0] * 40 + s[1]) as u8]
156 .iter()
157 .copied()
158 .chain(encode_relative(&s[2..]))
159 .collect();
160 Ok(Oid {
161 asn1: Cow::from(asn1_encoded),
162 relative: false,
163 })
164 }
165
166 pub fn from_relative(s: &[u64]) -> core::result::Result<Oid<'static>, OidParseError> {
168 if s.is_empty() {
169 return Err(OidParseError::TooShort);
170 }
171 let asn1_encoded: Vec<u8> = encode_relative(s).collect();
172 Ok(Oid {
173 asn1: Cow::from(asn1_encoded),
174 relative: true,
175 })
176 }
177
178 pub fn to_owned(&self) -> Oid<'static> {
185 Oid {
186 asn1: Cow::from(self.asn1.to_vec()),
187 relative: self.relative,
188 }
189 }
190
191 #[inline]
193 pub fn as_bytes(&self) -> &[u8] {
194 self.asn1.as_ref()
195 }
196
197 #[deprecated(since = "0.2.0", note = "Use `as_bytes` instead")]
199 #[inline]
200 pub fn bytes(&self) -> &[u8] {
201 self.as_bytes()
202 }
203
204 pub fn into_cow(self) -> Cow<'a, [u8]> {
206 self.asn1
207 }
208
209 #[cfg(feature = "bigint")]
212 pub fn to_id_string(&self) -> String {
213 let ints: Vec<String> = self.iter_bigint().map(|i| i.to_string()).collect();
214 ints.join(".")
215 }
216
217 #[cfg(not(feature = "bigint"))]
218 pub fn to_id_string(&self) -> String {
225 if let Some(arcs) = self.iter() {
226 let ints: Vec<String> = arcs.map(|i| i.to_string()).collect();
227 ints.join(".")
228 } else {
229 let mut ret = String::with_capacity(self.asn1.len() * 3);
230 for (i, o) in self.asn1.iter().enumerate() {
231 ret.push_str(&format!("{:02x}", o));
232 if i + 1 != self.asn1.len() {
233 ret.push(' ');
234 }
235 }
236 ret
237 }
238 }
239
240 #[cfg(feature = "bigint")]
242 pub fn iter_bigint(&'_ self) -> impl FusedIterator<Item = BigUint> + ExactSizeIterator + '_ {
243 SubIdentifierIterator {
244 oid: self,
245 pos: 0,
246 first: false,
247 n: PhantomData,
248 }
249 }
250
251 pub fn iter(&'_ self) -> Option<impl FusedIterator<Item = u64> + ExactSizeIterator + '_> {
254 let bytes = if self.relative {
256 &self.asn1
257 } else if self.asn1.is_empty() {
258 &[]
259 } else {
260 &self.asn1[1..]
261 };
262 let max_bits = bytes
263 .iter()
264 .fold((0usize, 0usize), |(max, cur), c| {
265 let is_end = (c >> 7) == 0u8;
266 if is_end {
267 (max.max(cur + 7), 0)
268 } else {
269 (max, cur + 7)
270 }
271 })
272 .0;
273 if max_bits > 64 {
274 return None;
275 }
276
277 Some(SubIdentifierIterator {
278 oid: self,
279 pos: 0,
280 first: false,
281 n: PhantomData,
282 })
283 }
284
285 pub fn from_ber_relative(bytes: &'a [u8]) -> ParseResult<'a, Self> {
286 let (rem, any) = Any::from_ber(bytes)?;
287 any.header.assert_primitive()?;
288 any.header.assert_tag(Tag::RelativeOid)?;
289 let asn1 = Cow::Borrowed(any.data);
290 Ok((rem, Oid::new_relative(asn1)))
291 }
292
293 pub fn from_der_relative(bytes: &'a [u8]) -> ParseResult<'a, Self> {
294 let (rem, any) = Any::from_der(bytes)?;
295 any.header.assert_tag(Tag::RelativeOid)?;
296 Self::check_constraints(&any)?;
297 let asn1 = Cow::Borrowed(any.data);
298 Ok((rem, Oid::new_relative(asn1)))
299 }
300
301 pub fn starts_with(&self, needle: &Oid) -> bool {
303 self.asn1.len() >= needle.asn1.len() && self.asn1.starts_with(needle.as_bytes())
304 }
305}
306
307trait Repr: Num + Shl<usize, Output = Self> + From<u8> {}
308impl<N> Repr for N where N: Num + Shl<usize, Output = N> + From<u8> {}
309
310struct SubIdentifierIterator<'a, N: Repr> {
311 oid: &'a Oid<'a>,
312 pos: usize,
313 first: bool,
314 n: PhantomData<&'a N>,
315}
316
317impl<N: Repr> Iterator for SubIdentifierIterator<'_, N> {
318 type Item = N;
319
320 fn next(&mut self) -> Option<Self::Item> {
321 use num_traits::identities::Zero;
322
323 if self.pos == self.oid.asn1.len() {
324 return None;
325 }
326 if !self.oid.relative {
327 if !self.first {
328 debug_assert!(self.pos == 0);
329 self.first = true;
330 return Some((self.oid.asn1[0] / 40).into());
331 } else if self.pos == 0 {
332 self.pos += 1;
333 if self.oid.asn1[0] == 0 && self.oid.asn1.len() == 1 {
334 return None;
335 }
336 return Some((self.oid.asn1[0] % 40).into());
337 }
338 }
339 let mut res = <N as Zero>::zero();
341 for o in self.oid.asn1[self.pos..].iter() {
342 self.pos += 1;
343 res = (res << 7) + (o & 0b111_1111).into();
344 let flag = o >> 7;
345 if flag == 0u8 {
346 break;
347 }
348 }
349 Some(res)
350 }
351}
352
353impl<N: Repr> FusedIterator for SubIdentifierIterator<'_, N> {}
354
355impl<N: Repr> ExactSizeIterator for SubIdentifierIterator<'_, N> {
356 fn len(&self) -> usize {
357 if self.oid.relative {
358 self.oid.asn1.iter().filter(|o| (*o >> 7) == 0u8).count()
359 } else if self.oid.asn1.is_empty() {
360 0
361 } else if self.oid.asn1.len() == 1 {
362 if self.oid.asn1[0] == 0 {
363 1
364 } else {
365 2
366 }
367 } else {
368 2 + self.oid.asn1[2..]
369 .iter()
370 .filter(|o| (*o >> 7) == 0u8)
371 .count()
372 }
373 }
374}
375
376impl fmt::Display for Oid<'_> {
377 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
378 if self.relative {
379 f.write_str("rel. ")?;
380 }
381 f.write_str(&self.to_id_string())
382 }
383}
384
385impl fmt::Debug for Oid<'_> {
386 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
387 f.write_str("OID(")?;
388 <Oid as fmt::Display>::fmt(self, f)?;
389 f.write_str(")")
390 }
391}
392
393impl FromStr for Oid<'_> {
394 type Err = OidParseError;
395
396 fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
397 let v: core::result::Result<Vec<_>, _> = s.split('.').map(|c| c.parse::<u64>()).collect();
398 v.map_err(|_| OidParseError::ParseIntError)
399 .and_then(|v| Oid::from(&v))
400 }
401}
402
403#[macro_export]
448macro_rules! oid {
449 (raw $( $item:literal ).*) => {
450 $crate::exports::asn1_rs_impl::encode_oid!( $( $item ).* )
451 };
452 (raw $items:expr) => {
453 $crate::exports::asn1_rs_impl::encode_oid!($items)
454 };
455 (rel $($item:literal ).*) => {
456 $crate::Oid::new_relative($crate::exports::borrow::Cow::Borrowed(
457 &$crate::exports::asn1_rs_impl::encode_oid!(rel $( $item ).*),
458 ))
459 };
460 ($($item:literal ).*) => {
461 $crate::Oid::new($crate::exports::borrow::Cow::Borrowed(
462 &$crate::oid!(raw $( $item ).*),
463 ))
464 };
465}
466
467#[cfg(all(test, feature = "std"))]
468mod tests {
469 use crate::{FromDer, Oid, ToDer};
470 use hex_literal::hex;
471
472 #[test]
473 fn declare_oid() {
474 let oid = super::oid! {1.2.840.113549.1};
475 assert_eq!(oid.to_string(), "1.2.840.113549.1");
476 }
477
478 const OID_RSA_ENCRYPTION: &[u8] = &oid! {raw 1.2.840.113549.1.1.1};
479 const OID_EC_PUBLIC_KEY: &[u8] = &oid! {raw 1.2.840.10045.2.1};
480 #[allow(clippy::match_like_matches_macro)]
481 fn compare_oid(oid: &Oid) -> bool {
482 match oid.as_bytes() {
483 OID_RSA_ENCRYPTION => true,
484 OID_EC_PUBLIC_KEY => true,
485 _ => false,
486 }
487 }
488
489 #[test]
490 fn test_compare_oid() {
491 let oid = Oid::from(&[1, 2, 840, 113_549, 1, 1, 1]).unwrap();
492 assert_eq!(oid, oid! {1.2.840.113549.1.1.1});
493 let oid = Oid::from(&[1, 2, 840, 113_549, 1, 1, 1]).unwrap();
494 assert!(compare_oid(&oid));
495 }
496
497 #[test]
498 fn oid_to_der() {
499 let oid = super::oid! {1.2.840.113549.1};
500 assert_eq!(oid.to_der_len(), Ok(9));
501 let v = oid.to_der_vec().expect("could not serialize");
502 assert_eq!(&v, &hex! {"06 07 2a 86 48 86 f7 0d 01"});
503 let (_, oid2) = Oid::from_der(&v).expect("could not re-parse");
504 assert_eq!(&oid, &oid2);
505 }
506
507 #[test]
508 fn oid_starts_with() {
509 const OID_RSA_ENCRYPTION: Oid = oid! {1.2.840.113549.1.1.1};
510 const OID_EC_PUBLIC_KEY: Oid = oid! {1.2.840.10045.2.1};
511 let oid = super::oid! {1.2.840.113549.1};
512 assert!(OID_RSA_ENCRYPTION.starts_with(&oid));
513 assert!(!OID_EC_PUBLIC_KEY.starts_with(&oid));
514 }
515
516 #[test]
517 fn oid_macro_parameters() {
518 macro_rules! foo {
520 ($a:literal $b:literal $c:literal) => {
521 super::oid!($a.$b.$c)
522 };
523 }
524
525 let oid = foo!(1 2 3);
526 assert_eq!(oid, oid! {1.2.3});
527 }
528}