Coverage Report

Created: 2026-08-14 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}