Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

const-oid: fallible const parser + ::new_unwrap #458

Merged
merged 1 commit into from
Mar 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion const-oid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ This library supports parsing OIDs in const contexts, e.g.:
```rust
use const_oid::ObjectIdentifier;

pub const MY_OID: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.1.1.1");
pub const MY_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1");
```

The OID parser is implemented entirely in terms of `const fn` and without the
Expand Down
2 changes: 1 addition & 1 deletion const-oid/oiddbgen/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl Node {

quote! {
pub const #symb: crate::NamedOid<'_> = crate::NamedOid {
oid: crate::ObjectIdentifier::new(#obid),
oid: crate::ObjectIdentifier::new_unwrap(#obid),
name: #name,
};
}
Expand Down
14 changes: 9 additions & 5 deletions const-oid/src/arcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,22 +102,26 @@ pub(crate) struct RootArcs(u8);
impl RootArcs {
/// Create [`RootArcs`] from the first and second arc values represented
/// as `Arc` integers.
pub(crate) fn new(first_arc: Arc, second_arc: Arc) -> Result<Self> {
if first_arc > ARC_MAX_FIRST || second_arc > ARC_MAX_SECOND {
return Err(Error);
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 });
}

let byte = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + second_arc as u8;
Ok(Self(byte))
}

/// Get the value of the first arc
pub(crate) fn first_arc(self) -> Arc {
pub(crate) const fn first_arc(self) -> Arc {
self.0 as Arc / (ARC_MAX_SECOND + 1)
}

/// Get the value of the second arc
pub(crate) fn second_arc(self) -> Arc {
pub(crate) const fn second_arc(self) -> Arc {
self.0 as Arc % (ARC_MAX_SECOND + 1)
}
}
Expand Down
2 changes: 1 addition & 1 deletion const-oid/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ mod tests {
let cn = super::DB.by_oid(&CN.oid).expect("cn not found");
assert_eq!(&CN, cn);

let none = ObjectIdentifier::new("0.1.2.3.4.5.6.7.8.9");
let none = ObjectIdentifier::new_unwrap("0.1.2.3.4.5.6.7.8.9");
assert_eq!(None, super::DB.by_oid(&none));
}

Expand Down
1,862 changes: 931 additions & 931 deletions const-oid/src/db/gen.rs

Large diffs are not rendered by default.

93 changes: 46 additions & 47 deletions const-oid/src/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::{
};

/// BER/DER encoder
#[derive(Debug)]
pub(crate) struct Encoder {
/// Current state
state: State,
Expand All @@ -18,6 +19,7 @@ pub(crate) struct Encoder {
}

/// Current state of the encoder
#[derive(Debug)]
enum State {
/// Initial state - no arcs yet encoded
Initial,
Expand All @@ -40,59 +42,76 @@ impl Encoder {
}

/// Encode an [`Arc`] as base 128 into the internal buffer
pub(crate) const fn encode(mut self, arc: Arc) -> Self {
pub(crate) const fn arc(mut self, arc: Arc) -> Result<Self> {
match self.state {
State::Initial => {
assert!(arc <= ARC_MAX_FIRST, "invalid first arc (must be 0-2)");
if arc > ARC_MAX_FIRST {
return Err(Error::ArcInvalid { arc });
}

self.state = State::FirstArc(arc);
self
Ok(self)
}
State::FirstArc(first_arc) => {
assert!(arc <= ARC_MAX_SECOND, "invalid second arc (must be 0-39)");
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;
self
Ok(self)
}
State::Body => {
// Total number of bytes in encoded arc - 1
let nbytes = base128_len(arc);

assert!(
self.cursor + nbytes + 1 < ObjectIdentifier::MAX_SIZE,
"OID too long (exceeded max DER bytes)"
);
if self.cursor + nbytes + 1 >= ObjectIdentifier::MAX_SIZE {
return Err(Error::Length);
}

let new_cursor = self.cursor + nbytes + 1;
let mut result = self.encode_base128_byte(arc, nbytes, false);
result.cursor = new_cursor;
result

// 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) -> ObjectIdentifier {
assert!(self.cursor >= 2, "OID too short (minimum 3 arcs)");
ObjectIdentifier {
bytes: self.bytes,
length: self.cursor as u8,
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 base128 value
const fn encode_base128_byte(mut self, mut n: u32, i: usize, continued: bool) -> Self {
const fn encode_base128_byte(mut self, mut n: u32, i: usize, continued: bool) -> Result<Self> {
let mask = if continued { 0b10000000 } else { 0 };

if n > 0x80 {
self.bytes[self.cursor + i] = (n & 0b1111111) as u8 | mask;
n >>= 7;

assert!(i > 0, "Base 128 offset miscalculation");
self.encode_base128_byte(n, i.saturating_sub(1), true)
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;
self
Ok(self)
}
}
}
Expand All @@ -108,26 +127,6 @@ const fn base128_len(arc: Arc) -> usize {
}
}

/// Write the given unsigned integer in base 128
// TODO(tarcieri): consolidate encoding logic with `encode_base128_byte`
pub(crate) fn write_base128(bytes: &mut [u8], mut n: Arc) -> Result<usize> {
let nbytes = base128_len(n);
let mut i = nbytes;
let mut mask = 0;

while n > 0x80 {
let byte = bytes.get_mut(i).ok_or(Error)?;
*byte = (n & 0b1111111 | mask) as u8;
n >>= 7;
i = i.checked_sub(1).expect("overflow");
mask = 0b10000000;
}

bytes[0] = (n | mask) as u8;

Ok(nbytes + 1)
}

#[cfg(test)]
mod tests {
use super::Encoder;
Expand All @@ -139,12 +138,12 @@ mod tests {
#[test]
fn encode() {
let encoder = Encoder::new();
let encoder = encoder.encode(1);
let encoder = encoder.encode(2);
let encoder = encoder.encode(840);
let encoder = encoder.encode(10045);
let encoder = encoder.encode(2);
let encoder = encoder.encode(1);
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);
}
}
54 changes: 50 additions & 4 deletions const-oid/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,63 @@
//! Error types

use crate::Arc;
use core::fmt;

/// Result type
pub type Result<T> = core::result::Result<T, Error>;

/// Error type
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct 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 fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("OID error")
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 '.'"),
}
}
}

Expand Down
Loading