chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

170
vendor/const-oid/src/arcs.rs vendored Normal file
View File

@@ -0,0 +1,170 @@
//! Arcs are integer values which exist within an OID's hierarchy.
use crate::{Error, ObjectIdentifier, Result};
use core::mem;
/// Type alias used to represent an "arc" (i.e. integer identifier value).
///
/// X.660 does not define a maximum size of an arc.
///
/// The current representation is `u32`, which has been selected as being
/// sufficient to cover the current PKCS/PKIX use cases this library has been
/// used in conjunction with.
///
/// Future versions may potentially make it larger if a sufficiently important
/// use case is discovered.
pub type Arc = u32;
/// Maximum value of the first arc in an OID.
pub(crate) const ARC_MAX_FIRST: Arc = 2;
/// Maximum value of the second arc in an OID.
pub(crate) const ARC_MAX_SECOND: Arc = 39;
/// Maximum number of bytes supported in an arc.
const ARC_MAX_BYTES: usize = mem::size_of::<Arc>();
/// Maximum value of the last byte in an arc.
const ARC_MAX_LAST_OCTET: u8 = 0b11110000; // Max bytes of leading 1-bits
/// [`Iterator`] over [`Arc`] values (a.k.a. nodes) in an [`ObjectIdentifier`].
///
/// This iterates over all arcs in an OID, including the root.
pub struct Arcs<'a> {
/// OID we're iterating over
oid: &'a ObjectIdentifier,
/// Current position within the serialized DER bytes of this OID
cursor: Option<usize>,
}
impl<'a> Arcs<'a> {
/// Create a new iterator over the arcs of this OID
pub(crate) fn new(oid: &'a ObjectIdentifier) -> Self {
Self { oid, cursor: None }
}
/// Try to parse the next arc in this OID.
///
/// This method is fallible so it can be used as a first pass to determine
/// that the arcs in the OID are well-formed.
pub(crate) fn try_next(&mut self) -> Result<Option<Arc>> {
match self.cursor {
// Indicates we're on the root OID
None => {
let root = RootArcs::try_from(self.oid.as_bytes()[0])?;
self.cursor = Some(0);
Ok(Some(root.first_arc()))
}
Some(0) => {
let root = RootArcs::try_from(self.oid.as_bytes()[0])?;
self.cursor = Some(1);
Ok(Some(root.second_arc()))
}
Some(offset) => {
let mut result = 0;
let mut arc_bytes = 0;
loop {
let len = checked_add!(offset, arc_bytes);
match self.oid.as_bytes().get(len).cloned() {
// The arithmetic below includes advance checks
// against `ARC_MAX_BYTES` and `ARC_MAX_LAST_OCTET`
// which ensure the operations will not overflow.
#[allow(clippy::integer_arithmetic)]
Some(byte) => {
arc_bytes = checked_add!(arc_bytes, 1);
if (arc_bytes > ARC_MAX_BYTES) && (byte & ARC_MAX_LAST_OCTET != 0) {
return Err(Error::ArcTooBig);
}
result = result << 7 | (byte & 0b1111111) as Arc;
if byte & 0b10000000 == 0 {
self.cursor = Some(checked_add!(offset, arc_bytes));
return Ok(Some(result));
}
}
None => {
if arc_bytes == 0 {
return Ok(None);
} else {
return Err(Error::Base128);
}
}
}
}
}
}
}
}
impl<'a> Iterator for Arcs<'a> {
type Item = Arc;
fn next(&mut self) -> Option<Arc> {
// ObjectIdentifier constructors should ensure the OID is well-formed
self.try_next().expect("OID malformed")
}
}
/// Byte containing the first and second arcs of an OID.
///
/// This is represented this way in order to reduce the overall size of the
/// [`ObjectIdentifier`] struct.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct RootArcs(u8);
impl RootArcs {
/// Create [`RootArcs`] from the first and second arc values represented
/// as `Arc` integers.
pub(crate) const fn new(first_arc: Arc, second_arc: Arc) -> Result<Self> {
if first_arc > ARC_MAX_FIRST {
return Err(Error::ArcInvalid { arc: first_arc });
}
if second_arc > ARC_MAX_SECOND {
return Err(Error::ArcInvalid { arc: second_arc });
}
// The checks above ensure this operation will not overflow
#[allow(clippy::integer_arithmetic)]
let byte = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + second_arc as u8;
Ok(Self(byte))
}
/// Get the value of the first arc
#[allow(clippy::integer_arithmetic)]
pub(crate) const fn first_arc(self) -> Arc {
self.0 as Arc / (ARC_MAX_SECOND + 1)
}
/// Get the value of the second arc
#[allow(clippy::integer_arithmetic)]
pub(crate) const fn second_arc(self) -> Arc {
self.0 as Arc % (ARC_MAX_SECOND + 1)
}
}
impl TryFrom<u8> for RootArcs {
type Error = Error;
// Ensured not to overflow by constructor invariants
#[allow(clippy::integer_arithmetic)]
fn try_from(octet: u8) -> Result<Self> {
let first = octet as Arc / (ARC_MAX_SECOND + 1);
let second = octet as Arc % (ARC_MAX_SECOND + 1);
let result = Self::new(first, second)?;
debug_assert_eq!(octet, result.0);
Ok(result)
}
}
impl From<RootArcs> for u8 {
fn from(root_arcs: RootArcs) -> u8 {
root_arcs.0
}
}

