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/media/6ee8e381/logo.svg",
6    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
7)]
8#![forbid(unsafe_code)]
9#![warn(
10    clippy::integer_arithmetic,
11    clippy::panic,
12    clippy::panic_in_result_fn,
13    clippy::unwrap_used,
14    missing_docs,
15    rust_2018_idioms,
16    unused_lifetimes,
17    unused_qualifications
18)]
19
20#[cfg(feature = "std")]
21extern crate std;
22
23#[macro_use]
24mod checked;
25
26mod arcs;
27mod encoder;
28mod error;
29mod parser;
30
31#[cfg(feature = "db")]
32#[cfg_attr(docsrs, doc(cfg(feature = "db")))]
33pub mod db;
34
35pub use crate::{
36    arcs::{Arc, Arcs},
37    error::{Error, Result},
38};
39
40use crate::encoder::Encoder;
41use core::{fmt, str::FromStr};
42
43/// A trait which associates an OID with a type.
44pub trait AssociatedOid {
45    /// The OID associated with this type.
46    const OID: ObjectIdentifier;
47}
48
49/// A trait which associates a dynamic, `&self`-dependent OID with a type,
50/// which may change depending on the type's value.
51///
52/// This trait is object safe and auto-impl'd for any types which impl
53/// [`AssociatedOid`].
54pub trait DynAssociatedOid {
55    /// Get the OID associated with this value.
56    fn oid(&self) -> ObjectIdentifier;
57}
58
59impl<T: AssociatedOid> DynAssociatedOid for T {
60    fn oid(&self) -> ObjectIdentifier {
61        T::OID
62    }
63}
64
65/// Object identifier (OID).
66///
67/// OIDs are hierarchical structures consisting of "arcs", i.e. integer
68/// identifiers.
69///
70/// # Validity
71///
72/// In order for an OID to be considered valid by this library, it must meet
73/// the following criteria:
74///
75/// - The OID MUST have at least 3 arcs
76/// - The first arc MUST be within the range 0-2
77/// - The second arc MUST be within the range 0-39
78/// - The BER/DER encoding of the OID MUST be shorter than
79///   [`ObjectIdentifier::MAX_SIZE`]
80#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
81pub struct ObjectIdentifier {
82    /// Length in bytes
83    length: u8,
84
85    /// Array containing BER/DER-serialized bytes (no header)
86    bytes: [u8; Self::MAX_SIZE],
87}
88
89#[allow(clippy::len_without_is_empty)]
90impl ObjectIdentifier {
91    /// Maximum size of a BER/DER-encoded OID in bytes.
92    pub const MAX_SIZE: usize = 39; // makes `ObjectIdentifier` 40-bytes total w\ 1-byte length
93
94    /// Parse an [`ObjectIdentifier`] from the dot-delimited string form,
95    /// panicking on parse errors.
96    ///
97    /// This function exists as a workaround for `unwrap` not yet being
98    /// stable in `const fn` contexts, and is intended to allow the result to
99    /// be bound to a constant value:
100    ///
101    /// ```
102    /// use const_oid::ObjectIdentifier;
103    ///
104    /// pub const MY_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1");
105    /// ```
106    ///
107    /// In future versions of Rust it should be possible to replace this with
108    /// `ObjectIdentifier::new(...).unwrap()`.
109    ///
110    /// Use [`ObjectIdentifier::new`] for fallible parsing.
111    // TODO(tarcieri): remove this when `Result::unwrap` is `const fn`
112    pub const fn new_unwrap(s: &str) -> Self {
113        match Self::new(s) {
114            Ok(oid) => oid,
115            Err(err) => err.panic(),
116        }
117    }
118
119    /// Parse an [`ObjectIdentifier`] from the dot-delimited string form.
120    pub const fn new(s: &str) -> Result<Self> {
121        // TODO(tarcieri): use `?` when stable in `const fn`
122        match parser::Parser::parse(s) {
123            Ok(parser) => parser.finish(),
124            Err(err) => Err(err),
125        }
126    }
127
128    /// Parse an OID from a slice of [`Arc`] values (i.e. integers).
129    pub fn from_arcs(arcs: impl IntoIterator<Item = Arc>) -> Result<Self> {
130        let mut encoder = Encoder::new();
131
132        for arc in arcs {
133            encoder = encoder.arc(arc)?;
134        }
135
136        encoder.finish()
137    }
138
139    /// Parse an OID from from its BER/DER encoding.
140    pub fn from_bytes(ber_bytes: &[u8]) -> Result<Self> {
141        let len = ber_bytes.len();
142
143        match len {
144            0 => return Err(Error::Empty),
145            3..=Self::MAX_SIZE => (),
146            _ => return Err(Error::NotEnoughArcs),
147        }
148        let mut bytes = [0u8; Self::MAX_SIZE];
149        bytes[..len].copy_from_slice(ber_bytes);
150
151        let oid = Self {
152            bytes,
153            length: len as u8,
154        };
155
156        // Ensure arcs are well-formed
157        let mut arcs = oid.arcs();
158        while arcs.try_next()?.is_some() {}
159
160        Ok(oid)
161    }
162
163    /// Get the BER/DER serialization of this OID as bytes.
164    ///
165    /// Note that this encoding omits the tag/length, and only contains the
166    /// value portion of the encoded OID.
167    pub fn as_bytes(&self) -> &[u8] {
168        &self.bytes[..self.length as usize]
169    }
170
171    /// Return the arc with the given index, if it exists.
172    pub fn arc(&self, index: usize) -> Option<Arc> {
173        self.arcs().nth(index)
174    }
175
176    /// Iterate over the arcs (a.k.a. nodes) of an [`ObjectIdentifier`].
177    ///
178    /// Returns [`Arcs`], an iterator over [`Arc`] values.
179    pub fn arcs(&self) -> Arcs<'_> {
180        Arcs::new(self)
181    }
182
183    /// Get the length of this [`ObjectIdentifier`] in arcs.
184    pub fn len(&self) -> usize {
185        self.arcs().count()
186    }
187
188    /// Get the parent OID of this one (if applicable).
189    pub fn parent(&self) -> Option<Self> {
190        let num_arcs = self.len().checked_sub(1)?;
191        Self::from_arcs(self.arcs().take(num_arcs)).ok()
192    }
193
194    /// Push an additional arc onto this OID, returning the child OID.
195    pub const fn push_arc(self, arc: Arc) -> Result<Self> {
196        // TODO(tarcieri): use `?` when stable in `const fn`
197        match Encoder::extend(self).arc(arc) {
198            Ok(encoder) => encoder.finish(),
199            Err(err) => Err(err),
200        }
201    }
202}
203
204impl AsRef<[u8]> for ObjectIdentifier {
205    fn as_ref(&self) -> &[u8] {
206        self.as_bytes()
207    }
208}
209
210impl FromStr for ObjectIdentifier {
211    type Err = Error;
212
213    fn from_str(string: &str) -> Result<Self> {
214        Self::new(string)
215    }
216}
217
218impl TryFrom<&[u8]> for ObjectIdentifier {
219    type Error = Error;
220
221    fn try_from(ber_bytes: &[u8]) -> Result<Self> {
222        Self::from_bytes(ber_bytes)
223    }
224}
225
226impl From<&ObjectIdentifier> for ObjectIdentifier {
227    fn from(oid: &ObjectIdentifier) -> ObjectIdentifier {
228        *oid
229    }
230}
231
232impl fmt::Debug for ObjectIdentifier {
233    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234        write!(f, "ObjectIdentifier({})", self)
235    }
236}
237
238impl fmt::Display for ObjectIdentifier {
239    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240        let len = self.arcs().count();
241
242        for (i, arc) in self.arcs().enumerate() {
243            write!(f, "{}", arc)?;
244
245            if let Some(j) = i.checked_add(1) {
246                if j < len {
247                    write!(f, ".")?;
248                }
249            }
250        }
251
252        Ok(())
253    }
254}
255
256// Implement by hand because the derive would create invalid values.
257// Use the constructor to create a valid oid with at least 3 arcs.
258#[cfg(feature = "arbitrary")]
259impl<'a> arbitrary::Arbitrary<'a> for ObjectIdentifier {
260    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
261        let first = u.int_in_range(0..=arcs::ARC_MAX_FIRST)?;
262        let second = u.int_in_range(0..=arcs::ARC_MAX_SECOND)?;
263        let third = u.arbitrary()?;
264
265        let mut oid = Self::from_arcs([first, second, third])
266            .map_err(|_| arbitrary::Error::IncorrectFormat)?;
267
268        for arc in u.arbitrary_iter()? {
269            oid = oid
270                .push_arc(arc?)
271                .map_err(|_| arbitrary::Error::IncorrectFormat)?;
272        }
273
274        Ok(oid)
275    }
276
277    fn size_hint(depth: usize) -> (usize, Option<usize>) {
278        (Arc::size_hint(depth).0.saturating_mul(3), None)
279    }
280}