const_oid/arcs.rs
1//! Arcs are integer values which exist within an OID's hierarchy.
2
3use crate::{Error, Result};
4
5#[cfg(doc)]
6use crate::ObjectIdentifier;
7
8/// Type alias used to represent an "arc", i.e. integer identifier value, where an OID comprises a
9/// sequence of arcs.
10///
11/// X.660 does not define a maximum size of an arc. We instead follow Mozilla* conventions for
12/// maximum values of an arc, with a maximum value of 2^32-1 (4294967295), a.k.a. [`u32::MAX`]
13/// with [`Arc`] being a type alias for [`u32`].
14///
15/// Note that this means we deliberately do *NOT* support UUIDs used as OIDs.
16///
17/// *NOTE: please see this study for a survey of how various OID libraries handle maximum arcs:
18/// <https://misc.daniel-marschall.de/asn.1/oid_facts.html>
19pub type Arc = u32;
20
21/// Maximum value of the first arc in an OID.
22pub(crate) const ARC_MAX_FIRST: Arc = 2;
23
24/// Maximum value of the second arc in an OID.
25pub(crate) const ARC_MAX_SECOND: Arc = 39;
26
27/// Maximum number of bytes supported in an arc.
28///
29/// Note that OIDs are base 128 encoded (with continuation bits), so we must consider how many bytes
30/// are required when each byte can only represent 7-bits of the input.
31const ARC_MAX_BYTES: usize = (Arc::BITS as usize).div_ceil(7);
32
33/// Maximum value of the last byte in an arc.
34const ARC_MAX_LAST_OCTET: u8 = 0b11110000; // Max bytes of leading 1-bits
35
36/// [`Iterator`] over [`Arc`] values (a.k.a. nodes) in an [`ObjectIdentifier`].
37///
38/// This iterates over all arcs in an OID, including the root.
39pub struct Arcs<'a> {
40 /// OID bytes we're iterating over.
41 bytes: &'a [u8],
42
43 /// Current position within the serialized BER bytes of this OID.
44 cursor: Option<usize>,
45}
46
47impl<'a> Arcs<'a> {
48 /// Create a new iterator over an OID encoded as BER bytes.
49 pub(crate) fn new(bytes: &'a [u8]) -> Self {
50 Self {
51 bytes,
52 cursor: None,
53 }
54 }
55
56 /// Try to parse the next arc in this OID.
57 ///
58 /// This method is fallible so it can be used as a first pass to determine
59 /// that the arcs in the OID are well-formed.
60 pub(crate) fn try_next(&mut self) -> Result<Option<Arc>> {
61 match self.cursor {
62 // Indicates we're on the root arc
63 None => {
64 let root_byte = *self.bytes.first().ok_or(Error::Empty)?;
65 let root = RootArcs::try_from(root_byte)?;
66 self.cursor = Some(0);
67 Ok(Some(root.first_arc()))
68 }
69 Some(0) => {
70 let root = RootArcs::try_from(self.bytes[0])?;
71 self.cursor = Some(1);
72 Ok(Some(root.second_arc()))
73 }
74 Some(offset) => {
75 let mut result = 0;
76 let mut arc_bytes = 0;
77
78 loop {
79 let len = checked_add!(offset, arc_bytes);
80
81 match self.bytes.get(len).cloned() {
82 // The arithmetic below includes advance checks
83 // against `ARC_MAX_BYTES` and `ARC_MAX_LAST_OCTET`
84 // which ensure the operations will not overflow.
85 #[allow(clippy::arithmetic_side_effects)]
86 Some(byte) => {
87 arc_bytes = checked_add!(arc_bytes, 1);
88
89 if (arc_bytes > ARC_MAX_BYTES) && (byte & ARC_MAX_LAST_OCTET != 0) {
90 return Err(Error::ArcTooBig);
91 }
92
93 result = (result << 7) | (byte & 0b1111111) as Arc;
94
95 if byte & 0b10000000 == 0 {
96 self.cursor = Some(checked_add!(offset, arc_bytes));
97 return Ok(Some(result));
98 }
99 }
100 None => {
101 if arc_bytes == 0 {
102 return Ok(None);
103 } else {
104 return Err(Error::Base128);
105 }
106 }
107 }
108 }
109 }
110 }
111 }
112}
113
114impl Iterator for Arcs<'_> {
115 type Item = Arc;
116
117 fn next(&mut self) -> Option<Arc> {
118 // ObjectIdentifier constructors should ensure the OID is well-formed
119 self.try_next().expect("OID malformed")
120 }
121}
122
123/// Byte containing the first and second arcs of an OID.
124///
125/// This is represented this way in order to reduce the overall size of the
126/// [`ObjectIdentifier`] struct.
127#[derive(Copy, Clone, Debug, Eq, PartialEq)]
128struct RootArcs(u8);
129
130impl RootArcs {
131 /// Create [`RootArcs`] from the first and second arc values represented
132 /// as `Arc` integers.
133 pub(crate) const fn new(first_arc: Arc, second_arc: Arc) -> Result<Self> {
134 if first_arc > ARC_MAX_FIRST {
135 return Err(Error::ArcInvalid { arc: first_arc });
136 }
137
138 if second_arc > ARC_MAX_SECOND {
139 return Err(Error::ArcInvalid { arc: second_arc });
140 }
141
142 // The checks above ensure this operation will not overflow
143 #[allow(clippy::arithmetic_side_effects)]
144 let byte = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + second_arc as u8;
145
146 Ok(Self(byte))
147 }
148
149 /// Get the value of the first arc
150 #[allow(clippy::arithmetic_side_effects)]
151 pub(crate) const fn first_arc(self) -> Arc {
152 self.0 as Arc / (ARC_MAX_SECOND + 1)
153 }
154
155 /// Get the value of the second arc
156 #[allow(clippy::arithmetic_side_effects)]
157 pub(crate) const fn second_arc(self) -> Arc {
158 self.0 as Arc % (ARC_MAX_SECOND + 1)
159 }
160}
161
162impl TryFrom<u8> for RootArcs {
163 type Error = Error;
164
165 // Ensured not to overflow by constructor invariants
166 #[allow(clippy::arithmetic_side_effects)]
167 fn try_from(octet: u8) -> Result<Self> {
168 let first = octet as Arc / (ARC_MAX_SECOND + 1);
169 let second = octet as Arc % (ARC_MAX_SECOND + 1);
170 let result = Self::new(first, second)?;
171 debug_assert_eq!(octet, result.0);
172 Ok(result)
173 }
174}
175
176impl From<RootArcs> for u8 {
177 fn from(root_arcs: RootArcs) -> u8 {
178 root_arcs.0
179 }
180}