11
vendor/const-oid/src/checked.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
//! Checked arithmetic helpers.
/// `const fn`-friendly checked addition helper.
macro_rules! checked_add {
($a:expr, $b:expr) => {
match $a.checked_add($b) {
Some(n) => n,
None => return Err(Error::Length),
}
};
}

164
vendor/const-oid/src/db.rs vendored Normal file
View File

@@ -0,0 +1,164 @@
//! OID Names Database
//!
//! The contents of this database are generated from the official IANA
//! [Object Identifier Descriptors] Registry CSV file and from [RFC 5280].
//! If we are missing values you care about, please contribute a patch to
//! `oiddbgen` (a subcrate in the source code) to generate the values from
//! the relevant standard.
//!
//! [RFC 5280]: https://datatracker.ietf.org/doc/html/rfc5280
//! [Object Identifier Descriptors]: https://www.iana.org/assignments/ldap-parameters/ldap-parameters.xhtml#ldap-parameters-3
#![allow(clippy::integer_arithmetic, missing_docs)]
mod gen;
pub use gen::*;
use crate::{Error, ObjectIdentifier};
/// A const implementation of byte equals.
const fn eq(lhs: &[u8], rhs: &[u8]) -> bool {
if lhs.len() != rhs.len() {
return false;
}
let mut i = 0usize;
while i < lhs.len() {
if lhs[i] != rhs[i] {
return false;
}
i += 1;
}
true
}
/// A const implementation of case-insensitive ASCII equals.
const fn eq_case(lhs: &[u8], rhs: &[u8]) -> bool {
if lhs.len() != rhs.len() {
return false;
}
let mut i = 0usize;
while i < lhs.len() {
if !lhs[i].eq_ignore_ascii_case(&rhs[i]) {
return false;
}
i += 1;
}
true
}
/// A query interface for OIDs/Names.
#[derive(Copy, Clone)]
pub struct Database<'a>(&'a [(&'a ObjectIdentifier, &'a str)]);
impl<'a> Database<'a> {
/// Looks up a name for an OID.
///
/// Errors if the input is not a valid OID.
/// Returns the input if no name is found.
pub fn resolve<'b>(&self, oid: &'b str) -> Result<&'b str, Error>
where
'a: 'b,
{
Ok(self.by_oid(&oid.parse()?).unwrap_or(oid))
}
/// Finds a named oid by its associated OID.
pub const fn by_oid(&self, oid: &ObjectIdentifier) -> Option<&'a str> {
let mut i = 0;
while i < self.0.len() {
let lhs = self.0[i].0;
if lhs.length == oid.length && eq(&lhs.bytes, &oid.bytes) {
return Some(self.0[i].1);
}
i += 1;
}
None
}
/// Finds a named oid by its associated name.
pub const fn by_name(&self, name: &str) -> Option<&'a ObjectIdentifier> {
let mut i = 0;
while i < self.0.len() {
let lhs = self.0[i].1;
if eq_case(lhs.as_bytes(), name.as_bytes()) {
return Some(self.0[i].0);
}
i += 1;
}
None
}
/// Return the list of matched name for the OID.
pub const fn find_names_for_oid(&self, oid: ObjectIdentifier) -> Names<'a> {
Names {
database: *self,
oid,
position: 0,
}
}
}
/// Iterator returning the multiple names that may be associated with an OID.
pub struct Names<'a> {
database: Database<'a>,
oid: ObjectIdentifier,
position: usize,
}
impl<'a> Iterator for Names<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
let mut i = self.position;
while i < self.database.0.len() {
let lhs = self.database.0[i].0;
if lhs.as_bytes().eq(self.oid.as_bytes()) {
self.position = i + 1;
return Some(self.database.0[i].1);
}
i += 1;
}
None
}
}
#[cfg(test)]
mod tests {
use crate::ObjectIdentifier;
use super::rfc4519::CN;
#[test]
fn by_oid() {
let cn = super::DB.by_oid(&CN).expect("cn not found");
assert_eq!("cn", cn);
let none = ObjectIdentifier::new_unwrap("0.1.2.3.4.5.6.7.8.9");
assert_eq!(None, super::DB.by_oid(&none));
}
#[test]
fn by_name() {
let cn = super::DB.by_name("CN").expect("cn not found");
assert_eq!(&CN, cn);
assert_eq!(None, super::DB.by_name("purplePeopleEater"));
}
}

