//! Arcs are integer values which exist within an OID's hierarchy. use crate::{Error, ObjectIdentifier, Result}; use core::mem; /// Type alias used to represent an "arc" (i.e. integer identifier value). /// /// X.660 does not define a maximum size of an arc. /// /// The current representation is `u32`, which has been selected as being /// sufficient to cover the current PKCS/PKIX use cases this library has been /// used in conjunction with. /// /// Future versions may potentially make it larger if a sufficiently important /// use case is discovered. pub type Arc = u32; /// Maximum value of the first arc in an OID. pub(crate) const ARC_MAX_FIRST: Arc = 2; /// Maximum value of the second arc in an OID. pub(crate) const ARC_MAX_SECOND: Arc = 39; /// Maximum number of bytes supported in an arc. const ARC_MAX_BYTES: usize = mem::size_of::(); /// Maximum value of the last byte in an arc. const ARC_MAX_LAST_OCTET: u8 = 0b11110000; // Max bytes of leading 1-bits /// [`Iterator`] over [`Arc`] values (a.k.a. nodes) in an [`ObjectIdentifier`]. /// /// This iterates over all arcs in an OID, including the root. pub struct Arcs<'a> { /// OID we're iterating over oid: &'a ObjectIdentifier, /// Current position within the serialized DER bytes of this OID cursor: Option, } impl<'a> Arcs<'a> { /// Create a new iterator over the arcs of this OID pub(crate) fn new(oid: &'a ObjectIdentifier) -> Self { Self { oid, cursor: None } } /// Try to parse the next arc in this OID. /// /// This method is fallible so it can be used as a first pass to determine /// that the arcs in the OID are well-formed. pub(crate) fn try_next(&mut self) -> Result> { match self.cursor { // Indicates we're on the root OID None => { let root = RootArcs::try_from(self.oid.as_bytes()[0])?; self.cursor = Some(0); Ok(Some(root.first_arc())) } Some(0) => { let root = RootArcs::try_from(self.oid.as_bytes()[0])?; self.cursor = Some(1); Ok(Some(root.second_arc())) } Some(offset) => { let mut result = 0; let mut arc_bytes = 0; loop { let len = checked_add!(offset, arc_bytes); match self.oid.as_bytes().get(len).cloned() { // The arithmetic below includes advance checks // against `ARC_MAX_BYTES` and `ARC_MAX_LAST_OCTET` // which ensure the operations will not overflow. #[allow(clippy::integer_arithmetic)] Some(byte) => { arc_bytes = checked_add!(arc_bytes, 1); if (arc_bytes > ARC_MAX_BYTES) && (byte & ARC_MAX_LAST_OCTET != 0) { return Err(Error::ArcTooBig); } result = result << 7 | (byte & 0b1111111) as Arc; if byte & 0b10000000 == 0 { self.cursor = Some(checked_add!(offset, arc_bytes)); return Ok(Some(result)); } } None => { if arc_bytes == 0 { return Ok(None); } else { return Err(Error::Base128); } } } } } } } } impl<'a> Iterator for Arcs<'a> { type Item = Arc; fn next(&mut self) -> Option { // ObjectIdentifier constructors should ensure the OID is well-formed self.try_next().expect("OID malformed") } } /// Byte containing the first and second arcs of an OID. /// /// This is represented this way in order to reduce the overall size of the /// [`ObjectIdentifier`] struct. #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct RootArcs(u8); impl RootArcs { /// Create [`RootArcs`] from the first and second arc values represented /// as `Arc` integers. pub(crate) const fn new(first_arc: Arc, second_arc: Arc) -> Result { if first_arc > ARC_MAX_FIRST { return Err(Error::ArcInvalid { arc: first_arc }); } if second_arc > ARC_MAX_SECOND { return Err(Error::ArcInvalid { arc: second_arc }); } // The checks above ensure this operation will not overflow #[allow(clippy::integer_arithmetic)] let byte = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + second_arc as u8; Ok(Self(byte)) } /// Get the value of the first arc #[allow(clippy::integer_arithmetic)] pub(crate) const fn first_arc(self) -> Arc { self.0 as Arc / (ARC_MAX_SECOND + 1) } /// Get the value of the second arc #[allow(clippy::integer_arithmetic)] pub(crate) const fn second_arc(self) -> Arc { self.0 as Arc % (ARC_MAX_SECOND + 1) } } impl TryFrom for RootArcs { type Error = Error; // Ensured not to overflow by constructor invariants #[allow(clippy::integer_arithmetic)] fn try_from(octet: u8) -> Result { let first = octet as Arc / (ARC_MAX_SECOND + 1); let second = octet as Arc % (ARC_MAX_SECOND + 1); let result = Self::new(first, second)?; debug_assert_eq!(octet, result.0); Ok(result) } } impl From for u8 { fn from(root_arcs: RootArcs) -> u8 { root_arcs.0 } }