const_oid/arcs.rs
1//! Arcs are integer values which exist within an OID's hierarchy.
2
3use crate::{Error, ObjectIdentifier, Result};
4use core::mem;
5
6/// Type alias used to represent an "arc" (i.e. integer identifier value).
7///
8/// X.660 does not define a maximum size of an arc.
9///
10/// The current representation is `u32`, which has been selected as being
11/// sufficient to cover the current PKCS/PKIX use cases this library has been
12/// used in conjunction with.
13///
14/// Future versions may potentially make it larger if a sufficiently important
15/// use case is discovered.
16pub type Arc = u32;
17
18/// Maximum value of the first arc in an OID.
19pub(crate) const ARC_MAX_FIRST: Arc = 2;
20
21/// Maximum value of the second arc in an OID.
22pub(crate) const ARC_MAX_SECOND: Arc = 39;
23
24/// Maximum number of bytes supported in an arc.
25const ARC_MAX_BYTES: usize = mem::size_of::<Arc>();
26
27/// Maximum value of the last byte in an arc.
28const ARC_MAX_LAST_OCTET: u8 = 0b11110000; // Max bytes of leading 1-bits
29
30/// [`Iterator`] over [`Arc`] values (a.k.a. nodes) in an [`ObjectIdentifier`].
31///
32/// This iterates over all arcs in an OID, including the root.
33pub struct Arcs<'a> {
34 /// OID we're iterating over
35 oid: &'a ObjectIdentifier,
36
37 /// Current position within the serialized DER bytes of this OID
38 cursor: Option<usize>,
39}
40
41impl<'a> Arcs<'a> {
42 /// Create a new iterator over the arcs of this OID
43 pub(crate) fn new(oid: &'a ObjectIdentifier) -> Self {
44 Self { oid, cursor: None }
45 }
46
47 /// Try to parse the next arc in this OID.
48 ///
49 /// This method is fallible so it can be used as a first pass to determine
50 /// that the arcs in the OID are well-formed.
51 pub(crate) fn try_next(&mut self) -> Result<Option<Arc>> {
52 match self.cursor {
53 // Indicates we're on the root OID
54 None => {
55 let root = RootArcs::try_from(self.oid.as_bytes()[0])?;
56 self.cursor = Some(0);
57 Ok(Some(root.first_arc()))
58 }
59 Some(0) => {
60 let root = RootArcs::try_from(self.oid.as_bytes()[0])?;
61 self.cursor = Some(1);
62 Ok(Some(root.second_arc()))
63 }
64 Some(offset) => {
65 let mut result = 0;
66 let mut arc_bytes = 0;
67
68 loop {
69 let len = checked_add!(offset, arc_bytes);
70
71 match self.oid.as_bytes().get(len).cloned() {
72 // The arithmetic below includes advance checks
73 // against `ARC_MAX_BYTES` and `ARC_MAX_LAST_OCTET`
74 // which ensure the operations will not overflow.
75 #[allow(clippy::integer_arithmetic)]
76 Some(byte) => {
77 arc_bytes = checked_add!(arc_bytes, 1);
78
79 if (arc_bytes > ARC_MAX_BYTES) && (byte & ARC_MAX_LAST_OCTET != 0) {
80 return Err(Error::ArcTooBig);
81 }
82
83 result = result << 7 | (byte & 0b1111111) as Arc;
84
85 if byte & 0b10000000 == 0 {
86 self.cursor = Some(checked_add!(offset, arc_bytes));
87 return Ok(Some(result));
88 }
89 }
90 None => {
91 if arc_bytes == 0 {
92 return Ok(None);
93 } else {
94 return Err(Error::Base128);
95 }
96 }
97 }
98 }
99 }
100 }
101 }
102}
103
104impl<'a> Iterator for Arcs<'a> {
105 type Item = Arc;
106
107 fn next(&mut self) -> Option<Arc> {
108 // ObjectIdentifier constructors should ensure the OID is well-formed
109 self.try_next().expect("OID malformed")
110 }
111}
112
113/// Byte containing the first and second arcs of an OID.
114///
115/// This is represented this way in order to reduce the overall size of the
116/// [`ObjectIdentifier`] struct.
117#[derive(Copy, Clone, Debug, Eq, PartialEq)]
118struct RootArcs(u8);
119
120impl RootArcs {
121 /// Create [`RootArcs`] from the first and second arc values represented
122 /// as `Arc` integers.
123 pub(crate) const fn new(first_arc: Arc, second_arc: Arc) -> Result<Self> {
124 if first_arc > ARC_MAX_FIRST {
125 return Err(Error::ArcInvalid { arc: first_arc });
126 }
127
128 if second_arc > ARC_MAX_SECOND {
129 return Err(Error::ArcInvalid { arc: second_arc });
130 }
131
132 // The checks above ensure this operation will not overflow
133 #[allow(clippy::integer_arithmetic)]
134 let byte = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + second_arc as u8;
135
136 Ok(Self(byte))
137 }
138
139 /// Get the value of the first arc
140 #[allow(clippy::integer_arithmetic)]
141 pub(crate) const fn first_arc(self) -> Arc {
142 self.0 as Arc / (ARC_MAX_SECOND + 1)
143 }
144
145 /// Get the value of the second arc
146 #[allow(clippy::integer_arithmetic)]
147 pub(crate) const fn second_arc(self) -> Arc {
148 self.0 as Arc % (ARC_MAX_SECOND + 1)
149 }
150}
151
152impl TryFrom<u8> for RootArcs {
153 type Error = Error;
154
155 // Ensured not to overflow by constructor invariants
156 #[allow(clippy::integer_arithmetic)]
157 fn try_from(octet: u8) -> Result<Self> {
158 let first = octet as Arc / (ARC_MAX_SECOND + 1);
159 let second = octet as Arc % (ARC_MAX_SECOND + 1);
160 let result = Self::new(first, second)?;
161 debug_assert_eq!(octet, result.0);
162 Ok(result)
163 }
164}
165
166impl From<RootArcs> for u8 {
167 fn from(root_arcs: RootArcs) -> u8 {
168 root_arcs.0
169 }
170}