4248
vendor/const-oid/src/db/gen.rs vendored Normal file

File diff suppressed because it is too large Load Diff

165
vendor/const-oid/src/encoder.rs vendored Normal file
View File

@@ -0,0 +1,165 @@
//! OID encoder with `const` support.
use crate::{
arcs::{ARC_MAX_FIRST, ARC_MAX_SECOND},
Arc, Error, ObjectIdentifier, Result,
};
/// BER/DER encoder
#[derive(Debug)]
pub(crate) struct Encoder {
/// Current state
state: State,
/// Bytes of the OID being encoded in-progress
bytes: [u8; ObjectIdentifier::MAX_SIZE],
/// Current position within the byte buffer
cursor: usize,
}
/// Current state of the encoder
#[derive(Debug)]
enum State {
/// Initial state - no arcs yet encoded
Initial,
/// First arc parsed
FirstArc(Arc),
/// Encoding base 128 body of the OID
Body,
}
impl Encoder {
/// Create a new encoder initialized to an empty default state.
pub(crate) const fn new() -> Self {
Self {
state: State::Initial,
bytes: [0u8; ObjectIdentifier::MAX_SIZE],
cursor: 0,
}
}
/// Extend an existing OID.
pub(crate) const fn extend(oid: ObjectIdentifier) -> Self {
Self {
state: State::Body,
bytes: oid.bytes,
cursor: oid.length as usize,
}
}
/// Encode an [`Arc`] as base 128 into the internal buffer.
pub(crate) const fn arc(mut self, arc: Arc) -> Result<Self> {
match self.state {
State::Initial => {
if arc > ARC_MAX_FIRST {
return Err(Error::ArcInvalid { arc });
}
self.state = State::FirstArc(arc);
Ok(self)
}
// Ensured not to overflow by `ARC_MAX_SECOND` check
#[allow(clippy::integer_arithmetic)]
State::FirstArc(first_arc) => {
if arc > ARC_MAX_SECOND {
return Err(Error::ArcInvalid { arc });
}
self.state = State::Body;
self.bytes[0] = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + arc as u8;
self.cursor = 1;
Ok(self)
}
// TODO(tarcieri): finer-grained overflow safety / checked arithmetic
#[allow(clippy::integer_arithmetic)]
State::Body => {
// Total number of bytes in encoded arc - 1
let nbytes = base128_len(arc);
// Shouldn't overflow on any 16-bit+ architectures
if self.cursor + nbytes + 1 >= ObjectIdentifier::MAX_SIZE {
return Err(Error::Length);
}
let new_cursor = self.cursor + nbytes + 1;
// TODO(tarcieri): use `?` when stable in `const fn`
match self.encode_base128_byte(arc, nbytes, false) {
Ok(mut encoder) => {
encoder.cursor = new_cursor;
Ok(encoder)
}
Err(err) => Err(err),
}
}
}
}
/// Finish encoding an OID.
pub(crate) const fn finish(self) -> Result<ObjectIdentifier> {
if self.cursor >= 2 {
Ok(ObjectIdentifier {
bytes: self.bytes,
length: self.cursor as u8,
})
} else {
Err(Error::NotEnoughArcs)
}
}
/// Encode a single byte of a Base 128 value.
const fn encode_base128_byte(mut self, mut n: u32, i: usize, continued: bool) -> Result<Self> {
let mask = if continued { 0b10000000 } else { 0 };
// Underflow checked by branch
#[allow(clippy::integer_arithmetic)]
if n > 0x80 {
self.bytes[checked_add!(self.cursor, i)] = (n & 0b1111111) as u8 | mask;
n >>= 7;
if i > 0 {
self.encode_base128_byte(n, i.saturating_sub(1), true)
} else {
Err(Error::Base128)
}
} else {
self.bytes[self.cursor] = n as u8 | mask;
Ok(self)
}
}
}
/// Compute the length - 1 of an arc when encoded in base 128.
const fn base128_len(arc: Arc) -> usize {
match arc {
0..=0x7f => 0,
0x80..=0x3fff => 1,
0x4000..=0x1fffff => 2,
0x200000..=0x1fffffff => 3,
_ => 4,
}
}
#[cfg(test)]
mod tests {
use super::Encoder;
use hex_literal::hex;
/// OID `1.2.840.10045.2.1` encoded as ASN.1 BER/DER
const EXAMPLE_OID_BER: &[u8] = &hex!("2A8648CE3D0201");
#[test]
fn encode() {
let encoder = Encoder::new();
let encoder = encoder.arc(1).unwrap();
let encoder = encoder.arc(2).unwrap();
let encoder = encoder.arc(840).unwrap();
let encoder = encoder.arc(10045).unwrap();
let encoder = encoder.arc(2).unwrap();
let encoder = encoder.arc(1).unwrap();
assert_eq!(&encoder.bytes[..encoder.cursor], EXAMPLE_OID_BER);
}
}

