Coverage Report

Created: 2025-10-12 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/const-oid-0.9.6/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
#![forbid(unsafe_code)]
9
#![warn(
10
    clippy::integer_arithmetic,
11
    clippy::panic,
12
    clippy::panic_in_result_fn,
13
    clippy::unwrap_used,
14
    missing_docs,
15
    rust_2018_idioms,
16
    unused_lifetimes,
17
    unused_qualifications
18
)]
19
20
#[cfg(feature = "std")]
21
extern crate std;
22
23
#[macro_use]
24
mod checked;
25
26
mod arcs;
27
mod encoder;
28
mod error;
29
mod parser;
30
31
#[cfg(feature = "db")]
32
#[cfg_attr(docsrs, doc(cfg(feature = "db")))]
33
pub mod db;
34
35
pub use crate::{
36
    arcs::{Arc, Arcs},
37
    error::{Error, Result},
38
};
39
40
use crate::encoder::Encoder;
41
use core::{fmt, str::FromStr};
42
43
/// A trait which associates an OID with a type.
44
pub trait AssociatedOid {
45
    /// The OID associated with this type.
46
    const OID: ObjectIdentifier;
47
}
48
49
/// A trait which associates a dynamic, `&self`-dependent OID with a type,
50
/// which may change depending on the type's value.
51
///
52
/// This trait is object safe and auto-impl'd for any types which impl
53
/// [`AssociatedOid`].
54
pub trait DynAssociatedOid {
55
    /// Get the OID associated with this value.
56
    fn oid(&self) -> ObjectIdentifier;
57
}
58
59
impl<T: AssociatedOid> DynAssociatedOid for T {
60
0
    fn oid(&self) -> ObjectIdentifier {
61
0
        T::OID
62
0
    }
63
}
64
65
/// Object identifier (OID).
66
///
67
/// OIDs are hierarchical structures consisting of "arcs", i.e. integer
68
/// identifiers.
69
///
70
/// # Validity
71
///
72
/// In order for an OID to be considered valid by this library, it must meet
73
/// the following criteria:
74
///
75
/// - The OID MUST have at least 3 arcs
76
/// - The first arc MUST be within the range 0-2
77
/// - The second arc MUST be within the range 0-39
78
/// - The BER/DER encoding of the OID MUST be shorter than
79
///   [`ObjectIdentifier::MAX_SIZE`]
80
#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
81
pub struct ObjectIdentifier {
82
    /// Length in bytes
83
    length: u8,
84
85
    /// Array containing BER/DER-serialized bytes (no header)
86
    bytes: [u8; Self::MAX_SIZE],
87
}
88
89
#[allow(clippy::len_without_is_empty)]
90
impl ObjectIdentifier {
91
    /// Maximum size of a BER/DER-encoded OID in bytes.
92
    pub const MAX_SIZE: usize = 39; // makes `ObjectIdentifier` 40-bytes total w\ 1-byte length
93
94
    /// Parse an [`ObjectIdentifier`] from the dot-delimited string form,
95
    /// panicking on parse errors.
96
    ///
97
    /// This function exists as a workaround for `unwrap` not yet being
98
    /// stable in `const fn` contexts, and is intended to allow the result to
99
    /// be bound to a constant value:
100
    ///
101
    /// ```
102
    /// use const_oid::ObjectIdentifier;
103
    ///
104
    /// pub const MY_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1");
105
    /// ```
106
    ///
107
    /// In future versions of Rust it should be possible to replace this with
108
    /// `ObjectIdentifier::new(...).unwrap()`.
109
    ///
110
    /// Use [`ObjectIdentifier::new`] for fallible parsing.
111
    // TODO(tarcieri): remove this when `Result::unwrap` is `const fn`
112
0
    pub const fn new_unwrap(s: &str) -> Self {
113
0
        match Self::new(s) {
114
0
            Ok(oid) => oid,
115
0
            Err(err) => err.panic(),
116
        }
117
0
    }
118
119
    /// Parse an [`ObjectIdentifier`] from the dot-delimited string form.
120
0
    pub const fn new(s: &str) -> Result<Self> {
121
        // TODO(tarcieri): use `?` when stable in `const fn`
122
0
        match parser::Parser::parse(s) {
123
0
            Ok(parser) => parser.finish(),
124
0
            Err(err) => Err(err),
125
        }
126
0
    }
127
128
    /// Parse an OID from a slice of [`Arc`] values (i.e. integers).
129
0
    pub fn from_arcs(arcs: impl IntoIterator<Item = Arc>) -> Result<Self> {
130
0
        let mut encoder = Encoder::new();
131
132
0
        for arc in arcs {
133
0
            encoder = encoder.arc(arc)?;
134
        }
135
136
0
        encoder.finish()
137
0
    }
138
139
    /// Parse an OID from from its BER/DER encoding.
140
31.0k
    pub fn from_bytes(ber_bytes: &[u8]) -> Result<Self> {
141
31.0k
        let len = ber_bytes.len();
142
143
31.0k
        match len {
144
2
            0 => return Err(Error::Empty),
145
31.0k
            3..=Self::MAX_SIZE => (),
146
3
            _ => return Err(Error::NotEnoughArcs),
147
        }
148
31.0k
        let mut bytes = [0u8; Self::MAX_SIZE];
149
31.0k
        bytes[..len].copy_from_slice(ber_bytes);
150
151
31.0k
        let oid = Self {
152
31.0k
            bytes,
153
31.0k
            length: len as u8,
154
31.0k
        };
155
156
        // Ensure arcs are well-formed
157
31.0k
        let mut arcs = oid.arcs();
158
212k
        while arcs.try_next()?.is_some() {}
159
160
30.9k
        Ok(oid)
161
31.0k
    }
162
163
    /// Get the BER/DER serialization of this OID as bytes.
164
    ///
165
    /// Note that this encoding omits the tag/length, and only contains the
166
    /// value portion of the encoded OID.
167
239k
    pub fn as_bytes(&self) -> &[u8] {
168
239k
        &self.bytes[..self.length as usize]
169
239k
    }
170
171
    /// Return the arc with the given index, if it exists.
172
0
    pub fn arc(&self, index: usize) -> Option<Arc> {
173
0
        self.arcs().nth(index)
174
0
    }
175
176
    /// Iterate over the arcs (a.k.a. nodes) of an [`ObjectIdentifier`].
177
    ///
178
    /// Returns [`Arcs`], an iterator over [`Arc`] values.
179
31.0k
    pub fn arcs(&self) -> Arcs<'_> {
180
31.0k
        Arcs::new(self)
181
31.0k
    }
