| //! 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::<Arc>(); |
| |
| /// 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<usize>, |
| } |
| |
| 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<Option<Arc>> { |
| 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<Arc> { |
| // 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<Self> { |
| 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<u8> for RootArcs { |
| type Error = Error; |
| |
| // Ensured not to overflow by constructor invariants |
| #[allow(clippy::integer_arithmetic)] |
| fn try_from(octet: u8) -> Result<Self> { |
| 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<RootArcs> for u8 { |
| fn from(root_arcs: RootArcs) -> u8 { |
| root_arcs.0 |
| } |
| } |