83
vendor/const-oid/src/error.rs vendored Normal file
View File

@@ -0,0 +1,83 @@
//! Error types
use crate::Arc;
use core::fmt;
/// Result type
pub type Result<T> = core::result::Result<T, Error>;
/// OID errors.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Error {
/// Arc exceeds allowed range (i.e. for first or second OID)
ArcInvalid {
/// Arc value that is erroneous.
arc: Arc,
},
/// Arc is too big (exceeds 32-bit limits of this library).
///
/// Technically the size of an arc is not constrained by X.660, however
/// this library has elected to use `u32` as the arc representation as
/// sufficient for PKIX/PKCS usages.
ArcTooBig,
/// Base 128 encoding error (used in BER/DER serialization of arcs).
Base128,
/// Expected a digit, but was provided something else.
DigitExpected {
/// What was found instead of a digit
actual: u8,
},
/// Input data is empty.
Empty,
/// OID length is invalid (too short or too long).
Length,
/// Minimum 3 arcs required.
NotEnoughArcs,
/// Trailing `.` character at end of input.
TrailingDot,
}
impl Error {
/// Escalate this error into a panic.
///
/// This is a workaround until `Result::unwrap` is allowed in `const fn`.
#[allow(clippy::panic)]
pub(crate) const fn panic(self) -> ! {
match self {
Error::ArcInvalid { .. } | Error::ArcTooBig => panic!("OID contains invalid arc"),
Error::Base128 => panic!("OID contains arc with invalid base 128 encoding"),
Error::DigitExpected { .. } => panic!("OID expected to start with digit"),
Error::Empty => panic!("OID value is empty"),
Error::Length => panic!("OID length invalid"),
Error::NotEnoughArcs => panic!("OID requires minimum of 3 arcs"),
Error::TrailingDot => panic!("OID ends with invalid trailing '.'"),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Error::ArcInvalid { arc } => write!(f, "OID contains out-of-range arc: {}", arc),
Error::ArcTooBig => f.write_str("OID contains arc which is larger than 32-bits"),
Error::Base128 => f.write_str("OID contains arc with invalid base 128 encoding"),
Error::DigitExpected { actual } => {
write!(f, "expected digit, got '{}'", char::from(actual))
}
Error::Empty => f.write_str("OID value is empty"),
Error::Length => f.write_str("OID length invalid"),
Error::NotEnoughArcs => f.write_str("OID requires minimum of 3 arcs"),
Error::TrailingDot => f.write_str("OID ends with invalid trailing '.'"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}

280
vendor/const-oid/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,280 @@
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
)]
#![forbid(unsafe_code)]
#![warn(
clippy::integer_arithmetic,
clippy::panic,
clippy::panic_in_result_fn,
clippy::unwrap_used,
missing_docs,
rust_2018_idioms,
unused_lifetimes,
unused_qualifications
)]
#[cfg(feature = "std")]
extern crate std;
#[macro_use]
mod checked;
mod arcs;
mod encoder;
mod error;
mod parser;
#[cfg(feature = "db")]
#[cfg_attr(docsrs, doc(cfg(feature = "db")))]
pub mod db;
pub use crate::{
arcs::{Arc, Arcs},
error::{Error, Result},
};
use crate::encoder::Encoder;
use core::{fmt, str::FromStr};
/// A trait which associates an OID with a type.
pub trait AssociatedOid {
/// The OID associated with this type.
const OID: ObjectIdentifier;
}
/// A trait which associates a dynamic, `&self`-dependent OID with a type,
/// which may change depending on the type's value.
///
/// This trait is object safe and auto-impl'd for any types which impl
/// [`AssociatedOid`].
pub trait DynAssociatedOid {
/// Get the OID associated with this value.
fn oid(&self) -> ObjectIdentifier;
}
impl<T: AssociatedOid> DynAssociatedOid for T {
fn oid(&self) -> ObjectIdentifier {
T::OID
}
}
/// Object identifier (OID).
///
/// OIDs are hierarchical structures consisting of "arcs", i.e. integer
/// identifiers.
///
/// # Validity
///
/// In order for an OID to be considered valid by this library, it must meet
/// the following criteria:
///
/// - The OID MUST have at least 3 arcs
/// - The first arc MUST be within the range 0-2
/// - The second arc MUST be within the range 0-39
/// - The BER/DER encoding of the OID MUST be shorter than
/// [`ObjectIdentifier::MAX_SIZE`]
#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ObjectIdentifier {
/// Length in bytes
length: u8,
/// Array containing BER/DER-serialized bytes (no header)
bytes: [u8; Self::MAX_SIZE],
}
#[allow(clippy::len_without_is_empty)]
impl ObjectIdentifier {
/// Maximum size of a BER/DER-encoded OID in bytes.
pub const MAX_SIZE: usize = 39; // makes `ObjectIdentifier` 40-bytes total w\ 1-byte length
/// Parse an [`ObjectIdentifier`] from the dot-delimited string form,
/// panicking on parse errors.
///
/// This function exists as a workaround for `unwrap` not yet being
/// stable in `const fn` contexts, and is intended to allow the result to
/// be bound to a constant value:
///
/// ```
/// use const_oid::ObjectIdentifier;
///
/// pub const MY_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1");
/// ```
///
/// In future versions of Rust it should be possible to replace this with
/// `ObjectIdentifier::new(...).unwrap()`.
///
/// Use [`ObjectIdentifier::new`] for fallible parsing.
// TODO(tarcieri): remove this when `Result::unwrap` is `const fn`
pub const fn new_unwrap(s: &str) -> Self {
match Self::new(s) {
Ok(oid) => oid,
Err(err) => err.panic(),
}
}
/// Parse an [`ObjectIdentifier`] from the dot-delimited string form.
pub const fn new(s: &str) -> Result<Self> {
// TODO(tarcieri): use `?` when stable in `const fn`
match parser::Parser::parse(s) {
Ok(parser) => parser.finish(),
Err(err) => Err(err),
}
}
/// Parse an OID from a slice of [`Arc`] values (i.e. integers).
pub fn from_arcs(arcs: impl IntoIterator<Item = Arc>) -> Result<Self> {
let mut encoder = Encoder::new();
for arc in arcs {
encoder = encoder.arc(arc)?;
}
encoder.finish()
}
/// Parse an OID from from its BER/DER encoding.
pub fn from_bytes(ber_bytes: &[u8]) -> Result<Self> {
let len = ber_bytes.len();
match len {
0 => return Err(Error::Empty),
3..=Self::MAX_SIZE => (),
_ => return Err(Error::NotEnoughArcs),
}
let mut bytes = [0u8; Self::MAX_SIZE];
bytes[..len].copy_from_slice(ber_bytes);
let oid = Self {
bytes,
length: len as u8,
};
// Ensure arcs are well-formed
let mut arcs = oid.arcs();
while arcs.try_next()?.is_some() {}
Ok(oid)
}
/// Get the BER/DER serialization of this OID as bytes.
///
/// Note that this encoding omits the tag/length, and only contains the
/// value portion of the encoded OID.
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.length as usize]
}
/// Return the arc with the given index, if it exists.
pub fn arc(&self, index: usize) -> Option<Arc> {
self.arcs().nth(index)
}
/// Iterate over the arcs (a.k.a. nodes) of an [`ObjectIdentifier`].
///
/// Returns [`Arcs`], an iterator over [`Arc`] values.
pub fn arcs(&self) -> Arcs<'_> {
Arcs::new(self)
}
/// Get the length of this [`ObjectIdentifier`] in arcs.
pub fn len(&self) -> usize {
self.arcs().count()
}
/// Get the parent OID of this one (if applicable).
pub fn parent(&self) -> Option<Self> {
let num_arcs = self.len().checked_sub(1)?;
Self::from_arcs(self.arcs().take(num_arcs)).ok()
}
/// Push an additional arc onto this OID, returning the child OID.
pub const fn push_arc(self, arc: Arc) -> Result<Self> {
// TODO(tarcieri): use `?` when stable in `const fn`
match Encoder::extend(self).arc(arc) {
Ok(encoder) => encoder.finish(),
Err(err) => Err(err),
}
}
}
impl AsRef<[u8]> for ObjectIdentifier {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl FromStr for ObjectIdentifier {
type Err = Error;
fn from_str(string: &str) -> Result<Self> {
Self::new(string)
}
}
impl TryFrom<&[u8]> for ObjectIdentifier {
type Error = Error;
fn try_from(ber_bytes: &[u8]) -> Result<Self> {
Self::from_bytes(ber_bytes)
}
}
impl From<&ObjectIdentifier> for ObjectIdentifier {
fn from(oid: &ObjectIdentifier) -> ObjectIdentifier {
*oid
}
}
impl fmt::Debug for ObjectIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ObjectIdentifier({})", self)
}
}
impl fmt::Display for ObjectIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let len = self.arcs().count();
for (i, arc) in self.arcs().enumerate() {
write!(f, "{}", arc)?;
if let Some(j) = i.checked_add(1) {
if j < len {
write!(f, ".")?;
}
}
}
Ok(())
}
}
// Implement by hand because the derive would create invalid values.
// Use the constructor to create a valid oid with at least 3 arcs.
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for ObjectIdentifier {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let first = u.int_in_range(0..=arcs::ARC_MAX_FIRST)?;
let second = u.int_in_range(0..=arcs::ARC_MAX_SECOND)?;
let third = u.arbitrary()?;
let mut oid = Self::from_arcs([first, second, third])
.map_err(|_| arbitrary::Error::IncorrectFormat)?;
for arc in u.arbitrary_iter()? {
oid = oid
.push_arc(arc?)
.map_err(|_| arbitrary::Error::IncorrectFormat)?;
}
Ok(oid)
}
fn size_hint(depth: usize) -> (usize, Option<usize>) {
(Arc::size_hint(depth).0.saturating_mul(3), None)
}
}

