const_oid/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
6    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
7    html_root_url = "https://docs.rs/const-oid/0.7.1"
8)]
9#![forbid(unsafe_code, clippy::unwrap_used)]
10#![warn(missing_docs, rust_2018_idioms)]
11
12#[cfg(feature = "std")]
13extern crate std;
14
15#[macro_use]
16mod macros;
17
18mod arcs;
19mod encoder;
20mod error;
21mod parser;
22
23pub use crate::{
24    arcs::{Arc, Arcs},
25    error::{Error, Result},
26};
27
28use crate::arcs::{RootArcs, ARC_MAX_BYTES, ARC_MAX_LAST_OCTET};
29use core::{fmt, str::FromStr};
30
31/// Object identifier (OID).
32///
33/// OIDs are hierarchical structures consisting of "arcs", i.e. integer
34/// identifiers.
35///
36/// # Validity
37///
38/// In order for an OID to be considered valid by this library, it must meet
39/// the following criteria:
40///
41/// - The OID MUST have at least 3 arcs
42/// - The first arc MUST be within the range 0-2
43/// - The second arc MUST be within the range 0-39
44/// - The BER/DER encoding of the OID MUST be shorter than
45///   [`ObjectIdentifier::MAX_SIZE`]
46#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
47pub struct ObjectIdentifier {
48    /// Length in bytes
49    length: u8,
50
51    /// Array containing BER/DER-serialized bytes (no header)
52    bytes: [u8; Self::MAX_SIZE],
53}
54
55#[allow(clippy::len_without_is_empty)]
56impl ObjectIdentifier {
57    /// Maximum size of a BER/DER-encoded OID in bytes.
58    pub const MAX_SIZE: usize = 39; // 32-bytes total w\ 1-byte length
59
60    /// Parse an [`ObjectIdentifier`] from the dot-delimited string form, e.g.:
61    ///
62    /// ```
63    /// use const_oid::ObjectIdentifier;
64    ///
65    /// pub const MY_OID: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.1.1.1");
66    /// ```
67    ///
68    /// # Panics
69    ///
70    /// This method panics in the event the OID is malformed according to the
71    /// "Validity" rules given in the toplevel documentation for this type.
72    ///
73    /// For that reason this method is *ONLY* recommended for use in constants
74    /// (where it will generate a compiler error instead).
75    ///
76    /// To parse an OID from a `&str` slice fallibly and without panicking,
77    /// use the [`FromStr`][1] impl instead (or via `str`'s [`parse`][2]
78    /// method).
79    ///
80    /// [1]: ./struct.ObjectIdentifier.html#impl-FromStr
81    /// [2]: https://doc.rust-lang.org/nightly/std/primitive.str.html#method.parse
82    pub const fn new(s: &str) -> Self {
83        parser::Parser::parse(s).finish()
84    }
85
86    /// Parse an OID from a slice of [`Arc`] values (i.e. integers).
87    pub fn from_arcs(arcs: &[Arc]) -> Result<Self> {
88        let mut bytes = [0u8; Self::MAX_SIZE];
89
90        bytes[0] = match *arcs {
91            [first, second, _, ..] => RootArcs::new(first, second)?.into(),
92            _ => return Err(Error),
93        };
94
95        let mut offset = 1;
96
97        for &arc in &arcs[2..] {
98            offset += encoder::write_base128(&mut bytes[offset..], arc)?;
99        }
100
101        Ok(Self {
102            bytes,
103            length: offset as u8,
104        })
105    }
106
107    /// Parse an OID from from its BER/DER encoding.
108    pub fn from_bytes(ber_bytes: &[u8]) -> Result<Self> {
109        let len = ber_bytes.len();
110
111        if !(2..=Self::MAX_SIZE).contains(&len) {
112            return Err(Error);
113        }
114
115        // Validate root arcs are in range
116        ber_bytes
117            .get(0)
118            .cloned()
119            .ok_or(Error)
120            .and_then(RootArcs::try_from)?;
121
122        // Validate lower arcs are well-formed
123        let mut arc_offset = 1;
124        let mut arc_bytes = 0;
125
126        // TODO(tarcieri): consolidate this with `Arcs::next`?
127        while arc_offset < len {
128            match ber_bytes.get(arc_offset + arc_bytes).cloned() {
129                Some(byte) => {
130                    if (arc_bytes == ARC_MAX_BYTES) && (byte & ARC_MAX_LAST_OCTET != 0) {
131                        // Overflowed `Arc` (u32)
132                        return Err(Error);
133                    }
134
135                    arc_bytes += 1;
136
137                    if byte & 0b10000000 == 0 {
138                        arc_offset += arc_bytes;
139                        arc_bytes = 0;
140                    }
141                }
142                None => return Err(Error), // truncated OID
143            }
144        }
145
146        let mut bytes = [0u8; Self::MAX_SIZE];
147        bytes[..len].copy_from_slice(ber_bytes);
148
149        Ok(Self {
150            bytes,
151            length: len as u8,
152        })
153    }
154
155    /// Get the BER/DER serialization of this OID as bytes.
156    ///
157    /// Note that this encoding omits the tag/length, and only contains the
158    /// value portion of the encoded OID.
159    pub fn as_bytes(&self) -> &[u8] {
160        &self.bytes[..self.length as usize]
161    }
162
163    /// Return the arc with the given index, if it exists.
164    pub fn arc(&self, index: usize) -> Option<Arc> {
165        self.arcs().nth(index)
166    }
167
168    /// Iterate over the arcs (a.k.a. nodes) of an [`ObjectIdentifier`].
169    ///
170    /// Returns [`Arcs`], an iterator over `Arc` values representing the value
171    /// of each arc/node.
172    pub fn arcs(&self) -> Arcs<'_> {
173        Arcs::new(self)
174    }
175}
176
177impl AsRef<[u8]> for ObjectIdentifier {
178    fn as_ref(&self) -> &[u8] {
179        self.as_bytes()
180    }
181}
182
183impl FromStr for ObjectIdentifier {
184    type Err = Error;
185
186    fn from_str(string: &str) -> Result<Self> {
187        let mut split = string.split('.');
188        let first_arc = split.next().and_then(|s| s.parse().ok()).ok_or(Error)?;
189        let second_arc = split.next().and_then(|s| s.parse().ok()).ok_or(Error)?;
190
191        let mut bytes = [0u8; Self::MAX_SIZE];
192        bytes[0] = RootArcs::new(first_arc, second_arc)?.into();
193
194        let mut offset = 1;
195
196        for s in split {
197            let arc = s.parse().map_err(|_| Error)?;
198            offset += encoder::write_base128(&mut bytes[offset..], arc)?;
199        }
200
201        if offset > 1 {
202            Ok(Self {
203                bytes,
204                length: offset as u8,
205            })
206        } else {
207            // Minimum 3 arcs
208            Err(Error)
209        }
210    }
211}
212
213impl TryFrom<&[u8]> for ObjectIdentifier {
214    type Error = Error;
215
216    fn try_from(ber_bytes: &[u8]) -> Result<Self> {
217        Self::from_bytes(ber_bytes)
218    }
219}
220
221impl From<&ObjectIdentifier> for ObjectIdentifier {
222    fn from(oid: &ObjectIdentifier) -> ObjectIdentifier {
223        *oid
224    }
225}
226
227impl fmt::Debug for ObjectIdentifier {
228    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229        write!(f, "ObjectIdentifier({})", self)
230    }
231}
232
233impl fmt::Display for ObjectIdentifier {
234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235        let len = self.arcs().count();
236
237        for (i, arc) in self.arcs().enumerate() {
238            write!(f, "{}", arc)?;
239
240            if i < len - 1 {
241                write!(f, ".")?;
242            }
243        }
244
245        Ok(())
246    }
247}