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