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#![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        Self::from_bytes_sized(ber_bytes)
123    }
124}
125
126impl<const MAX_SIZE: usize> ObjectIdentifier<MAX_SIZE> {
127    /// Parse an OID from from its BER/DER encoding.
128    ///
129    /// Returns `Err(Error::Length)` if bytes do not fit in `MAX_SIZE`.
130    pub fn from_bytes_sized(ber_bytes: &[u8]) -> Result<Self> {
131        ObjectIdentifierRef::from_bytes(ber_bytes)?.try_into()
132    }
133
134    /// Get the BER/DER serialization of this OID as bytes.
135    ///
136    /// Note that this encoding omits the ASN.1 tag/length, and only contains the value portion of
137    /// the encoded OID.
138    pub const fn as_bytes(&self) -> &[u8] {
139        self.ber.as_bytes()
140    }
141
142    /// Borrow an [`ObjectIdentifierRef`] which corresponds to this [`ObjectIdentifier`].
143    pub const fn as_oid_ref(&self) -> &ObjectIdentifierRef {
144        ObjectIdentifierRef::from_bytes_unchecked(self.as_bytes())
145    }
146
147    /// Get the parent OID of this one (if applicable).
148    pub fn parent(&self) -> Option<Self> {
149        let num_arcs = self.len().checked_sub(1)?;
150        let mut encoder = Encoder::new();
151
152        for arc in self.arcs().take(num_arcs) {
153            encoder = encoder.arc(arc).ok()?;
154        }
155
156        encoder.finish().ok()
157    }
158
159    /// Push an additional arc onto this OID, returning the child OID.
160    pub const fn push_arc(self, arc: Arc) -> Result<Self> {
161        // TODO(tarcieri): use `?` when stable in `const fn`
162        match Encoder::extend(self).arc(arc) {
163            Ok(encoder) => encoder.finish(),
164            Err(err) => Err(err),
165        }
166    }
167
168    /// Does this OID start with the other OID?
169    pub const fn starts_with<const SIZE: usize>(&self, other: ObjectIdentifier<SIZE>) -> bool {
170        let len = other.as_bytes().len();
171
172        if self.as_bytes().len() < len {
173            return false;
174        }
175
176        let mut i = 0;
177        while i < len {
178            if self.as_bytes()[i] != other.as_bytes()[i] {
179                return false;
180            }
181
182            match i.checked_add(1) {
183                Some(succ) => i = succ,
184                None => return false,
185            }
186        }
187
188        true
189    }
190}
191
192impl<const MAX_SIZE: usize> AsRef<[u8]> for ObjectIdentifier<MAX_SIZE> {
193    fn as_ref(&self) -> &[u8] {
194        self.as_bytes()
195    }
196}
197
198impl<const MAX_SIZE: usize> AsRef<ObjectIdentifierRef> for ObjectIdentifier<MAX_SIZE> {
199    fn as_ref(&self) -> &ObjectIdentifierRef {
200        self.as_oid_ref()
201    }
202}
203
204impl<const MAX_SIZE: usize> Borrow<ObjectIdentifierRef> for ObjectIdentifier<MAX_SIZE> {
205    fn borrow(&self) -> &ObjectIdentifierRef {
206        self.as_oid_ref()
207    }
208}
209
210impl<const MAX_SIZE: usize> Deref for ObjectIdentifier<MAX_SIZE> {
211    type Target = ObjectIdentifierRef;
212
213    fn deref(&self) -> &ObjectIdentifierRef {
214        self.as_oid_ref()
215    }
216}
217
218impl FromStr for ObjectIdentifier {
219    type Err = Error;
220
221    fn from_str(string: &str) -> Result<Self> {
222        Self::new(string)
223    }
224}
225
226impl TryFrom<&[u8]> for ObjectIdentifier {
227    type Error = Error;
228
229    fn try_from(ber_bytes: &[u8]) -> Result<Self> {
230        Self::from_bytes(ber_bytes)
231    }
232}
233
234impl<const MAX_SIZE: usize> TryFrom<&ObjectIdentifierRef> for ObjectIdentifier<MAX_SIZE> {
235    type Error = Error;
236
237    fn try_from(oid_ref: &ObjectIdentifierRef) -> Result<Self> {
238        let len = oid_ref.as_bytes().len();
239
240        if len > MAX_SIZE {
241            return Err(Error::Length);
242        }
243
244        let mut bytes = [0u8; MAX_SIZE];
245        bytes[..len].copy_from_slice(oid_ref.as_bytes());
246
247        let ber = Buffer {
248            bytes,
249            length: len as u8,
250        };
251
252        Ok(Self { ber })
253    }
254}
255
256impl<const MAX_SIZE: usize> fmt::Debug for ObjectIdentifier<MAX_SIZE> {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        write!(f, "ObjectIdentifier({self})")
259    }
260}
261
262impl<const MAX_SIZE: usize> fmt::Display for ObjectIdentifier<MAX_SIZE> {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        write!(f, "{}", self.as_oid_ref())
265    }
266}
267
268// Implement by hand because the derive would create invalid values.
269// Use the constructor to create a valid oid with at least 3 arcs.
270#[cfg(feature = "arbitrary")]
271impl<'a> arbitrary::Arbitrary<'a> for ObjectIdentifier {
272    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
273        let first = u.int_in_range(0..=arcs::ARC_MAX_FIRST)?;
274        let second = u.int_in_range(0..=arcs::ARC_MAX_SECOND)?;
275        let third = u.arbitrary()?;
276
277        let mut oid = Self::from_arcs([first, second, third])
278            .map_err(|_| arbitrary::Error::IncorrectFormat)?;
279
280        for arc in u.arbitrary_iter()? {
281            oid = oid
282                .push_arc(arc?)
283                .map_err(|_| arbitrary::Error::IncorrectFormat)?;
284        }
285
286        Ok(oid)
287    }
288
289    fn size_hint(depth: usize) -> (usize, Option<usize>) {
290        (Arc::size_hint(depth).0.saturating_mul(3), None)
291    }
292}
293
294/// OID reference type: wrapper for the BER serialization.
295#[derive(Eq, Hash, PartialEq, PartialOrd, Ord)]
296#[repr(transparent)]
297pub struct ObjectIdentifierRef {
298    /// BER/DER-serialized bytes (sans ASN.1 tag/length).
299    ber: [u8],
300}
301
302impl ObjectIdentifierRef {
303    /// Create an [`ObjectIdentifierRef`], validating that the provided byte slice contains a valid
304    /// BER/DER encoding.
305    // TODO(tarcieri): `const fn` support
306    pub fn from_bytes(ber: &[u8]) -> Result<&Self> {
307        // Ensure arcs are well-formed
308        let mut arcs = Arcs::new(ber);
309        while arcs.try_next()?.is_some() {}
310        Ok(Self::from_bytes_unchecked(ber))
311    }
312
313    /// Create an [`ObjectIdentifierRef`] from the given byte slice without first checking that it
314    /// contains valid BER/DER.
315    pub(crate) const fn from_bytes_unchecked(ber: &[u8]) -> &Self {
316        debug_assert!(!ber.is_empty());
317
318        // SAFETY: `ObjectIdentifierRef` is a `repr(transparent)` newtype for `[u8]`.
319        #[allow(unsafe_code)]
320        unsafe {
321            &*(ber as *const [u8] as *const ObjectIdentifierRef)
322        }
323    }
324
325    /// Get the BER/DER serialization of this OID as bytes.
326    ///
327    /// Note that this encoding omits the ASN.1 tag/length, and only contains the value portion of
328    /// the encoded OID.
329    pub const fn as_bytes(&self) -> &[u8] {
330        &self.ber
331    }
332
333    /// Return the arc with the given index, if it exists.
334    pub fn arc(&self, index: usize) -> Option<Arc> {
335        self.arcs().nth(index)
336    }
337
338    /// Iterate over the arcs (a.k.a. nodes) of an [`ObjectIdentifier`].
339    ///
340    /// Returns [`Arcs`], an iterator over [`Arc`] values.
341    pub fn arcs(&self) -> Arcs<'_> {
342        Arcs::new(self.ber.as_ref())
343    }
344
345    /// Get the length of this [`ObjectIdentifier`] in arcs.
346    pub fn len(&self) -> usize {
347        self.arcs().count()
348    }
349}
350
351impl AsRef<[u8]> for ObjectIdentifierRef {
352    fn as_ref(&self) -> &[u8] {
353        self.as_bytes()
354    }
355}
356
357impl<'a, const MAX_SIZE: usize> From<&'a ObjectIdentifier<MAX_SIZE>> for &'a ObjectIdentifierRef {
358    fn from(oid: &'a ObjectIdentifier<MAX_SIZE>) -> &'a ObjectIdentifierRef {
359        oid.as_oid_ref()
360    }
361}
362
363impl<'a> TryFrom<&'a [u8]> for &'a ObjectIdentifierRef {
364    type Error = Error;
365
366    fn try_from(ber_bytes: &'a [u8]) -> Result<Self> {
367        ObjectIdentifierRef::from_bytes(ber_bytes)
368    }
369}
370
371impl fmt::Debug for ObjectIdentifierRef {
372    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373        write!(f, "ObjectIdentifierRef({self})")
374    }
375}
376
377impl fmt::Display for ObjectIdentifierRef {
378    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
379        let len = self.arcs().count();
380
381        for (i, arc) in self.arcs().enumerate() {
382            write!(f, "{arc}")?;
383
384            if let Some(j) = i.checked_add(1) {
385                if j < len {
386                    write!(f, ".")?;
387                }
388            }
389        }
390
391        Ok(())
392    }
393}
394
395impl<const MAX_SIZE: usize> PartialEq<ObjectIdentifier<MAX_SIZE>> for ObjectIdentifierRef {
396    fn eq(&self, other: &ObjectIdentifier<MAX_SIZE>) -> bool {
397        self.as_bytes().eq(other.as_bytes())
398    }
399}