#![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("../README.md")] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" )] #![forbid(unsafe_code)] #![warn( clippy::integer_arithmetic, clippy::panic, clippy::panic_in_result_fn, clippy::unwrap_used, missing_docs, rust_2018_idioms, unused_lifetimes, unused_qualifications )] #[cfg(feature = "std")] extern crate std; #[macro_use] mod checked; mod arcs; mod encoder; mod error; mod parser; #[cfg(feature = "db")] #[cfg_attr(docsrs, doc(cfg(feature = "db")))] pub mod db; pub use crate::{ arcs::{Arc, Arcs}, error::{Error, Result}, }; use crate::encoder::Encoder; use core::{fmt, str::FromStr}; /// A trait which associates an OID with a type. pub trait AssociatedOid { /// The OID associated with this type. const OID: ObjectIdentifier; } /// A trait which associates a dynamic, `&self`-dependent OID with a type, /// which may change depending on the type's value. /// /// This trait is object safe and auto-impl'd for any types which impl /// [`AssociatedOid`]. pub trait DynAssociatedOid { /// Get the OID associated with this value. fn oid(&self) -> ObjectIdentifier; } impl DynAssociatedOid for T { fn oid(&self) -> ObjectIdentifier { T::OID } } /// Object identifier (OID). /// /// OIDs are hierarchical structures consisting of "arcs", i.e. integer /// identifiers. /// /// # Validity /// /// In order for an OID to be considered valid by this library, it must meet /// the following criteria: /// /// - The OID MUST have at least 3 arcs /// - The first arc MUST be within the range 0-2 /// - The second arc MUST be within the range 0-39 /// - The BER/DER encoding of the OID MUST be shorter than /// [`ObjectIdentifier::MAX_SIZE`] #[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)] pub struct ObjectIdentifier { /// Length in bytes length: u8, /// Array containing BER/DER-serialized bytes (no header) bytes: [u8; Self::MAX_SIZE], } #[allow(clippy::len_without_is_empty)] impl ObjectIdentifier { /// Maximum size of a BER/DER-encoded OID in bytes. pub const MAX_SIZE: usize = 39; // makes `ObjectIdentifier` 40-bytes total w\ 1-byte length /// Parse an [`ObjectIdentifier`] from the dot-delimited string form, /// panicking on parse errors. /// /// This function exists as a workaround for `unwrap` not yet being /// stable in `const fn` contexts, and is intended to allow the result to /// be bound to a constant value: /// /// ``` /// use const_oid::ObjectIdentifier; /// /// pub const MY_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1"); /// ``` /// /// In future versions of Rust it should be possible to replace this with /// `ObjectIdentifier::new(...).unwrap()`. /// /// Use [`ObjectIdentifier::new`] for fallible parsing. // TODO(tarcieri): remove this when `Result::unwrap` is `const fn` pub const fn new_unwrap(s: &str) -> Self { match Self::new(s) { Ok(oid) => oid, Err(err) => err.panic(), } } /// Parse an [`ObjectIdentifier`] from the dot-delimited string form. pub const fn new(s: &str) -> Result { // TODO(tarcieri): use `?` when stable in `const fn` match parser::Parser::parse(s) { Ok(parser) => parser.finish(), Err(err) => Err(err), } } /// Parse an OID from a slice of [`Arc`] values (i.e. integers). pub fn from_arcs(arcs: impl IntoIterator) -> Result { let mut encoder = Encoder::new(); for arc in arcs { encoder = encoder.arc(arc)?; } encoder.finish() } /// Parse an OID from from its BER/DER encoding. pub fn from_bytes(ber_bytes: &[u8]) -> Result { let len = ber_bytes.len(); match len { 0 => return Err(Error::Empty), 3..=Self::MAX_SIZE => (), _ => return Err(Error::NotEnoughArcs), } let mut bytes = [0u8; Self::MAX_SIZE]; bytes[..len].copy_from_slice(ber_bytes); let oid = Self { bytes, length: len as u8, }; // Ensure arcs are well-formed let mut arcs = oid.arcs(); while arcs.try_next()?.is_some() {} Ok(oid) } /// Get the BER/DER serialization of this OID as bytes. /// /// Note that this encoding omits the tag/length, and only contains the /// value portion of the encoded OID. pub fn as_bytes(&self) -> &[u8] { &self.bytes[..self.length as usize] } /// Return the arc with the given index, if it exists. pub fn arc(&self, index: usize) -> Option { self.arcs().nth(index) } /// Iterate over the arcs (a.k.a. nodes) of an [`ObjectIdentifier`]. /// /// Returns [`Arcs`], an iterator over [`Arc`] values. pub fn arcs(&self) -> Arcs<'_> { Arcs::new(self) } /// Get the length of this [`ObjectIdentifier`] in arcs. pub fn len(&self) -> usize { self.arcs().count() } /// Get the parent OID of this one (if applicable). pub fn parent(&self) -> Option { let num_arcs = self.len().checked_sub(1)?; Self::from_arcs(self.arcs().take(num_arcs)).ok() } /// Push an additional arc onto this OID, returning the child OID. pub const fn push_arc(self, arc: Arc) -> Result { // TODO(tarcieri): use `?` when stable in `const fn` match Encoder::extend(self).arc(arc) { Ok(encoder) => encoder.finish(), Err(err) => Err(err), } } } impl AsRef<[u8]> for ObjectIdentifier { fn as_ref(&self) -> &[u8] { self.as_bytes() } } impl FromStr for ObjectIdentifier { type Err = Error; fn from_str(string: &str) -> Result { Self::new(string) } } impl TryFrom<&[u8]> for ObjectIdentifier { type Error = Error; fn try_from(ber_bytes: &[u8]) -> Result { Self::from_bytes(ber_bytes) } } impl From<&ObjectIdentifier> for ObjectIdentifier { fn from(oid: &ObjectIdentifier) -> ObjectIdentifier { *oid } } impl fmt::Debug for ObjectIdentifier { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ObjectIdentifier({})", self) } } impl fmt::Display for ObjectIdentifier { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let len = self.arcs().count(); for (i, arc) in self.arcs().enumerate() { write!(f, "{}", arc)?; if let Some(j) = i.checked_add(1) { if j < len { write!(f, ".")?; } } } Ok(()) } } // Implement by hand because the derive would create invalid values. // Use the constructor to create a valid oid with at least 3 arcs. #[cfg(feature = "arbitrary")] impl<'a> arbitrary::Arbitrary<'a> for ObjectIdentifier { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { let first = u.int_in_range(0..=arcs::ARC_MAX_FIRST)?; let second = u.int_in_range(0..=arcs::ARC_MAX_SECOND)?; let third = u.arbitrary()?; let mut oid = Self::from_arcs([first, second, third]) .map_err(|_| arbitrary::Error::IncorrectFormat)?; for arc in u.arbitrary_iter()? { oid = oid .push_arc(arc?) .map_err(|_| arbitrary::Error::IncorrectFormat)?; } Ok(oid) } fn size_hint(depth: usize) -> (usize, Option) { (Arc::size_hint(depth).0.saturating_mul(3), None) } }