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: fix off-by-one error parsing large BER arcs #84

Merged
merged 1 commit into from
Oct 12, 2021
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
36 changes: 21 additions & 15 deletions const-oid/src/arcs.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
//! Arcs are integer values which exist within an OID's hierarchy.

use crate::{Error, ObjectIdentifier, Result};
use core::convert::TryFrom;
use core::{convert::TryFrom, mem};

/// Type used to represent an "arc" (i.e. integer identifier value)
/// Type used to represent an "arc" (i.e. integer identifier value).
pub type Arc = u32;

/// Maximum value of the first arc in an OID
pub(crate) const FIRST_ARC_MAX: Arc = 2;
/// 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 SECOND_ARC_MAX: Arc = 39;
/// 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.
pub(crate) const ARC_MAX_BYTES: usize = mem::size_of::<Arc>();

/// Maximum value of the last byte in an arc.
pub(crate) const ARC_MAX_LAST_OCTET: u8 = 0b11110000; // Max bytes of leading 1-bits

/// [`Iterator`] over arcs (a.k.a. nodes) in an [`ObjectIdentifier`].
///
Expand Down Expand Up @@ -55,8 +61,8 @@ impl<'a> Iterator for Arcs<'a> {
match self.oid.as_bytes().get(offset + arc_bytes).cloned() {
Some(byte) => {
arc_bytes += 1;
assert!(
arc_bytes < 4 || byte & 0b11110000 == 0,
debug_assert!(
arc_bytes < ARC_MAX_BYTES || byte & ARC_MAX_LAST_OCTET == 0,
"OID arc overflowed"
);
result = result << 7 | (byte & 0b1111111) as Arc;
Expand All @@ -67,7 +73,7 @@ impl<'a> Iterator for Arcs<'a> {
}
}
None => {
assert_eq!(arc_bytes, 0, "truncated OID");
debug_assert_eq!(arc_bytes, 0, "truncated OID");
return None;
}
}
Expand All @@ -88,31 +94,31 @@ 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 > FIRST_ARC_MAX || second_arc > SECOND_ARC_MAX {
if first_arc > ARC_MAX_FIRST || second_arc > ARC_MAX_SECOND {
return Err(Error);
}

let byte = (first_arc * (SECOND_ARC_MAX + 1)) as u8 + second_arc as u8;
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 {
self.0 as Arc / (SECOND_ARC_MAX + 1)
self.0 as Arc / (ARC_MAX_SECOND + 1)
}

/// Get the value of the second arc
pub(crate) fn second_arc(self) -> Arc {
self.0 as Arc % (SECOND_ARC_MAX + 1)
self.0 as Arc % (ARC_MAX_SECOND + 1)
}
}

impl TryFrom<u8> for RootArcs {
type Error = Error;

fn try_from(octet: u8) -> Result<Self> {
let first = octet as Arc / (SECOND_ARC_MAX + 1);
let second = octet as Arc % (SECOND_ARC_MAX + 1);
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)
Expand Down
8 changes: 4 additions & 4 deletions const-oid/src/encoder.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! OID encoder with `const` support.

use crate::{
arcs::{FIRST_ARC_MAX, SECOND_ARC_MAX},
arcs::{ARC_MAX_FIRST, ARC_MAX_SECOND},
Arc, Error, ObjectIdentifier, Result,
};

Expand Down Expand Up @@ -43,14 +43,14 @@ impl Encoder {
pub(crate) const fn encode(mut self, arc: Arc) -> Self {
match self.state {
State::Initial => {
const_assert!(arc <= FIRST_ARC_MAX, "invalid first arc (must be 0-2)");
const_assert!(arc <= ARC_MAX_FIRST, "invalid first arc (must be 0-2)");
self.state = State::FirstArc(arc);
self
}
State::FirstArc(first_arc) => {
const_assert!(arc <= SECOND_ARC_MAX, "invalid second arc (must be 0-39)");
const_assert!(arc <= ARC_MAX_SECOND, "invalid second arc (must be 0-39)");
self.state = State::Body;
self.bytes[0] = (first_arc * (SECOND_ARC_MAX + 1)) as u8 + arc as u8;
self.bytes[0] = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + arc as u8;
self.cursor = 1;
self
}
Expand Down
8 changes: 4 additions & 4 deletions const-oid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub use crate::{
error::{Error, Result},
};

use crate::arcs::RootArcs;
use crate::arcs::{RootArcs, ARC_MAX_BYTES, ARC_MAX_LAST_OCTET};
use core::{convert::TryFrom, fmt, str::FromStr};

/// Object identifier (OID).
Expand Down Expand Up @@ -179,13 +179,13 @@ impl ObjectIdentifier {
while arc_offset < len {
match ber_bytes.get(arc_offset + arc_bytes).cloned() {
Some(byte) => {
arc_bytes += 1;

if arc_bytes == 4 && byte & 0b11110000 != 0 {
if (arc_bytes == ARC_MAX_BYTES) && (byte & ARC_MAX_LAST_OCTET != 0) {
// Overflowed `Arc` (u32)
return Err(Error);
}

arc_bytes += 1;

if byte & 0b10000000 == 0 {
arc_offset += arc_bytes;
arc_bytes = 0;
Expand Down
33 changes: 20 additions & 13 deletions const-oid/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,20 @@ use const_oid::ObjectIdentifier;
use hex_literal::hex;
use std::string::ToString;

/// Example OID value with a root arc of `1`
const EXAMPLE_OID_1: ObjectIdentifier = ObjectIdentifier::new("1.2.840.10045.2.1");
/// Example OID value with a root arc of `0` (and large arc).
const EXAMPLE_OID_0_STRING: &str = "0.9.2342.19200300.100.1.1";
const EXAMPLE_OID_0_BER: &[u8] = &hex!("0992268993F22C640101");
const EXAMPLE_OID_0: ObjectIdentifier = ObjectIdentifier::new(EXAMPLE_OID_0_STRING);

/// Example OID value with a root arc of `2`
const EXAMPLE_OID_2: ObjectIdentifier = ObjectIdentifier::new("2.16.840.1.101.3.4.1.42");

/// Example OID 1 encoded as ASN.1 BER/DER
const EXAMPLE_OID_1_BER: &[u8] = &hex!("2A8648CE3D0201");

/// Example OID 2 encoded as ASN.1 BER/DER
const EXAMPLE_OID_2_BER: &[u8] = &hex!("60864801650304012A");

/// Example OID 1 as a string
/// Example OID value with a root arc of `1`.
const EXAMPLE_OID_1_STRING: &str = "1.2.840.10045.2.1";
const EXAMPLE_OID_1_BER: &[u8] = &hex!("2A8648CE3D0201");
const EXAMPLE_OID_1: ObjectIdentifier = ObjectIdentifier::new(EXAMPLE_OID_1_STRING);

/// Example OID 2 as a string
/// Example OID value with a root arc of `2`.
const EXAMPLE_OID_2_STRING: &str = "2.16.840.1.101.3.4.1.42";
const EXAMPLE_OID_2_BER: &[u8] = &hex!("60864801650304012A");
const EXAMPLE_OID_2: ObjectIdentifier = ObjectIdentifier::new(EXAMPLE_OID_2_STRING);

#[test]
fn display() {
Expand All @@ -33,6 +30,11 @@ fn display() {

#[test]
fn from_bytes() {
let oid0 = ObjectIdentifier::from_bytes(EXAMPLE_OID_0_BER).unwrap();
assert_eq!(oid0.arc(0).unwrap(), 0);
assert_eq!(oid0.arc(1).unwrap(), 9);
assert_eq!(oid0, EXAMPLE_OID_0);

let oid1 = ObjectIdentifier::from_bytes(EXAMPLE_OID_1_BER).unwrap();
assert_eq!(oid1.arc(0).unwrap(), 1);
assert_eq!(oid1.arc(1).unwrap(), 2);
Expand All @@ -53,6 +55,11 @@ fn from_bytes() {

#[test]
fn from_str() {
let oid0 = EXAMPLE_OID_0_STRING.parse::<ObjectIdentifier>().unwrap();
assert_eq!(oid0.arc(0).unwrap(), 0);
assert_eq!(oid0.arc(1).unwrap(), 9);
assert_eq!(oid0, EXAMPLE_OID_0);

let oid1 = EXAMPLE_OID_1_STRING.parse::<ObjectIdentifier>().unwrap();
assert_eq!(oid1.arc(0).unwrap(), 1);
assert_eq!(oid1.arc(1).unwrap(), 2);
Expand Down