Skip to content

Commit 7c4638b

Browse files
authored
const-oid: fix large arc handling in encoder (#1522)
BER encodings were being miscomputed for certain large arcs. The previous method was a bit wacky (in addition to being buggy) and attempted to encode each arc backwards within the BER output buffer. This switches to a new method which splits the upper 7 bits from an arc and encodes that as a byte, continuing until all bytes of the arc have been encoded, which is much more straightforward. The problematic cases which were reported have now been corrected. Fixes #1520
1 parent 895dbdf commit 7c4638b

File tree

2 files changed

+87
-59
lines changed

2 files changed

+87
-59
lines changed

const-oid/src/encoder.rs

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -73,27 +73,9 @@ impl<const MAX_SIZE: usize> Encoder<MAX_SIZE> {
7373
self.cursor = 1;
7474
Ok(self)
7575
}
76-
// TODO(tarcieri): finer-grained overflow safety / checked arithmetic
77-
#[allow(clippy::arithmetic_side_effects)]
7876
State::Body => {
79-
// Total number of bytes in encoded arc - 1
8077
let nbytes = base128_len(arc);
81-
82-
// Shouldn't overflow on any 16-bit+ architectures
83-
if self.cursor + nbytes + 1 > MAX_SIZE {
84-
return Err(Error::Length);
85-
}
86-
87-
let new_cursor = self.cursor + nbytes + 1;
88-
89-
// TODO(tarcieri): use `?` when stable in `const fn`
90-
match self.encode_base128_byte(arc, nbytes, false) {
91-
Ok(mut encoder) => {
92-
encoder.cursor = new_cursor;
93-
Ok(encoder)
94-
}
95-
Err(err) => Err(err),
96-
}
78+
self.encode_base128(arc, nbytes)
9779
}
9880
}
9981
}
@@ -113,23 +95,19 @@ impl<const MAX_SIZE: usize> Encoder<MAX_SIZE> {
11395
}
11496

11597
/// Encode a single byte of a Base 128 value.
116-
const fn encode_base128_byte(mut self, mut n: u32, i: usize, continued: bool) -> Result<Self> {
117-
let mask = if continued { 0b10000000 } else { 0 };
118-
119-
// Underflow checked by branch
120-
#[allow(clippy::arithmetic_side_effects)]
121-
if n > 0x80 {
122-
self.bytes[checked_add!(self.cursor, i)] = (n & 0b1111111) as u8 | mask;
123-
n >>= 7;
124-
125-
if i > 0 {
126-
self.encode_base128_byte(n, i.saturating_sub(1), true)
127-
} else {
128-
Err(Error::Base128)
129-
}
130-
} else {
131-
self.bytes[self.cursor] = n as u8 | mask;
132-
Ok(self)
98+
const fn encode_base128(mut self, n: u32, remaining_len: usize) -> Result<Self> {
99+
if self.cursor >= MAX_SIZE {
100+
return Err(Error::Length);
101+
}
102+
103+
let mask = if remaining_len > 0 { 0b10000000 } else { 0 };
104+
let (hi, lo) = split_high_bits(n);
105+
self.bytes[self.cursor] = hi | mask;
106+
self.cursor = checked_add!(self.cursor, 1);
107+
108+
match remaining_len.checked_sub(1) {
109+
Some(len) => self.encode_base128(lo, len),
110+
None => Ok(self),
133111
}
134112
}
135113
}
@@ -145,6 +123,29 @@ const fn base128_len(arc: Arc) -> usize {
145123
}
146124
}
147125

