const_oid/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_auto_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#![allow(clippy::len_without_is_empty)]
9#![deny(unsafe_code)]
10#![warn(
11    clippy::arithmetic_side_effects,
12    clippy::mod_module_files,
13    clippy::panic,
14    clippy::panic_in_result_fn,
15    clippy::unwrap_used,
16    missing_docs,
17    rust_2018_idioms,
18    unused_lifetimes,
19    unused_qualifications
20)]
21
22#[macro_use]
23mod checked;
24
25mod arcs;
26mod buffer;
27mod encoder;
28mod error;
29mod parser;
30mod traits;
31
32#[cfg(feature = "db")]
33pub mod db;
34
35pub use crate::{
36    arcs::{Arc, Arcs},
37    buffer::Buffer,
38    error::{Error, Result},
39    traits::{AssociatedOid, DynAssociatedOid},
40};
41
42use crate::encoder::Encoder;
43use core::{borrow::Borrow, fmt, ops::Deref, str::FromStr};
44
45/// Default maximum size.
46///
47/// Makes `ObjectIdentifier` 40-bytes total w\ 1-byte length.
48const DEFAULT_MAX_SIZE: usize = 39;
49
50/// Object identifier (OID).
51///
52/// OIDs are hierarchical structures consisting of "arcs", i.e. integer
53/// identifiers.
54///
55/// # Validity
56///
57/// In order for an OID to be considered valid by this library, it must meet
58/// the following criteria:
59///
60/// - The OID MUST have at least 3 arcs
61/// - The first arc MUST be within the range 0-2
62/// - The second arc MUST be within the range 0-39
63/// - The BER/DER encoding of the OID MUST be shorter than
64///   [`ObjectIdentifier::MAX_SIZE`]
65#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
66pub struct ObjectIdentifier<const MAX_SIZE: usize = DEFAULT_MAX_SIZE> {
67    /// Buffer containing BER/DER-serialized bytes (sans ASN.1 tag/length)
68    ber: Buffer<MAX_SIZE>,
69}
70
71impl ObjectIdentifier {
72    /// Maximum size of a BER/DER-encoded OID in bytes.
73    pub const MAX_SIZE: usize = DEFAULT_MAX_SIZE;
74
75    /// Parse an [`ObjectIdentifier`] from the dot-delimited string form,
76    /// panicking on parse errors.
77    ///
78    /// This function exists as a workaround for `unwrap` not yet being
79    /// stable in `const fn` contexts, and is intended to allow the result to
80    /// be bound to a constant value:
81    ///
82    /// ```
83    /// use const_oid::ObjectIdentifier;
84    ///
85    /// pub const MY_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1");
86    /// ```
87    ///
88    /// In future versions of Rust it should be possible to replace this with
89    /// `ObjectIdentifier::new(...).unwrap()`.
90    ///
91    /// Use [`ObjectIdentifier::new`] for fallible parsing.
92    // TODO(tarcieri): remove this when `Result::unwrap` is `const fn`
93    pub const fn new_unwrap(s: &str) -> Self {
94        match Self::new(s) {
95            Ok(oid) => oid,
96            Err(err) => err.panic(),
97        }
98    }
99
100    /// Parse an [`ObjectIdentifier`] from the dot-delimited string form.
101    pub const fn new(s: &str) -> Result<Self> {
102        // TODO(tarcieri): use `?` when stable in `const fn`
103        match parser::Parser::parse(s) {
104            Ok(parser) => parser.finish(),
105            Err(err) => Err(err),
106        }
107    }
108
109    /// Parse an OID from a slice of [`Arc`] values (i.e. integers).
110    pub fn from_arcs(arcs: impl IntoIterator<Item = Arc>) -> Result<Self> {
111        let mut encoder = Encoder::new();
112
113        for arc in arcs {
114            encoder = encoder.arc(arc)?;
115        }
116
117        encoder.finish()
118    }
119
120    /// Parse an OID from from its BER/DER encoding.
121    pub fn from_bytes(ber_bytes: &[u8]) -> Result<Self> {
122        ObjectIdentifierRef::from_bytes(ber_bytes)?.try_into()
123    }
124}
125
126impl<const MAX_SIZE: usize> ObjectIdentifier<MAX_SIZE> {
127    /// Get the BER/DER serialization of this OID as bytes.
128    ///
129    /// Note that this encoding omits the ASN.1 tag/length, and only contains the value portion of
130    /// the encoded OID.
131    pub const fn as_bytes(&self) -> &[u8] {
132        self.ber.as_bytes()
133    }
134
135    /// Borrow an [`ObjectIdentifierRef`] which corresponds to this [`ObjectIdentifier`].
136    pub const fn as_oid_ref(&self) -> &ObjectIdentifierRef {
137        ObjectIdentifierRef::from_bytes_unchecked(self.as_bytes())
138    }
139
140    /// Get the parent OID of this one (if applicable).
141    pub fn parent(&self) -> Option<Self> {
142        let num_arcs = self.len().checked_sub(1)?;
143        let mut encoder = Encoder::new();
144
145        for arc in self.arcs().take(num_arcs) {
146            encoder = encoder.arc(arc).ok()?;
147        }
148
149        encoder.finish().ok()
150    }
151
152    /// Push an additional arc onto this OID, returning the child OID.
153    pub const fn push_arc(self, arc: Arc) -> Result<Self> {
154        // TODO(tarcieri): use `?` when stable in `const fn`
155        match Encoder::extend(self).arc(arc) {
156            Ok(encoder) => encoder.finish(),
157            Err(err) => Err(err),
158        }
159    }
160
161    /// Does this OID start with the other OID?
162    pub const fn starts_with<const SIZE: usize>(&self, other: ObjectIdentifier<SIZE>) -> bool {
163        let len = other.as_bytes().len();
164
165        if self.as_bytes().len() < len {
166            return false;
167        }
168
169        let mut i = 0;
170        while i < len {
171            if self.as_bytes()[i] != other.as_bytes()[i] {
172                return false;
173            }
174
175            match i.checked_add(1) {
176                Some(succ) => i = succ,
177                None => return false,
178            }
179        }
180
181        true
182    }
183}
184
185impl<const MAX_SIZE: usize> AsRef<[u8]> for ObjectIdentifier<MAX_SIZE> {
186    fn as_ref(&self) -> &[u8] {
187        self.as_bytes()
188    }
189}
190
191impl<const MAX_SIZE: usize> AsRef<ObjectIdentifierRef> for ObjectIdentifier<MAX_SIZE> {
192    fn as_ref(&self) -> &ObjectIdentifierRef {
193        self.as_oid_ref()
194    }
195}
196
197impl<const MAX_SIZE: usize> Borrow<ObjectIdentifierRef> for ObjectIdentifier<MAX_SIZE> {
198    fn borrow(&self) -> &ObjectIdentifierRef {
199        self.as_oid_ref()
200    }
201}
202
203impl<const MAX_SIZE: usize> Deref for ObjectIdentifier<MAX_SIZE> {
204    type Target = ObjectIdentifierRef;
205
206    fn deref(&self) -> &ObjectIdentifierRef {
207        self.as_oid_ref()
208    }
209}
210
211impl FromStr for ObjectIdentifier {
212    type Err = Error;
213
214    fn from_str(string: &str) -> Result<Self> {
215        Self::new(string)
216    }
217}
218
219impl TryFrom<&[u8]> for ObjectIdentifier {
220    type Error = Error;
221
222    fn try_from(ber_bytes: &[u8]) -> Result<Self> {
223        Self::from_bytes(ber_bytes)
224    }
225}
226
227impl<const MAX_SIZE: usize> TryFrom<&ObjectIdentifierRef> for ObjectIdentifier<MAX_SIZE> {
228    type Error = Error;
229
230    fn try_from(oid_ref: &ObjectIdentifierRef) -> Result<Self> {
231        let len = oid_ref.as_bytes().len();
232
233        if len > MAX_SIZE {
234            return Err(Error::Length);
235        }
236
237        let mut bytes = [0u8; MAX_SIZE];
238        bytes[..len].copy_from_slice(oid_ref.as_bytes());
239
240        let ber = Buffer {
241            bytes,
242            length: len as u8,
243        };
244
245        Ok(Self { ber })
246    }
247}
248
249impl<const MAX_SIZE: usize> fmt::Debug for ObjectIdentifier<MAX_SIZE> {
250    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251        write!(f, "ObjectIdentifier({})", self)
252    }
253}
254
255impl<const MAX_SIZE: usize> fmt::Display for ObjectIdentifier<MAX_SIZE> {
256    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257        write!(f, "{}", self.as_oid_ref())
258    }
259}
260
261// Implement by hand because the derive would create invalid values.
262// Use the constructor to create a valid oid with at least 3 arcs.
263#[cfg(feature = "arbitrary")]
264impl<'a> arbitrary::Arbitrary<'a> for ObjectIdentifier {
265    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
266        let first = u.int_in_range(0..=arcs::ARC_MAX_FIRST)?;
267        let second = u.int_in_range(0..=arcs::ARC_MAX_SECOND)?;
268        let third = u.arbitrary()?;
269
270        let mut oid = Self::from_arcs([first, second, third])
271            .map_err(|_| arbitrary::Error::IncorrectFormat)?;
272
273        for arc in u.arbitrary_iter()? {
274            oid = oid
275                .push_arc(arc?)
276                .map_err(|_| arbitrary::Error::IncorrectFormat)?;
277        }
278
279        Ok(oid)
280    }
281
282    fn size_hint(depth: usize) -> (usize, Option<usize>) {
283        (Arc::size_hint(depth).0.saturating_mul(3), None)
284    }
285}
286
287/// OID reference type: wrapper for the BER serialization.
288#[derive(Eq, Hash, PartialEq, PartialOrd, Ord)]
289#[repr(transparent)]
290pub struct ObjectIdentifierRef {
291    /// BER/DER-serialized bytes (sans ASN.1 tag/length).
292    ber: [u8],
293}
294
295impl ObjectIdentifierRef {
296    /// Create an [`ObjectIdentifierRef`], validating that the provided byte slice contains a valid
297    /// BER/DER encoding.
298    // TODO(tarcieri): `const fn` support
299    pub fn from_bytes(ber: &[u8]) -> Result<&Self> {
300        // Ensure arcs are well-formed
301        let mut arcs = Arcs::new(ber);
302        while arcs.try_next()?.is_some() {}
303        Ok(Self::from_bytes_unchecked(ber))
304    }
305
306    /// Create an [`ObjectIdentifierRef`] from the given byte slice without first checking that it
307    /// contains valid BER/DER.
308    pub(crate) const fn from_bytes_unchecked(ber: &[u8]) -> &Self {
309        debug_assert!(!ber.is_empty());
310
311        // SAFETY: `ObjectIdentifierRef` is a `repr(transparent)` newtype for `[u8]`.
312        #[allow(unsafe_code)]
313        unsafe {
314            &*(ber as *const [u8] as *const ObjectIdentifierRef)
315        }
316    }
317
318    /// Get the BER/DER serialization of this OID as bytes.
319    ///
320    /// Note that this encoding omits the ASN.1 tag/length, and only contains the value portion of
321    /// the encoded OID.
322    pub const fn as_bytes(&self) -> &[u8] {
323        &self.ber
324    }
325
326    /// Return the arc with the given index, if it exists.
327    pub fn arc(&self, index: usize) -> Option<Arc> {
328        self.arcs().nth(index)
329    }
330
331    /// Iterate over the arcs (a.k.a. nodes) of an [`ObjectIdentifier`].
332    ///
333    /// Returns [`Arcs`], an iterator over [`Arc`] values.
334    pub fn arcs(&self) -> Arcs<'_> {
335        Arcs::new(self.ber.as_ref())
336    }
337
338    /// Get the length of this [`ObjectIdentifier`] in arcs.
339    pub fn len(&self) -> usize {
340        self.arcs().count()
341    }
342}
343
344impl AsRef<[u8]> for ObjectIdentifierRef {
345    fn as_ref(&self) -> &[u8] {
346        self.as_bytes()
347    }
348}
349
350impl<'a, const MAX_SIZE: usize> From<&'a ObjectIdentifier<MAX_SIZE>> for &'a ObjectIdentifierRef {
351    fn from(oid: &'a ObjectIdentifier<MAX_SIZE>) -> &'a ObjectIdentifierRef {
352        oid.as_oid_ref()
353    }
354}
355
356impl<'a> TryFrom<&'a [u8]> for &'a ObjectIdentifierRef {
357    type Error = Error;
358
359    fn try_from(ber_bytes: &'a [u8]) -> Result<Self> {
360        ObjectIdentifierRef::from_bytes(ber_bytes)
361    }
362}
363
364impl fmt::Debug for ObjectIdentifierRef {
365    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366        write!(f, "ObjectIdentifierRef({})", self)
367    }
368}
369
370impl fmt::Display for ObjectIdentifierRef {
371    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372        let len = self.arcs().count();
373
374        for (i, arc) in self.arcs().enumerate() {
375            write!(f, "{}", arc)?;
376
377            if let Some(j) = i.checked_add(1) {
378                if j < len {
379                    write!(f, ".")?;
380                }
381            }
382        }
383
384        Ok(())
385    }
386}
387
388impl<const MAX_SIZE: usize> PartialEq<ObjectIdentifier<MAX_SIZE>> for ObjectIdentifierRef {
389    fn eq(&self, other: &ObjectIdentifier<MAX_SIZE>) -> bool {
390        self.as_bytes().eq(other.as_bytes())
391    }
392}