asn1/
object_identifier.rs

1use crate::base128;
2use crate::parser::{ParseError, ParseErrorKind, ParseResult};
3use alloc::fmt;
4
5const MAX_OID_LENGTH: usize = 63;
6
7/// Represents an ASN.1 `OBJECT IDENTIFIER`.
8///
9/// `ObjectIdentifier`s are opaque, the only thing may be done with them is
10/// test if they are equal to another `ObjectIdentifier`. The generally
11/// recommended practice for handling them is to create some
12/// `ObjectIdentifier` constants with `asn1::oid!()` and then compare them
13/// with `ObjectIdentifier`s you get from parsing.
14///
15/// `asn1::oid!()` takes a series of arcs, for example: `asn1::oid!(1, 2, 3)`.
16///
17/// rust-asn1 stores `ObjectIdentifier`s in a fixed-size buffer, therefore
18/// they are limited to OID values whose DER encoding fits into that buffer.
19/// This buffer is sufficiently large to fit all known publically known OIDs,
20/// so this should not affect most people.
21#[derive(PartialEq, Eq, Clone, Hash)]
22pub struct ObjectIdentifier {
23    // Store the OID as DER encoded.
24    der_encoded: [u8; MAX_OID_LENGTH],
25    der_encoded_len: u8,
26}
27
28impl ObjectIdentifier {
29    /// Parses an OID from a dotted string, e.g. `"1.2.840.113549"`.
30    pub fn from_string(oid: &str) -> Option<ObjectIdentifier> {
31        let mut parts = oid.split('.');
32
33        let first = parts.next()?.parse::<u128>().ok()?;
34        let second = parts.next()?.parse::<u128>().ok()?;
35        if first > 2 || (first < 2 && second >= 40) {
36            return None;
37        }
38
39        let mut der_data = [0; MAX_OID_LENGTH];
40        let mut der_data_len = 0;
41        der_data_len +=
42            base128::write_base128_int(&mut der_data[der_data_len..], 40 * first + second)?;
43        for part in parts {
44            der_data_len += base128::write_base128_int(
45                &mut der_data[der_data_len..],
46                part.parse::<u128>().ok()?,
47            )?;
48        }
49        Some(ObjectIdentifier {
50            der_encoded: der_data,
51            der_encoded_len: der_data_len as u8,
52        })
53    }
54
55    /// Creates an `ObjectIdentifier` from its DER representation. This does
56    /// not perform any allocations or copies.
57    pub fn from_der(data: &[u8]) -> ParseResult<ObjectIdentifier> {
58        if data.is_empty() {
59            return Err(ParseError::new(ParseErrorKind::InvalidValue));
60        } else if data.len() > MAX_OID_LENGTH {
61            return Err(ParseError::new(ParseErrorKind::OidTooLong));
62        }
63
64        let mut parsed = (0, data);
65        while !parsed.1.is_empty() {
66            // `base128::read_base128_int` can return a `ShortData` error, but
67            // in context here that means `InvalidValue`.
68            parsed = base128::read_base128_int(parsed.1)
69                .map_err(|_| ParseError::new(ParseErrorKind::InvalidValue))?;
70        }
71
72        let mut storage = [0; MAX_OID_LENGTH];
73        storage[..data.len()].copy_from_slice(data);
74
75        Ok(ObjectIdentifier {
76            der_encoded: storage,
77            der_encoded_len: data.len() as u8,
78        })
79    }
80
81    /// Creates an `ObjectIdentifier` from its DER representation. Does not
82    /// check that the DER is valid. Intended only for use from the `oid!()`
83    /// macro. Do not use yourself!
84    #[doc(hidden)]
85    pub const fn from_der_unchecked(data: [u8; MAX_OID_LENGTH], data_len: u8) -> ObjectIdentifier {
86        ObjectIdentifier {
87            der_encoded: data,
88            der_encoded_len: data_len,
89        }
90    }
91
92    pub(crate) fn as_der(&self) -> &[u8] {
93        &self.der_encoded[..self.der_encoded_len as usize]
94    }
95}
96
97struct OidFormatter<'a>(&'a ObjectIdentifier);
98
99impl fmt::Debug for OidFormatter<'_> {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        let mut parsed = (0, self.0.as_der());
102
103        parsed = base128::read_base128_int(parsed.1).unwrap();
104        if parsed.0 < 80 {
105            write!(f, "{}.{}", parsed.0 / 40, parsed.0 % 40)?;
106        } else {
107            write!(f, "2.{}", parsed.0 - 80)?;
108        }
109
110        while !parsed.1.is_empty() {
111            parsed = base128::read_base128_int(parsed.1).unwrap();
112            write!(f, ".{}", parsed.0)?;
113        }
114
115        Ok(())
116    }
117}
118
119impl fmt::Debug for ObjectIdentifier {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        f.debug_struct("ObjectIdentifier")
122            .field("oid", &OidFormatter(self))
123            .finish()
124    }
125}
126
127impl fmt::Display for ObjectIdentifier {
128    /// Converts an `ObjectIdentifier` to a dotted string, e.g.
129    /// "1.2.840.113549".
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        fmt::Debug::fmt(&OidFormatter(self), f)
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::MAX_OID_LENGTH;
138    use crate::{ObjectIdentifier, ParseError, ParseErrorKind};
139    use alloc::format;
140    #[cfg(not(feature = "std"))]
141    use alloc::string::ToString;
142
143    #[test]
144    fn test_object_identifier_from_string() {
145        for val in &[
146            "",
147            "1",
148            "3.10",
149            "1.50",
150            "2.12.a3.4",
151            "a.4",
152            "1.a",
153            ".2.5",
154            "2..5",
155            "2.5.",
156            "1.3.6.1.4.1.1248.1.1.2.1.3.21.69.112.115.111.110.32.83.116.121.108.117.115.32.80.114.111.32.52.57.48.48.123.124412.31.213321.123.110.32.83.116.121.108.117.115.32.80.114.111.32.52.57.48.48.123.124412.31.213321.123",
157        ] {
158            assert_eq!(ObjectIdentifier::from_string(val), None);
159        }
160
161        for val in &[
162            "2.5",
163            "2.5.2",
164            "1.2.840.113549",
165            "1.2.3.4",
166            "1.2.840.133549.1.1.5",
167            "2.100.3",
168            "2.1.750304883",
169            "2.25.223663413560230117710484359924050447509",
170            "2.25.340282366920938463463374607431768211455",
171        ] {
172            assert!(ObjectIdentifier::from_string(val).is_some());
173        }
174    }
175
176    #[test]
177    fn test_from_der() {
178        assert_eq!(ObjectIdentifier::from_der(b"\x06\x40\x2b\x06\x01\x04\x01\x89\x60\x01\x01\x02\x01\x03\x15\x45\x70\x73\x6f\x6e\x20\x53\x74\x79\x6c\x75\x73\x20\x50\x72\x6f\x20\x34\x39\x30\x30\x7b\x87\xcb\x7c\x1f\x8d\x82\x49\x7b\x2b\x06\x01\x04\x01\x89\x60\x01\x01\x02\x01\x03\x15\x45\x70\x73\x6f\x6e\x20"), Err(ParseError::new(ParseErrorKind::OidTooLong)));
179    }
180
181    #[test]
182    fn test_from_der_unchecked() {
183        for (dotted_string, der) in &[("2.5", b"\x55" as &[u8]), ("2.100.3", b"\x81\x34\x03")] {
184            let mut data = [0; MAX_OID_LENGTH];
185            data[..der.len()].copy_from_slice(der);
186            assert_eq!(
187                ObjectIdentifier::from_string(dotted_string).unwrap(),
188                ObjectIdentifier::from_der_unchecked(data, der.len() as u8)
189            );
190        }
191    }
192
193    #[test]
194    fn test_debug() {
195        let oid = ObjectIdentifier::from_string("1.2.3.4").unwrap();
196        assert_eq!(format!("{oid:?}"), "ObjectIdentifier { oid: 1.2.3.4 }");
197    }
198
199    #[test]
200    fn test_to_string() {
201        for val in &[
202            "0.4",
203            "2.5",
204            "2.5.2",
205            "1.2.840.113549",
206            "1.2.3.4",
207            "1.2.840.133549.1.1.5",
208            "2.100.3",
209            "2.1.750304883",
210            "2.25.223663413560230117710484359924050447509",
211            "2.25.340282366920938463463374607431768211455",
212        ] {
213            assert_eq!(
214                &ObjectIdentifier::from_string(val).unwrap().to_string(),
215                val
216            );
217        }
218    }
219}