/rust/registry/src/index.crates.io-1949cf8c6b5b557f/const-oid-0.10.2/src/lib.rs
Line | Count | Source |
1 | | #![no_std] |
2 | | #![cfg_attr(docsrs, feature(doc_cfg))] |
3 | | #![doc = include_str!("../README.md")] |
4 | | #![doc( |
5 | | html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", |
6 | | html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" |
7 | | )] |
8 | | #![allow(clippy::len_without_is_empty)] |
9 | | #![deny(unsafe_code)] |
10 | | #![warn( |
11 | | clippy::arithmetic_side_effects, |
12 | | clippy::mod_module_files, |
13 | | clippy::panic, |
14 | | clippy::panic_in_result_fn, |
15 | | clippy::unwrap_used, |
16 | | missing_docs, |
17 | | rust_2018_idioms, |
18 | | unused_lifetimes, |
19 | | unused_qualifications |
20 | | )] |
21 | | |
22 | | #[macro_use] |
23 | | mod checked; |
24 | | |
25 | | mod arcs; |
26 | | mod buffer; |
27 | | mod encoder; |
28 | | mod error; |
29 | | mod parser; |
30 | | mod traits; |
31 | | |
32 | | #[cfg(feature = "db")] |
33 | | pub mod db; |
34 | | |
35 | | pub use crate::{ |
36 | | arcs::{Arc, Arcs}, |
37 | | buffer::Buffer, |
38 | | error::{Error, Result}, |
39 | | traits::{AssociatedOid, DynAssociatedOid}, |
40 | | }; |
41 | | |
42 | | use crate::encoder::Encoder; |
43 | | use core::{borrow::Borrow, fmt, ops::Deref, str::FromStr}; |
44 | | |
45 | | /// Default maximum size. |
46 | | /// |
47 | | /// Makes `ObjectIdentifier` 40-bytes total w\ 1-byte length. |
48 | | const DEFAULT_MAX_SIZE: usize = 39; |
49 | | |
50 | | /// Object identifier (OID). |
51 | | /// |
52 | | /// OIDs are hierarchical structures consisting of "arcs", i.e. integer |
53 | | /// identifiers. |
54 | | /// |
55 | | /// # Validity |
56 | | /// |
57 | | /// In order for an OID to be considered valid by this library, it must meet |
58 | | /// the following criteria: |
59 | | /// |
60 | | /// - The OID MUST have at least 3 arcs |
61 | | /// - The first arc MUST be within the range 0-2 |
62 | | /// - The second arc MUST be within the range 0-39 |
63 | | /// - The BER/DER encoding of the OID MUST be shorter than |
64 | | /// [`ObjectIdentifier::MAX_SIZE`] |
65 | | #[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)] |
66 | | pub struct ObjectIdentifier<const MAX_SIZE: usize = DEFAULT_MAX_SIZE> { |
67 | | /// Buffer containing BER/DER-serialized bytes (sans ASN.1 tag/length) |
68 | | ber: Buffer<MAX_SIZE>, |
69 | | } |
70 | | |
71 | | impl ObjectIdentifier { |
72 | | /// Maximum size of a BER/DER-encoded OID in bytes. |
73 | | pub const MAX_SIZE: usize = DEFAULT_MAX_SIZE; |
74 | | |
75 | | /// Parse an [`ObjectIdentifier`] from the dot-delimited string form, |
76 | | /// panicking on parse errors. |
77 | | /// |
78 | | /// This function exists as a workaround for `unwrap` not yet being |
79 | | /// stable in `const fn` contexts, and is intended to allow the result to |
80 | | /// be bound to a constant value: |
81 | | /// |
82 | | /// ``` |
83 | | /// use const_oid::ObjectIdentifier; |
84 | | /// |
85 | | /// pub const MY_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1"); |
86 | | /// ``` |
87 | | /// |
88 | | /// In future versions of Rust it should be possible to replace this with |
89 | | /// `ObjectIdentifier::new(...).unwrap()`. |
90 | | /// |
91 | | /// Use [`ObjectIdentifier::new`] for fallible parsing. |
92 | | // TODO(tarcieri): remove this when `Result::unwrap` is `const fn` |
93 | 0 | pub const fn new_unwrap(s: &str) -> Self { |
94 | 0 | match Self::new(s) { |
95 | 0 | Ok(oid) => oid, |
96 | 0 | Err(err) => err.panic(), |
97 | | } |
98 | 0 | } |
99 | | |
100 | | /// Parse an [`ObjectIdentifier`] from the dot-delimited string form. |
101 | 0 | pub const fn new(s: &str) -> Result<Self> { |
102 | | // TODO(tarcieri): use `?` when stable in `const fn` |
103 | 0 | match parser::Parser::parse(s) { |
104 | 0 | Ok(parser) => parser.finish(), |
105 | 0 | Err(err) => Err(err), |
106 | | } |
107 | 0 | } |
108 | | |
109 | | /// Parse an OID from a slice of [`Arc`] values (i.e. integers). |
110 | 0 | pub fn from_arcs(arcs: impl IntoIterator<Item = Arc>) -> Result<Self> { |
111 | 0 | let mut encoder = Encoder::new(); |
112 | | |
113 | 0 | for arc in arcs { |
114 | 0 | encoder = encoder.arc(arc)?; |
115 | | } |
116 | | |
117 | 0 | encoder.finish() |
118 | 0 | } |
119 | | |
120 | | /// Parse an OID from from its BER/DER encoding. |
121 | 0 | pub fn from_bytes(ber_bytes: &[u8]) -> Result<Self> { |
122 | 0 | Self::from_bytes_sized(ber_bytes) |
123 | 0 | } |
124 | | } |
125 | | |
126 | | impl<const MAX_SIZE: usize> ObjectIdentifier<MAX_SIZE> { |
127 | | /// Parse an OID from from its BER/DER encoding. |
128 | | /// |
129 | | /// Returns `Err(Error::Length)` if bytes do not fit in `MAX_SIZE`. |
130 | 0 | pub fn from_bytes_sized(ber_bytes: &[u8]) -> Result<Self> { |
131 | 0 | ObjectIdentifierRef::from_bytes(ber_bytes)?.try_into() |
132 | 0 | } |
133 | | |
134 | | /// Get the BER/DER serialization of this OID as bytes. |
135 | | /// |
136 | | /// Note that this encoding omits the ASN.1 tag/length, and only contains the value portion of |
137 | | /// the encoded OID. |
138 | 0 | pub const fn as_bytes(&self) -> &[u8] { |
139 | 0 | self.ber.as_bytes() |
140 | 0 | } |
141 | | |
142 | | /// Borrow an [`ObjectIdentifierRef`] which corresponds to this [`ObjectIdentifier`]. |
143 | 0 | pub const fn as_oid_ref(&self) -> &ObjectIdentifierRef { |
144 | 0 | ObjectIdentifierRef::from_bytes_unchecked(self.as_bytes()) |
145 | 0 | } |
146 | | |
147 | | /// Get the parent OID of this one (if applicable). |
148 | 0 | pub fn parent(&self) -> Option<Self> { |
149 | 0 | let num_arcs = self.len().checked_sub(1)?; |
150 | 0 | let mut encoder = Encoder::new(); |
151 | | |
152 | 0 | for arc in self.arcs().take(num_arcs) { |
153 | 0 | encoder = encoder.arc(arc).ok()?; |
154 | | } |
155 | | |
156 | 0 | encoder.finish().ok() |
157 | 0 | } |
158 | | |
159 | | /// Push an additional arc onto this OID, returning the child OID. |
160 | 0 | pub const fn push_arc(self, arc: Arc) -> Result<Self> { |
161 | | // TODO(tarcieri): use `?` when stable in `const fn` |
162 | 0 | match Encoder::extend(self).arc(arc) { |
163 | 0 | Ok(encoder) => encoder.finish(), |
164 | 0 | Err(err) => Err(err), |
165 | | } |
166 | 0 | } |
167 | | |
168 | | /// Does this OID start with the other OID? |
169 | 0 | pub const fn starts_with<const SIZE: usize>(&self, other: ObjectIdentifier<SIZE>) -> bool { |
170 | 0 | let len = other.as_bytes().len(); |
171 | | |
172 | 0 | if self.as_bytes().len() < len { |
173 | 0 | return false; |
174 | 0 | } |
175 | | |
176 | 0 | let mut i = 0; |
177 | 0 | while i < len { |
178 | 0 | if self.as_bytes()[i] != other.as_bytes()[i] { |
179 | 0 | return false; |
180 | 0 | } |
181 | | |
182 | 0 | match i.checked_add(1) { |
183 | 0 | Some(succ) => i = succ, |
184 | 0 | None => return false, |
185 | | } |
186 | | } |
187 | | |
188 | 0 | true |
189 | 0 | } |
190 | | } |
191 | | |
192 | | impl<const MAX_SIZE: usize> AsRef<[u8]> for ObjectIdentifier<MAX_SIZE> { |
193 | 0 | fn as_ref(&self) -> &[u8] { |
194 | 0 | self.as_bytes() |
195 | 0 | } |
196 | | } |
197 | | |
198 | | impl<const MAX_SIZE: usize> AsRef<ObjectIdentifierRef> for ObjectIdentifier<MAX_SIZE> { |
199 | 0 | fn as_ref(&self) -> &ObjectIdentifierRef { |
200 | 0 | self.as_oid_ref() |
201 | 0 | } |
202 | | } |
203 | | |
204 | | impl<const MAX_SIZE: usize> Borrow<ObjectIdentifierRef> for ObjectIdentifier<MAX_SIZE> { |
205 | 0 | fn borrow(&self) -> &ObjectIdentifierRef { |
206 | 0 | self.as_oid_ref() |
207 | 0 | } |
208 | | } |
209 | | |
210 | | impl<const MAX_SIZE: usize> Deref for ObjectIdentifier<MAX_SIZE> { |
211 | | type Target = ObjectIdentifierRef; |
212 | | |
213 | 0 | fn deref(&self) -> &ObjectIdentifierRef { |
214 | 0 | self.as_oid_ref() |
215 | 0 | } |
216 | | } |
217 | | |
218 | | impl FromStr for ObjectIdentifier { |
219 | | type Err = Error; |
220 | | |
221 | 0 | fn from_str(string: &str) -> Result<Self> { |
222 | 0 | Self::new(string) |
223 | 0 | } |
224 | | } |
225 | | |
226 | | impl TryFrom<&[u8]> for ObjectIdentifier { |
227 | | type Error = Error; |
228 | | |
229 | 0 | fn try_from(ber_bytes: &[u8]) -> Result<Self> { |
230 | 0 | Self::from_bytes(ber_bytes) |
231 | 0 | } |
232 | | } |
233 | | |
234 | | impl<const MAX_SIZE: usize> TryFrom<&ObjectIdentifierRef> for ObjectIdentifier<MAX_SIZE> { |
235 | | type Error = Error; |
236 | | |
237 | 0 | fn try_from(oid_ref: &ObjectIdentifierRef) -> Result<Self> { |
238 | 0 | let len = oid_ref.as_bytes().len(); |
239 | | |
240 | 0 | if len > MAX_SIZE { |
241 | 0 | return Err(Error::Length); |
242 | 0 | } |
243 | | |
244 | 0 | let mut bytes = [0u8; MAX_SIZE]; |
245 | 0 | bytes[..len].copy_from_slice(oid_ref.as_bytes()); |
246 | | |
247 | 0 | let ber = Buffer { |
248 | 0 | bytes, |
249 | 0 | length: len as u8, |
250 | 0 | }; |
251 | | |
252 | 0 | Ok(Self { ber }) |
253 | 0 | } |
254 | | } |
255 | | |
256 | | impl<const MAX_SIZE: usize> fmt::Debug for ObjectIdentifier<MAX_SIZE> { |
257 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
258 | 0 | write!(f, "ObjectIdentifier({self})") |
259 | 0 | } |
260 | | } |
261 | | |
262 | | impl<const MAX_SIZE: usize> fmt::Display for ObjectIdentifier<MAX_SIZE> { |
263 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
264 | 0 | write!(f, "{}", self.as_oid_ref()) |
265 | 0 | } |
266 | | } |
267 | | |
268 | | // Implement by hand because the derive would create invalid values. |
269 | | // Use the constructor to create a valid oid with at least 3 arcs. |
270 | | #[cfg(feature = "arbitrary")] |
271 | | impl<'a> arbitrary::Arbitrary<'a> for ObjectIdentifier { |
272 | | fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { |
273 | | let first = u.int_in_range(0..=arcs::ARC_MAX_FIRST)?; |
274 | | let second = u.int_in_range(0..=arcs::ARC_MAX_SECOND)?; |
275 | | let third = u.arbitrary()?; |
276 | | |
277 | | let mut oid = Self::from_arcs([first, second, third]) |
278 | | .map_err(|_| arbitrary::Error::IncorrectFormat)?; |
279 | | |
280 | | for arc in u.arbitrary_iter()? { |
281 | | oid = oid |
282 | | .push_arc(arc?) |
283 | | .map_err(|_| arbitrary::Error::IncorrectFormat)?; |
284 | | } |
285 | | |
286 | | Ok(oid) |
287 | | } |
288 | | |
289 | | fn size_hint(depth: usize) -> (usize, Option<usize>) { |
290 | | (Arc::size_hint(depth).0.saturating_mul(3), None) |
291 | | } |
292 | | } |
293 | | |
294 | | /// OID reference type: wrapper for the BER serialization. |
295 | | #[derive(Eq, Hash, PartialEq, PartialOrd, Ord)] |
296 | | #[repr(transparent)] |
297 | | pub struct ObjectIdentifierRef { |
298 | | /// BER/DER-serialized bytes (sans ASN.1 tag/length). |
299 | | ber: [u8], |
300 | | } |
301 | | |
302 | | impl ObjectIdentifierRef { |
303 | | /// Create an [`ObjectIdentifierRef`], validating that the provided byte slice contains a valid |
304 | | /// BER/DER encoding. |
305 | | // TODO(tarcieri): `const fn` support |
306 | 0 | pub fn from_bytes(ber: &[u8]) -> Result<&Self> { |
307 | | // Ensure arcs are well-formed |
308 | 0 | let mut arcs = Arcs::new(ber); |
309 | 0 | while arcs.try_next()?.is_some() {} |
310 | 0 | Ok(Self::from_bytes_unchecked(ber)) |
311 | 0 | } |
312 | | |
313 | | /// Create an [`ObjectIdentifierRef`] from the given byte slice without first checking that it |
314 | | /// contains valid BER/DER. |
315 | 0 | pub(crate) const fn from_bytes_unchecked(ber: &[u8]) -> &Self { |
316 | 0 | debug_assert!(!ber.is_empty()); |
317 | | |
318 | | // SAFETY: `ObjectIdentifierRef` is a `repr(transparent)` newtype for `[u8]`. |
319 | | #[allow(unsafe_code)] |
320 | | unsafe { |
321 | 0 | &*(ber as *const [u8] as *const ObjectIdentifierRef) |
322 | | } |
323 | 0 | } |
324 | | |
325 | | /// Get the BER/DER serialization of this OID as bytes. |
326 | | /// |
327 | | /// Note that this encoding omits the ASN.1 tag/length, and only contains the value portion of |
328 | | /// the encoded OID. |
329 | 0 | pub const fn as_bytes(&self) -> &[u8] { |
330 | 0 | &self.ber |
331 | 0 | } |
332 | | |
333 | | /// Return the arc with the given index, if it exists. |
334 | 0 | pub fn arc(&self, index: usize) -> Option<Arc> { |
335 | 0 | self.arcs().nth(index) |
336 | 0 | } |
337 | | |
338 | | /// Iterate over the arcs (a.k.a. nodes) of an [`ObjectIdentifier`]. |
339 | | /// |
340 | | /// Returns [`Arcs`], an iterator over [`Arc`] values. |
341 | 0 | pub fn arcs(&self) -> Arcs<'_> { |
342 | 0 | Arcs::new(self.ber.as_ref()) |
343 | 0 | } |
344 | | |
345 | | /// Get the length of this [`ObjectIdentifier`] in arcs. |
346 | 0 | pub fn len(&self) -> usize { |
347 | 0 | self.arcs().count() |
348 | 0 | } |
349 | | } |
350 | | |
351 | | impl AsRef<[u8]> for ObjectIdentifierRef { |
352 | 0 | fn as_ref(&self) -> &[u8] { |
353 | 0 | self.as_bytes() |
354 | 0 | } |
355 | | } |
356 | | |
357 | | impl<'a, const MAX_SIZE: usize> From<&'a ObjectIdentifier<MAX_SIZE>> for &'a ObjectIdentifierRef { |
358 | 0 | fn from(oid: &'a ObjectIdentifier<MAX_SIZE>) -> &'a ObjectIdentifierRef { |
359 | 0 | oid.as_oid_ref() |
360 | 0 | } |
361 | | } |
362 | | |
363 | | impl<'a> TryFrom<&'a [u8]> for &'a ObjectIdentifierRef { |
364 | | type Error = Error; |
365 | | |
366 | 0 | fn try_from(ber_bytes: &'a [u8]) -> Result<Self> { |
367 | 0 | ObjectIdentifierRef::from_bytes(ber_bytes) |
368 | 0 | } |
369 | | } |
370 | | |
371 | | impl fmt::Debug for ObjectIdentifierRef { |
372 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
373 | 0 | write!(f, "ObjectIdentifierRef({self})") |
374 | 0 | } |
375 | | } |
376 | | |
377 | | impl fmt::Display for ObjectIdentifierRef { |
378 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
379 | 0 | let len = self.arcs().count(); |
380 | | |
381 | 0 | for (i, arc) in self.arcs().enumerate() { |
382 | 0 | write!(f, "{arc}")?; |
383 | | |
384 | 0 | if let Some(j) = i.checked_add(1) { |
385 | 0 | if j < len { |
386 | 0 | write!(f, ".")?; |
387 | 0 | } |
388 | 0 | } |
389 | | } |
390 | | |
391 | 0 | Ok(()) |
392 | 0 | } |
393 | | } |
394 | | |
395 | | impl<const MAX_SIZE: usize> PartialEq<ObjectIdentifier<MAX_SIZE>> for ObjectIdentifierRef { |
396 | 0 | fn eq(&self, other: &ObjectIdentifier<MAX_SIZE>) -> bool { |
397 | 0 | self.as_bytes().eq(other.as_bytes()) |
398 | 0 | } |
399 | | } |