112
vendor/const-oid/src/parser.rs vendored Normal file
View File

@@ -0,0 +1,112 @@
//! OID string parser with `const` support.
use crate::{encoder::Encoder, Arc, Error, ObjectIdentifier, Result};
/// Const-friendly OID string parser.
///
/// Parses an OID from the dotted string representation.
#[derive(Debug)]
pub(crate) struct Parser {
/// Current arc in progress
current_arc: Arc,
/// BER/DER encoder
encoder: Encoder,
}
impl Parser {
/// Parse an OID from a dot-delimited string e.g. `1.2.840.113549.1.1.1`
pub(crate) const fn parse(s: &str) -> Result<Self> {
let bytes = s.as_bytes();
if bytes.is_empty() {
return Err(Error::Empty);
}
match bytes[0] {
b'0'..=b'9' => Self {
current_arc: 0,
encoder: Encoder::new(),
}
.parse_bytes(bytes),
actual => Err(Error::DigitExpected { actual }),
}
}
/// Finish parsing, returning the result
pub(crate) const fn finish(self) -> Result<ObjectIdentifier> {
self.encoder.finish()
}
/// Parse the remaining bytes
const fn parse_bytes(mut self, bytes: &[u8]) -> Result<Self> {
match bytes {
// TODO(tarcieri): use `?` when stable in `const fn`
[] => match self.encoder.arc(self.current_arc) {
Ok(encoder) => {
self.encoder = encoder;
Ok(self)
}
Err(err) => Err(err),
},
// TODO(tarcieri): checked arithmetic
#[allow(clippy::integer_arithmetic)]
[byte @ b'0'..=b'9', remaining @ ..] => {
let digit = byte.saturating_sub(b'0');
self.current_arc = self.current_arc * 10 + digit as Arc;
self.parse_bytes(remaining)
}
[b'.', remaining @ ..] => {
if remaining.is_empty() {
return Err(Error::TrailingDot);
}
// TODO(tarcieri): use `?` when stable in `const fn`
match self.encoder.arc(self.current_arc) {
Ok(encoder) => {
self.encoder = encoder;
self.current_arc = 0;
self.parse_bytes(remaining)
}
Err(err) => Err(err),
}
}
[byte, ..] => Err(Error::DigitExpected { actual: *byte }),
}
}
}
#[cfg(test)]
mod tests {
use super::Parser;
use crate::Error;
#[test]
fn parse() {
let oid = Parser::parse("1.23.456").unwrap().finish().unwrap();
assert_eq!(oid, "1.23.456".parse().unwrap());
}
#[test]
fn reject_empty_string() {
assert_eq!(Parser::parse("").err().unwrap(), Error::Empty);
}
#[test]
fn reject_non_digits() {
assert_eq!(
Parser::parse("X").err().unwrap(),
Error::DigitExpected { actual: b'X' }
);
assert_eq!(
Parser::parse("1.2.X").err().unwrap(),
Error::DigitExpected { actual: b'X' }
);
}
#[test]
fn reject_trailing_dot() {
assert_eq!(Parser::parse("1.23.").err().unwrap(), Error::TrailingDot);
}
}