Skip to content

Commit 8ab2bb0

Browse files
authored
der, x509-cert: fix handling of negative integers (#831)
1 parent acf6d3f commit 8ab2bb0

File tree

4 files changed

+84
-16
lines changed

4 files changed

+84
-16
lines changed

der/src/asn1/integer.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ macro_rules! impl_int_encoding {
4343
impl EncodeValue for $int {
4444
fn value_len(&self) -> Result<Length> {
4545
if *self < 0 {
46-
int::encoded_len(&(*self as $uint).to_be_bytes())
46+
int::negative_encoded_len(&(*self as $uint).to_be_bytes())
4747
} else {
4848
uint::encoded_len(&self.to_be_bytes())
4949
}

der/src/asn1/integer/bigint.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ impl<'a> DecodeValue<'a> for IntRef<'a> {
6666

6767
impl<'a> EncodeValue for IntRef<'a> {
6868
fn value_len(&self) -> Result<Length> {
69-
int::encoded_len(self.inner.as_slice())
69+
// Signed integers always hold their full encoded form.
70+
Ok(self.inner.len())
7071
}
7172

7273
fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
@@ -176,6 +177,8 @@ pub use self::allocating::{Int, Uint};
176177

177178
#[cfg(feature = "alloc")]
178179
mod allocating {
180+
use alloc::vec::Vec;
181+
179182
use super::{super::int, super::uint, IntRef, UintRef};
180183
use crate::{
181184
ord::OrdIsValueOrd,
@@ -244,7 +247,8 @@ mod allocating {
244247

245248
impl EncodeValue for Int {
246249
fn value_len(&self) -> Result<Length> {
247-
int::encoded_len(self.inner.as_slice())
250+
// Signed integers always hold their full encoded form.
251+
Ok(self.inner.len())
248252
}
249253

250254
fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
@@ -259,6 +263,22 @@ mod allocating {
259263
}
260264
}
261265

266+
impl From<Uint> for Int {
267+
fn from(value: Uint) -> Self {
268+
let mut inner: Vec<u8> = Vec::new();
269+
270+
// Add leading `0x00` byte if required
271+
if value.value_len().expect("invalid Uint") > value.len() {
272+
inner.push(0x00);
273+
}
274+
275+
inner.extend_from_slice(value.as_bytes());
276+
let inner = BytesOwned::new(inner).expect("invalid Uint");
277+
278+
Int { inner }
279+
}
280+
}
281+
262282
impl FixedTag for Int {
263283
const TAG: Tag = Tag::Integer;
264284
}

der/src/asn1/integer/int.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Support for encoding negative integers
1+
//! Support for encoding signed integers
22
33
use super::is_highest_bit_set;
44
use crate::{ErrorKind, Length, Result, Tag, Writer};
@@ -46,9 +46,9 @@ where
4646
writer.write(strip_leading_ones(bytes))
4747
}
4848

49-
/// Get the encoded length for the given signed integer serialized as bytes.
49+
/// Get the encoded length for the given **negative** integer serialized as bytes.
5050
#[inline]
51-
pub(super) fn encoded_len(bytes: &[u8]) -> Result<Length> {
51+
pub(super) fn negative_encoded_len(bytes: &[u8]) -> Result<Length> {
5252
Length::try_from(strip_leading_ones(bytes).len())
5353
}
5454

x509-cert/src/serial_number.rs

+58-10
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
use core::fmt::Display;
44

55
use der::{
6-
asn1::Int, DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag,
7-
ValueOrd, Writer,
6+
asn1::Int, asn1::Uint, DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader,
7+
Result, Tag, ValueOrd, Writer,
88
};
99

1010
/// [RFC 5280 Section 4.1.2.2.] Serial Number
@@ -32,15 +32,27 @@ impl SerialNumber {
3232
/// Maximum length in bytes for a [`SerialNumber`]
3333
pub const MAX_LEN: Length = Length::new(20);
3434

35+
/// See notes in `SerialNumber::new` and `SerialNumber::decode_value`.
36+
const MAX_DECODE_LEN: Length = Length::new(21);
37+
3538
/// Create a new [`SerialNumber`] from a byte slice.
39+
///
40+
/// The byte slice **must** represent a positive integer.
3641
pub fn new(bytes: &[u8]) -> Result<Self> {
37-
let inner = Int::new(bytes)?;
38-
39-
if inner.len() > SerialNumber::MAX_LEN {
42+
let inner = Uint::new(bytes)?;
43+
44+
// The user might give us a 20 byte unsigned integer with a high MSB,
45+
// which we'd then encode with 21 octets to preserve the sign bit.
46+
// RFC 5280 is ambiguous about whether this is valid, so we limit
47+
// `SerialNumber` *encodings* to 20 bytes or fewer while permitting
48+
// `SerialNumber` *decodings* to have up to 21 bytes below.
49+
if inner.value_len()? > SerialNumber::MAX_LEN {
4050
return Err(ErrorKind::Overlength.into());
4151
}
4252

43-
Ok(Self { inner })
53+
Ok(Self {
54+
inner: inner.into(),
55+
})
4456
}
4557

4658
/// Borrow the inner byte slice which contains the least significant bytes
@@ -64,7 +76,10 @@ impl<'a> DecodeValue<'a> for SerialNumber {
6476
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
6577
let inner = Int::decode_value(reader, header)?;
6678

67-
if inner.len() > SerialNumber::MAX_LEN {
79+
// See the note in `SerialNumber::new`: we permit lengths of 21 bytes here,
80+
// since some X.509 implementations interpret the limit of 20 bytes to refer
81+
// to the pre-encoded value.
82+
if inner.len() > SerialNumber::MAX_DECODE_LEN {
6883
return Err(ErrorKind::Overlength.into());
6984
}
7085

@@ -112,11 +127,44 @@ mod tests {
112127

113128
use super::*;
114129

130+
#[test]
131+
fn serial_number_invariants() {
132+
// Creating a new serial with an oversized encoding (due to high MSB) fails.
133+
{
134+
let too_big = [0x80; 20];
135+
assert!(SerialNumber::new(&too_big).is_err());
136+
}
137+
138+
// Creating a new serial with the maximum encoding succeeds.
139+
{
140+
let just_enough = [
141+
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
142+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
143+
];
144+
assert!(SerialNumber::new(&just_enough).is_ok());
145+
}
146+
}
147+
115148
#[test]
116149
fn serial_number_display() {
117-
let sn = SerialNumber::new(&[0xAA, 0xBB, 0xCC, 0x01, 0x10, 0x00, 0x11])
118-
.expect("unexpected error");
150+
{
151+
let sn = SerialNumber::new(&[0x11, 0x22, 0x33]).unwrap();
152+
153+
assert_eq!(sn.to_string(), "11:22:33")
154+
}
155+
156+
{
157+
let sn = SerialNumber::new(&[0xAA, 0xBB, 0xCC, 0x01, 0x10, 0x00, 0x11]).unwrap();
119158

120-
assert_eq!(sn.to_string(), "AA:BB:CC:01:10:00:11")
159+
// We force the user's serial to be positive if they give us a negative one.
160+
assert_eq!(sn.to_string(), "00:AA:BB:CC:01:10:00:11")
161+
}
162+
163+
{
164+
let sn = SerialNumber::new(&[0x00, 0x00, 0x01]).unwrap();
165+
166+
// Leading zeroes are ignored, due to canonicalization.
167+
assert_eq!(sn.to_string(), "01")
168+
}
121169
}
122170
}

0 commit comments

Comments
 (0)