From 692c8c71f0b88e083eccd669ac97e5e1e1613dd9 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 27 Jun 2021 14:20:48 -0400 Subject: [PATCH] Make OID storage fixed length - this will make it possible for them to be structurally equal (#180) --- src/object_identifier.rs | 83 +++++++++++++++++++++++++++------------- src/parser.rs | 5 ++- src/types.rs | 10 ++--- 3 files changed, 65 insertions(+), 33 deletions(-) diff --git a/src/object_identifier.rs b/src/object_identifier.rs index ac10832..43f022c 100644 --- a/src/object_identifier.rs +++ b/src/object_identifier.rs @@ -1,7 +1,7 @@ -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 @@ -9,28 +9,33 @@ use alloc::vec::Vec; /// `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>(mut reader: I) -> Option { +fn _read_base128_int>(mut reader: I) -> ParseResult { 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, 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; @@ -46,13 +51,19 @@ fn _write_base128_int(data: &mut Vec, 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> { + pub fn from_string(oid: &str) -> Option { let mut parts = oid.split('.'); let first = parts.next()?.parse::().ok()?; @@ -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::().ok()?); + _write_base128_int(&mut der_data, &mut der_data_len, part.parse::().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> { + pub fn from_der(data: &[u8]) -> ParseResult { 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 { @@ -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() { @@ -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); } @@ -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 &[ diff --git a/src/parser.rs b/src/parser.rs index 08730e3..71986eb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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`. @@ -430,7 +433,7 @@ mod tests { #[test] fn test_parse_object_identifier() { - assert_parses::>(&[ + assert_parses::(&[ ( Ok(ObjectIdentifier::from_string("2.5").unwrap()), b"\x06\x01\x55", diff --git a/src/types.rs b/src/types.rs index 26193a2..46ae381 100644 --- a/src/types.rs +++ b/src/types.rs @@ -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::from_der(data).ok_or(ParseError::InvalidValue) + fn parse_data(data: &'a [u8]) -> ParseResult { + 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) { - dest.extend_from_slice(&self.der_encoded); + dest.extend_from_slice(self.as_der()); } }