182
183
    /// Get the length of this [`ObjectIdentifier`] in arcs.
184
0
    pub fn len(&self) -> usize {
185
0
        self.arcs().count()
186
0
    }
187
188
    /// Get the parent OID of this one (if applicable).
189
0
    pub fn parent(&self) -> Option<Self> {
190
0
        let num_arcs = self.len().checked_sub(1)?;
191
0
        Self::from_arcs(self.arcs().take(num_arcs)).ok()
192
0
    }
193
194
    /// Push an additional arc onto this OID, returning the child OID.
195
0
    pub const fn push_arc(self, arc: Arc) -> Result<Self> {
196
        // TODO(tarcieri): use `?` when stable in `const fn`
197
0
        match Encoder::extend(self).arc(arc) {
198
0
            Ok(encoder) => encoder.finish(),
199
0
            Err(err) => Err(err),
200
        }
201
0
    }
202
}
203
204
impl AsRef<[u8]> for ObjectIdentifier {
205
0
    fn as_ref(&self) -> &[u8] {
206
0
        self.as_bytes()
207
0
    }
208
}
209
210
impl FromStr for ObjectIdentifier {
211
    type Err = Error;
212
213
0
    fn from_str(string: &str) -> Result<Self> {
214
0
        Self::new(string)
215
0
    }
216
}
217
218
impl TryFrom<&[u8]> for ObjectIdentifier {
219
    type Error = Error;
220
221
0
    fn try_from(ber_bytes: &[u8]) -> Result<Self> {
222
0
        Self::from_bytes(ber_bytes)
223
0
    }
224
}
225
226
impl From<&ObjectIdentifier> for ObjectIdentifier {
227
0
    fn from(oid: &ObjectIdentifier) -> ObjectIdentifier {
228
0
        *oid
229
0
    }
230
}
231
232
impl fmt::Debug for ObjectIdentifier {
233
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234
0
        write!(f, "ObjectIdentifier({})", self)
235
0
    }
236
}
237
238
impl fmt::Display for ObjectIdentifier {
239
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240
0
        let len = self.arcs().count();
241
242
0
        for (i, arc) in self.arcs().enumerate() {
243
0
            write!(f, "{}", arc)?;
244
245
0
            if let Some(j) = i.checked_add(1) {
246
0
                if j < len {
247
0
                    write!(f, ".")?;
248
0
                }
249
0
            }
250
        }
251
252
0
        Ok(())
253
0
    }
254
}
255
256
// Implement by hand because the derive would create invalid values.
257
// Use the constructor to create a valid oid with at least 3 arcs.
258
#[cfg(feature = "arbitrary")]
259
impl<'a> arbitrary::Arbitrary<'a> for ObjectIdentifier {
260
    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
261
        let first = u.int_in_range(0..=arcs::ARC_MAX_FIRST)?;
262
        let second = u.int_in_range(0..=arcs::ARC_MAX_SECOND)?;
263
        let third = u.arbitrary()?;
264
265
        let mut oid = Self::from_arcs([first, second, third])
266
            .map_err(|_| arbitrary::Error::IncorrectFormat)?;
267
268
        for arc in u.arbitrary_iter()? {
269
            oid = oid
270
                .push_arc(arc?)
271
                .map_err(|_| arbitrary::Error::IncorrectFormat)?;
272
        }
273
274
        Ok(oid)
275
    }
276
277
    fn size_hint(depth: usize) -> (usize, Option<usize>) {
278
        (Arc::size_hint(depth).0.saturating_mul(3), None)
279
    }
280
}