Skip to content

Commit 1d596fc

Browse files
authored
der: preliminary indefinite length support (#1884)
Adds initial support to the `Length` type for decoding indefinite lengths when `EncodingRules::Ber` are in use. Note that this only adds the needed support to the `Length` type and handling of the constructed form of various universal tags (which may require a special `Reader` implementation for BER).
1 parent dd8dbfa commit 1d596fc

File tree

3 files changed

+126
-9
lines changed

3 files changed

+126
-9
lines changed

der/src/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ pub enum ErrorKind {
198198
#[cfg(feature = "std")]
199199
Io(std::io::ErrorKind),
200200

201-
/// Indefinite length disallowed.
201+
/// Indefinite length disallowed (or malformed when decoding BER)
202202
IndefiniteLength,
203203

204204
/// Incorrect length for a given field.
@@ -324,7 +324,7 @@ impl fmt::Display for ErrorKind {
324324
),
325325
#[cfg(feature = "std")]
326326
ErrorKind::Io(err) => write!(f, "I/O error: {err:?}"),
327-
ErrorKind::IndefiniteLength => write!(f, "indefinite length disallowed"),
327+
ErrorKind::IndefiniteLength => write!(f, "indefinite length disallowed/malformed"),
328328
ErrorKind::Length { tag } => write!(f, "incorrect length for {tag}"),
329329
ErrorKind::Noncanonical { tag } => {
330330
write!(f, "ASN.1 {tag} not canonically encoded as DER")

der/src/length.rs

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
//! Length calculations for encoded ASN.1 DER values
22
3-
use crate::{Decode, DerOrd, Encode, Error, ErrorKind, Reader, Result, SliceWriter, Tag, Writer};
3+
use crate::{
4+
Decode, DerOrd, Encode, EncodingRules, Error, ErrorKind, Reader, Result, SliceWriter, Tag,
5+
Writer,
6+
};
47
use core::{
58
cmp::Ordering,
69
fmt,
@@ -211,10 +214,14 @@ impl<'a> Decode<'a> for Length {
211214

212215
fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Length> {
213216
match reader.read_byte()? {
214-
// Note: per X.690 Section 8.1.3.6.1 the byte 0x80 encodes indefinite
215-
// lengths, which are not allowed in DER, so disallow that byte.
216217
len if len < INDEFINITE_LENGTH_OCTET => Ok(len.into()),
217-
INDEFINITE_LENGTH_OCTET => Err(ErrorKind::IndefiniteLength.into()),
218+
// Note: per X.690 Section 8.1.3.6.1 the byte 0x80 encodes indefinite lengths
219+
INDEFINITE_LENGTH_OCTET => match reader.encoding_rules() {
220+
// Indefinite lengths are allowed when decoding BER
221+
EncodingRules::Ber => decode_indefinite_length(&mut reader.clone()),
222+
// Indefinite lengths are disallowed when decoding DER
223+
EncodingRules::Der => Err(ErrorKind::IndefiniteLength.into()),
224+
},
218225
// 1-4 byte variable-sized length prefix
219226
tag @ 0x81..=0x84 => {
220227
let nbytes = tag.checked_sub(0x80).ok_or(ErrorKind::Overlength)? as usize;
@@ -308,12 +315,65 @@ impl<'a> arbitrary::Arbitrary<'a> for Length {
308315
}
309316
}
310317

318+
/// Decode indefinite lengths as used by ASN.1 BER and described in X.690 Section 8.1.3.6:
319+
///
320+
/// > 8.1.3.6 For the indefinite form, the length octets indicate that the
321+
/// > contents octets are terminated by end-of-contents
322+
/// > octets (see 8.1.5), and shall consist of a single octet.
323+
/// >
324+
/// > 8.1.3.6.1 The single octet shall have bit 8 set to one, and bits 7 to
325+
/// > 1 set to zero.
326+
/// >
327+
/// > 8.1.3.6.2 If this form of length is used, then end-of-contents octets
328+
/// > (see 8.1.5) shall be present in the encoding following the contents
329+
/// > octets.
330+
/// >
331+
/// > [...]
332+
/// >
333+
/// > 8.1.5 End-of-contents octets
334+
/// > The end-of-contents octets shall be present if the length is encoded as specified in 8.1.3.6,
335+
/// > otherwise they shall not be present.
336+
/// >
337+
/// > The end-of-contents octets shall consist of two zero octets.
338+
///
339+
/// This function decodes TLV records until it finds an end-of-contents marker (`00 00`), and
340+
/// computes the resulting length as the amount of data decoded.
341+
fn decode_indefinite_length<'a, R: Reader<'a>>(reader: &mut R) -> Result<Length> {
342+
/// The end-of-contents octets can be considered as the encoding of a value whose tag is
343+
/// universal class, whose form is primitive, whose number of the tag is zero, and whose
344+
/// contents are absent.
345+
const EOC_TAG: u8 = 0x00;
346+
347+
let start_pos = reader.position();
348+
349+
loop {
350+
// Look for the end-of-contents marker
351+
if reader.peek_byte() == Some(EOC_TAG) {
352+
// Drain the end-of-contents tag
353+
reader.drain(Length::ONE)?;
354+
355+
// Read the length byte and ensure it's zero (i.e. the full EOC is `00 00`)
356+
let length_byte = reader.read_byte()?;
357+
if length_byte == 0 {
358+
return reader.position() - start_pos;
359+
} else {
360+
return Err(reader.error(ErrorKind::IndefiniteLength));
361+
}
362+
}
363+
364+
let _tag = Tag::decode(reader)?;
365+
let inner_length = Length::decode(reader)?;
366+
reader.drain(inner_length)?;
367+
}
368+
}
369+
311370
#[cfg(test)]
312371
#[allow(clippy::unwrap_used)]
313372
mod tests {
314373
use super::Length;
315-
use crate::{Decode, DerOrd, Encode, ErrorKind};
374+
use crate::{Decode, DerOrd, Encode, EncodingRules, ErrorKind, Reader, SliceReader, Tag};
316375
use core::cmp::Ordering;
376+
use hex_literal::hex;
317377

318378
#[test]
319379
fn decode() {
@@ -404,4 +464,39 @@ mod tests {
404464
Ordering::Greater
405465
);
406466
}
467+
468+
#[test]
469+
fn indefinite() {
470+
/// Length of example in octets.
471+
const EXAMPLE_LEN: usize = 68;
472+
473+
/// Test vector from: <https://github.com/RustCrypto/formats/issues/779#issuecomment-2902948789>
474+
///
475+
/// Notably this example contains nested indefinite lengths to ensure the decoder handles
476+
/// them correctly.
477+
const EXAMPLE_BER: [u8; EXAMPLE_LEN] = hex!(
478+
"30 80 06 09 2A 86 48 86 F7 0D 01 07 01
479+
30 1D 06 09 60 86 48 01 65 03 04 01 2A 04 10 37
480+
34 3D F1 47 0D F6 25 EE B6 F4 BF D2 F1 AC C3 A0
481+
80 04 10 CC 74 AD F6 5D 97 3C 8B 72 CD 51 E1 B9
482+
27 F0 F0 00 00 00 00"
483+
);
484+
485+
let mut reader =
486+
SliceReader::new_with_encoding_rules(&EXAMPLE_BER, EncodingRules::Ber).unwrap();
487+
488+
// Decode initial tag of the message, leaving the reader at the length
489+
let tag = Tag::decode(&mut reader).unwrap();
490+
assert_eq!(tag, Tag::Sequence);
491+
492+
// Decode indefinite length
493+
let length = Length::decode(&mut reader).unwrap();
494+
495+
// Decoding the length should leave the position at the end of the indefinite length octet
496+
let pos = usize::try_from(reader.position()).unwrap();
497+
assert_eq!(pos, 2);
498+
499+
// The first two bytes are the header and the rest is the length of the message
500+
assert_eq!(usize::try_from(length).unwrap(), EXAMPLE_LEN - pos);
501+
}
407502
}

der/src/reader.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,28 @@ pub trait Reader<'r>: Clone {
7575
T::decode(self)
7676
}
7777

78+
/// Drain the given amount of data from the reader, discarding it.
79+
fn drain(&mut self, mut amount: Length) -> Result<(), Error> {
80+
const BUFFER_SIZE: usize = 16;
81+
let mut buffer = [0u8; BUFFER_SIZE];
82+
83+
while amount > Length::ZERO {
84+
let amount_usize = usize::try_from(amount)?;
85+
86+
let nbytes_drained = if amount_usize >= BUFFER_SIZE {
87+
self.read_into(&mut buffer)?;
88+
Length::try_from(BUFFER_SIZE)?
89+
} else {
90+
self.read_into(&mut buffer[..amount_usize])?;
91+
amount
92+
};
93+
94+
amount = (amount - nbytes_drained)?;
95+
}
96+
97+
Ok(())
98+
}
99+
78100
/// Return an error with the given [`ErrorKind`], annotating it with
79101
/// context about where the error occurred.
80102
fn error(&mut self, kind: ErrorKind) -> Error {
@@ -95,7 +117,7 @@ pub trait Reader<'r>: Clone {
95117
}
96118
}
97119

98-
/// Have we read all of the input data?
120+
/// Have we read all input data?
99121
fn is_finished(&self) -> bool {
100122
self.remaining_len().is_zero()
101123
}
@@ -185,7 +207,7 @@ pub trait Reader<'r>: Clone {
185207
self.read_value(header, f)
186208
}
187209

188-
/// Obtain a slice of bytes contain a complete TLV production suitable for parsing later.
210+
/// Obtain a slice of bytes containing a complete TLV production suitable for parsing later.
189211
fn tlv_bytes(&mut self) -> Result<&'r [u8], Error> {
190212
let header = Header::peek(self)?;
191213
let header_len = header.encoded_len()?;

0 commit comments

Comments
 (0)