Skip to content

Commit

Permalink
Make OID storage fixed length - this will make it possible for them t…
Browse files Browse the repository at this point in the history
…o be structurally equal (#180)
  • Loading branch information
alex authored Jun 27, 2021
1 parent 6fa91f2 commit 692c8c7
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 33 deletions.
83 changes: 56 additions & 27 deletions src/object_identifier.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,41 @@
use alloc::borrow::Cow;
use crate::parser::{ParseError, ParseResult};
use alloc::fmt;
use alloc::vec;
use alloc::vec::Vec;

const MAX_OID_LENGTH: usize = 32;

/// Represents an ASN.1 `OBJECT IDENTIFIER`. ObjectIdentifiers are opaque, the only thing may be
/// done with them is test if they are equal to another `ObjectIdentifier`. The generally
/// recommended practice for handling them is to create some `ObjectIdentifier` constants with
/// `ObjectIdentifier::from_string` and then compare ObjectIdentifiers you get from parsing to
/// those.
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct ObjectIdentifier<'a> {
// Store the OID as DER encoded. This means we can 0-copy on parse.
pub(crate) der_encoded: Cow<'a, [u8]>,
pub struct ObjectIdentifier {
// Store the OID as DER encoded.
der_encoded: [u8; MAX_OID_LENGTH],
der_encoded_len: u8,
}

fn _read_base128_int<I: Iterator<Item = u8>>(mut reader: I) -> Option<u32> {
fn _read_base128_int<I: Iterator<Item = u8>>(mut reader: I) -> ParseResult<u32> {
let mut ret = 0u32;
for _ in 0..4 {
let b = reader.next()?;
let b = reader.next().ok_or(ParseError::InvalidValue)?;
ret <<= 7;
ret |= u32::from(b & 0x7f);
if b & 0x80 == 0 {
return Some(ret);
return Ok(ret);
}
}
None
Err(ParseError::InvalidValue)
}

fn _write_base128_int(data: &mut Vec<u8>, n: u32) {
fn _write_base128_int(data: &mut [u8], data_len: &mut usize, n: u32) -> Option<()> {
if n == 0 {
data.push(0);
return;
if *data_len >= data.len() {
return None;
}
data[*data_len] = 0;
*data_len += 1;
return Some(());
}

let mut l = 0;
Expand All @@ -46,13 +51,19 @@ fn _write_base128_int(data: &mut Vec<u8>, n: u32) {
if i != 0 {
o |= 0x80;
}
data.push(o);
if *data_len >= data.len() {
return None;
}
data[*data_len] = o;
*data_len += 1;
}

Some(())
}

impl<'a> ObjectIdentifier<'a> {
impl ObjectIdentifier {
/// Parses an OID from a dotted string, e.g. `"1.2.840.113549"`.
pub fn from_string(oid: &str) -> Option<ObjectIdentifier<'a>> {
pub fn from_string(oid: &str) -> Option<ObjectIdentifier> {
let mut parts = oid.split('.');

let first = parts.next()?.parse::<u32>().ok()?;
Expand All @@ -61,38 +72,50 @@ impl<'a> ObjectIdentifier<'a> {
return None;
}

let mut der_data = vec![];
_write_base128_int(&mut der_data, 40 * first + second);
let mut der_data = [0; MAX_OID_LENGTH];
let mut der_data_len = 0;
_write_base128_int(&mut der_data, &mut der_data_len, 40 * first + second)?;
for part in parts {
_write_base128_int(&mut der_data, part.parse::<u32>().ok()?);
_write_base128_int(&mut der_data, &mut der_data_len, part.parse::<u32>().ok()?)?;
}
Some(ObjectIdentifier {
der_encoded: Cow::Owned(der_data),
der_encoded: der_data,
der_encoded_len: der_data_len as u8,
})
}

/// Creates an `ObjectIdentifier` from its DER representation. This does
/// not perform any allocations or copies.
pub fn from_der(data: &'a [u8]) -> Option<ObjectIdentifier<'a>> {
pub fn from_der(data: &[u8]) -> ParseResult<ObjectIdentifier> {
if data.is_empty() {
return None;
return Err(ParseError::InvalidValue);
} else if data.len() > MAX_OID_LENGTH {
return Err(ParseError::OidTooLong);
}
let mut cursor = data.iter().copied();
while cursor.len() > 0 {
_read_base128_int(&mut cursor)?;
}

Some(ObjectIdentifier {
der_encoded: Cow::Borrowed(data),
let mut storage = [0; MAX_OID_LENGTH];
storage[..data.len()].copy_from_slice(data);

Ok(ObjectIdentifier {
der_encoded: storage,
der_encoded_len: data.len() as u8,
})
}

pub(crate) fn as_der(&self) -> &[u8] {
&self.der_encoded[..self.der_encoded_len as usize]
}
}

impl fmt::Display for ObjectIdentifier<'_> {
impl fmt::Display for ObjectIdentifier {
/// Converts an `ObjectIdentifier` to a dotted string, e.g.
/// "1.2.840.113549".
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut cursor = self.der_encoded.iter().copied();
let mut cursor = self.as_der().iter().copied();

let first = _read_base128_int(&mut cursor).unwrap();
if first < 80 {
Expand All @@ -112,7 +135,7 @@ impl fmt::Display for ObjectIdentifier<'_> {

#[cfg(test)]
mod tests {
use crate::ObjectIdentifier;
use crate::{ObjectIdentifier, ParseError};

#[test]
fn test_object_identifier_from_string() {
Expand All @@ -127,6 +150,7 @@ mod tests {
".2.5",
"2..5",
"2.5.",
"1.3.6.1.4.1.1248.1.1.2.1.3.21.69.112.115.111.110.32.83.116.121.108.117.115.32.80.114.111.32.52.57.48.48.123.124412.31.213321.123",
] {
assert_eq!(ObjectIdentifier::from_string(val), None);
}
Expand All @@ -143,6 +167,11 @@ mod tests {
}
}

#[test]
fn test_from_der() {
assert_eq!(ObjectIdentifier::from_der(b"\x06\x2b\x2b\x06\x01\x04\x01\x89\x60\x01\x01\x02\x01\x03\x15\x45\x70\x73\x6f\x6e\x20\x53\x74\x79\x6c\x75\x73\x20\x50\x72\x6f\x20\x34\x39\x30\x30\x7b\x87\xcb\x7c\x1f\x8d\x82\x49\x7b"), Err(ParseError::OidTooLong));
}

#[test]
fn test_to_string() {
for val in &[
Expand Down
5 changes: 4 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ pub enum ParseError {
InvalidSetOrdering,
/// An OPTIONAL DEFAULT was written with a default value.
EncodedDefault,
/// OID value is longer than the maximum size rust-asn1 can store. This is
/// a limitation of rust-asn1.
OidTooLong,
}

/// The result of a `parse`. Either a successful value or a `ParseError`.
Expand Down Expand Up @@ -430,7 +433,7 @@ mod tests {

#[test]
fn test_parse_object_identifier() {
assert_parses::<ObjectIdentifier<'_>>(&[
assert_parses::<ObjectIdentifier>(&[
(
Ok(ObjectIdentifier::from_string("2.5").unwrap()),
b"\x06\x01\x55",
Expand Down
10 changes: 5 additions & 5 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,16 +480,16 @@ impl<'a> SimpleAsn1Writable<'a> for BigUint<'a> {
}
}

impl<'a> SimpleAsn1Readable<'a> for ObjectIdentifier<'a> {
impl<'a> SimpleAsn1Readable<'a> for ObjectIdentifier {
const TAG: u8 = 0x06;
fn parse_data(data: &'a [u8]) -> ParseResult<ObjectIdentifier<'a>> {
ObjectIdentifier::from_der(data).ok_or(ParseError::InvalidValue)
fn parse_data(data: &'a [u8]) -> ParseResult<ObjectIdentifier> {
ObjectIdentifier::from_der(data)
}
}
impl<'a> SimpleAsn1Writable<'a> for ObjectIdentifier<'a> {
impl<'a> SimpleAsn1Writable<'a> for ObjectIdentifier {
const TAG: u8 = 0x06;
fn write_data(&self, dest: &mut Vec<u8>) {
dest.extend_from_slice(&self.der_encoded);
dest.extend_from_slice(self.as_der());
}
}

Expand Down

0 comments on commit 692c8c7

Please sign in to comment.