126+
/// Split the highest 7-bits of an [`Arc`] from the rest of an arc.
127+
///
128+
/// Returns: `(hi, lo)`
129+
// TODO(tarcieri): always use checked arithmetic
130+
#[allow(clippy::arithmetic_side_effects)]
131+
const fn split_high_bits(arc: Arc) -> (u8, Arc) {
132+
if arc < 0x80 {
133+
return (arc as u8, 0);
134+
}
135+
136+
let hi_bit = 32 - arc.leading_zeros();
137+
let hi_bit_mod7 = hi_bit % 7;
138+
let upper_bit_pos = hi_bit
139+
- if hi_bit > 0 && hi_bit_mod7 == 0 {
140+
7
141+
} else {
142+
hi_bit_mod7
143+
};
144+
let upper_bits = arc >> upper_bit_pos;
145+
let lower_bits = arc ^ (upper_bits << upper_bit_pos);
146+
(upper_bits as u8, lower_bits)
147+
}
148+
148149
#[cfg(test)]
149150
#[allow(clippy::unwrap_used)]
150151
mod tests {

const-oid/tests/oid.rs

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,16 @@ const EXAMPLE_OID_2_BER: &[u8] = &hex!("60864801650304012A");
2323
const EXAMPLE_OID_2: ObjectIdentifier = ObjectIdentifier::new_unwrap(EXAMPLE_OID_2_STR);
2424

2525
/// Example OID value with a large arc
26-
const EXAMPLE_OID_LARGE_ARC_STR: &str = "0.9.2342.19200300.100.1.1";
27-
const EXAMPLE_OID_LARGE_ARC_BER: &[u8] = &hex!("0992268993F22C640101");
28-
const EXAMPLE_OID_LARGE_ARC: ObjectIdentifier =
29-
ObjectIdentifier::new_unwrap("0.9.2342.19200300.100.1.1");
26+
const EXAMPLE_OID_LARGE_ARC_0_STR: &str = "1.2.16384";
27+
const EXAMPLE_OID_LARGE_ARC_0_BER: &[u8] = &hex!("2A818000");
28+
const EXAMPLE_OID_LARGE_ARC_0: ObjectIdentifier =
29+
ObjectIdentifier::new_unwrap(crate::EXAMPLE_OID_LARGE_ARC_0_STR);
30+
31+
/// Example OID value with a large arc
32+
const EXAMPLE_OID_LARGE_ARC_1_STR: &str = "0.9.2342.19200300.100.1.1";
33+
const EXAMPLE_OID_LARGE_ARC_1_BER: &[u8] = &hex!("0992268993F22C640101");
34+
const EXAMPLE_OID_LARGE_ARC_1: ObjectIdentifier =
35+
ObjectIdentifier::new_unwrap(EXAMPLE_OID_LARGE_ARC_1_STR);
3036

3137
/// Create an OID from a string.
3238
pub fn oid(s: &str) -> ObjectIdentifier {
@@ -38,27 +44,37 @@ fn from_bytes() {
3844
let oid0 = ObjectIdentifier::from_bytes(EXAMPLE_OID_0_BER).unwrap();
3945
assert_eq!(oid0.arc(0).unwrap(), 0);
4046
assert_eq!(oid0.arc(1).unwrap(), 9);
47+
assert_eq!(oid0.arc(2).unwrap(), 2342);
4148
assert_eq!(oid0, EXAMPLE_OID_0);
4249

4350
let oid1 = ObjectIdentifier::from_bytes(EXAMPLE_OID_1_BER).unwrap();
4451
assert_eq!(oid1.arc(0).unwrap(), 1);
4552
assert_eq!(oid1.arc(1).unwrap(), 2);
53+
assert_eq!(oid1.arc(2).unwrap(), 840);
4654
assert_eq!(oid1, EXAMPLE_OID_1);
4755

4856
let oid2 = ObjectIdentifier::from_bytes(EXAMPLE_OID_2_BER).unwrap();
4957
assert_eq!(oid2.arc(0).unwrap(), 2);
5058
assert_eq!(oid2.arc(1).unwrap(), 16);
59+
assert_eq!(oid2.arc(2).unwrap(), 840);
5160
assert_eq!(oid2, EXAMPLE_OID_2);
5261

53-
let oid3 = ObjectIdentifier::from_bytes(EXAMPLE_OID_LARGE_ARC_BER).unwrap();
54-
assert_eq!(oid3.arc(0).unwrap(), 0);
55-
assert_eq!(oid3.arc(1).unwrap(), 9);
56-
assert_eq!(oid3.arc(2).unwrap(), 2342);
57-
assert_eq!(oid3.arc(3).unwrap(), 19200300);
58-
assert_eq!(oid3.arc(4).unwrap(), 100);
59-
assert_eq!(oid3.arc(5).unwrap(), 1);
60-
assert_eq!(oid3.arc(6).unwrap(), 1);
61-
assert_eq!(oid3, EXAMPLE_OID_LARGE_ARC);
62+
let oid_largearc0 = ObjectIdentifier::from_bytes(EXAMPLE_OID_LARGE_ARC_0_BER).unwrap();
63+
assert_eq!(oid_largearc0.arc(0).unwrap(), 1);
64+
assert_eq!(oid_largearc0.arc(1).unwrap(), 2);
65+
assert_eq!(oid_largearc0.arc(2).unwrap(), 16384);
66+
assert_eq!(oid_largearc0.arc(3), None);
67+
assert_eq!(oid_largearc0, EXAMPLE_OID_LARGE_ARC_0);
68+
69+
let oid_largearc1 = ObjectIdentifier::from_bytes(EXAMPLE_OID_LARGE_ARC_1_BER).unwrap();
70+
assert_eq!(oid_largearc1.arc(0).unwrap(), 0);
71+
assert_eq!(oid_largearc1.arc(1).unwrap(), 9);
72+
assert_eq!(oid_largearc1.arc(2).unwrap(), 2342);
73+
assert_eq!(oid_largearc1.arc(3).unwrap(), 19200300);
74+
assert_eq!(oid_largearc1.arc(4).unwrap(), 100);
75+
assert_eq!(oid_largearc1.arc(5).unwrap(), 1);
76+
assert_eq!(oid_largearc1.arc(6).unwrap(), 1);
77+
assert_eq!(oid_largearc1, EXAMPLE_OID_LARGE_ARC_1);
6278

6379
// Empty
6480
assert_eq!(ObjectIdentifier::from_bytes(&[]), Err(Error::Empty));
@@ -81,17 +97,25 @@ fn from_str() {
8197
assert_eq!(oid2.arc(1).unwrap(), 16);
8298
assert_eq!(oid2, EXAMPLE_OID_2);
8399

84-
let oid3 = EXAMPLE_OID_LARGE_ARC_STR
100+
let oid_largearc0 = EXAMPLE_OID_LARGE_ARC_0_STR
85101
.parse::<ObjectIdentifier>()
86102
.unwrap();
87-
assert_eq!(oid3.arc(0).unwrap(), 0);
88-
assert_eq!(oid3.arc(1).unwrap(), 9);
89-
assert_eq!(oid3.arc(2).unwrap(), 2342);
90-
assert_eq!(oid3.arc(3).unwrap(), 19200300);
91-
assert_eq!(oid3.arc(4).unwrap(), 100);
92-
assert_eq!(oid3.arc(5).unwrap(), 1);
93-
assert_eq!(oid3.arc(6).unwrap(), 1);
94-
assert_eq!(oid3, EXAMPLE_OID_LARGE_ARC);
103+
assert_eq!(oid_largearc0.arc(0).unwrap(), 1);
104+
assert_eq!(oid_largearc0.arc(1).unwrap(), 2);
105+
assert_eq!(oid_largearc0.arc(2).unwrap(), 16384);
106+
assert_eq!(oid_largearc0, EXAMPLE_OID_LARGE_ARC_0);
107+
108+
let oid_largearc1 = EXAMPLE_OID_LARGE_ARC_1_STR
109+
.parse::<ObjectIdentifier>()
110+
.unwrap();
111+
assert_eq!(oid_largearc1.arc(0).unwrap(), 0);
112+
assert_eq!(oid_largearc1.arc(1).unwrap(), 9);
113+
assert_eq!(oid_largearc1.arc(2).unwrap(), 2342);
114+
assert_eq!(oid_largearc1.arc(3).unwrap(), 19200300);
115+
assert_eq!(oid_largearc1.arc(4).unwrap(), 100);
116+
assert_eq!(oid_largearc1.arc(5).unwrap(), 1);
117+
assert_eq!(oid_largearc1.arc(6).unwrap(), 1);
118+
assert_eq!(oid_largearc1, EXAMPLE_OID_LARGE_ARC_1);
95119

96120
// Truncated
97121
assert_eq!(
@@ -117,7 +141,10 @@ fn display() {
117141
assert_eq!(EXAMPLE_OID_0.to_string(), EXAMPLE_OID_0_STR);
118142
assert_eq!(EXAMPLE_OID_1.to_string(), EXAMPLE_OID_1_STR);
119143
assert_eq!(EXAMPLE_OID_2.to_string(), EXAMPLE_OID_2_STR);
120-
assert_eq!(EXAMPLE_OID_LARGE_ARC.to_string(), EXAMPLE_OID_LARGE_ARC_STR);
144+
assert_eq!(
145+
EXAMPLE_OID_LARGE_ARC_1.to_string(),
146+
EXAMPLE_OID_LARGE_ARC_1_STR
147+
);
121148
}
122149

123150
#[test]

0 commit comments

Comments
 (0)