Skip to content

Commit 7ed19a6

Browse files
committed
der: add IndefiniteLength type
Per #823 and #827, making PKCS#7 work interoperably will involve supporting at limited number of BER productions, one of which is indefinite lengths. The current built-in `Length` type rejects them, as a proper DER parser is expected to. This commit adds a separate `IndefiniteLength` type as a newtype of `Option<Length>` with support for parsing indefinite lengths.
1 parent 4e2266e commit 7ed19a6

File tree

3 files changed

+116
-4
lines changed

3 files changed

+116
-4
lines changed

der/src/error.rs

+4
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ pub enum ErrorKind {
197197
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
198198
Io(std::io::ErrorKind),
199199

200+
/// Indefinite length disallowed.
201+
IndefiniteLength,
202+
200203
/// Incorrect length for a given field.
201204
Length {
202205
/// Tag of the value being decoded.
@@ -321,6 +324,7 @@ impl fmt::Display for ErrorKind {
321324
),
322325
#[cfg(feature = "std")]
323326
ErrorKind::Io(err) => write!(f, "I/O error: {:?}", err),
327+
ErrorKind::IndefiniteLength => write!(f, "indefinite length disallowed"),
324328
ErrorKind::Length { tag } => write!(f, "incorrect length for {}", tag),
325329
ErrorKind::Noncanonical { tag } => {
326330
write!(f, "ASN.1 {} not canonically encoded as DER", tag)

der/src/length.rs

+111-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ const MAX_DER_OCTETS: usize = 5;
1414
/// Maximum length as a `u32` (256 MiB).
1515
const MAX_U32: u32 = 0xfff_ffff;
1616

17+
/// Octet identifying an indefinite length as described in X.690 Section
18+
/// 8.1.3.6.1:
19+
///
20+
/// > The single octet shall have bit 8 set to one, and bits 7 to
21+
/// > 1 set to zero.
22+
const INDEFINITE_LENGTH_OCTET: u8 = 0b10000000; // 0x80
23+
1724
/// ASN.1-encoded length.
1825
///
1926
/// Maximum length is defined by the [`Length::MAX`] constant (256 MiB).
@@ -204,7 +211,8 @@ impl<'a> Decode<'a> for Length {
204211
match reader.read_byte()? {
205212
// Note: per X.690 Section 8.1.3.6.1 the byte 0x80 encodes indefinite
206213
// lengths, which are not allowed in DER, so disallow that byte.
207-
len if len < 0x80 => Ok(len.into()),
214+
len if len < INDEFINITE_LENGTH_OCTET => Ok(len.into()),
215+
INDEFINITE_LENGTH_OCTET => Err(ErrorKind::IndefiniteLength.into()),
208216
// 1-4 byte variable-sized length prefix
209217
tag @ 0x81..=0x84 => {
210218
let nbytes = tag.checked_sub(0x80).ok_or(ErrorKind::Overlength)? as usize;
@@ -299,9 +307,95 @@ impl<'a> arbitrary::Arbitrary<'a> for Length {
299307
}
300308
}
301309

310+
/// Length type with support for indefinite lengths as used by ASN.1 BER,
311+
/// as described in X.690 Section 8.1.3.6:
312+
///
313+
/// > 8.1.3.6 For the indefinite form, the length octets indicate that the
314+
/// > contents octets are terminated by end-of-contents
315+
/// > octets (see 8.1.5), and shall consist of a single octet.
316+
/// >
317+
/// > 8.1.3.6.1 The single octet shall have bit 8 set to one, and bits 7 to
318+
/// > 1 set to zero.
319+
/// >
320+
/// > 8.1.3.6.2 If this form of length is used, then end-of-contents octets
321+
/// > (see 8.1.5) shall be present in the encoding following the contents
322+
/// > octets.
323+
///
324+
/// Indefinite lengths are non-canonical and therefore invalid DER, however
325+
/// there are interoperability corner cases where we have little choice but to
326+
/// tolerate some BER productions where this is helpful.
327+
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
328+
pub struct IndefiniteLength(Option<Length>);
329+
330+
impl IndefiniteLength {
331+
/// Length of `0`.
332+
pub const ZERO: Self = Self(Some(Length::ZERO));
333+
334+
/// Length of `1`.
335+
pub const ONE: Self = Self(Some(Length::ONE));
336+
337+
/// Indefinite length.
338+
pub const INDEFINITE: Self = Self(None);
339+
}
340+
341+
impl IndefiniteLength {
342+
/// Is this length definite?
343+
pub fn is_definite(self) -> bool {
344+
self.0.is_some()
345+
}
346+
/// Is this length indefinite?
347+
pub fn is_indefinite(self) -> bool {
348+
self.0.is_none()
349+
}
350+
}
351+
352+
impl<'a> Decode<'a> for IndefiniteLength {
353+
fn decode<R: Reader<'a>>(reader: &mut R) -> Result<IndefiniteLength> {
354+
if reader.peek_byte() == Some(INDEFINITE_LENGTH_OCTET) {
355+
// Consume the byte we already peeked at.
356+
let byte = reader.read_byte()?;
357+
debug_assert_eq!(byte, INDEFINITE_LENGTH_OCTET);
358+
359+
Ok(Self::INDEFINITE)
360+
} else {
361+
Length::decode(reader).map(Into::into)
362+
}
363+
}
364+
}
365+
366+
impl Encode for IndefiniteLength {
367+
fn encoded_len(&self) -> Result<Length> {
368+
match self.0 {
369+
Some(length) => length.encoded_len(),
370+
None => Ok(Length::ONE),
371+
}
372+
}
373+
374+
fn encode(&self, writer: &mut impl Writer) -> Result<()> {
375+
match self.0 {
376+
Some(length) => length.encode(writer),
377+
None => writer.write_byte(INDEFINITE_LENGTH_OCTET),
378+
}
379+
}
380+
}
381+
382+
impl From<Length> for IndefiniteLength {
383+
fn from(length: Length) -> IndefiniteLength {
384+
Self(Some(length))
385+
}
386+
}
387+
388+
impl TryFrom<IndefiniteLength> for Length {
389+
type Error = Error;
390+
391+
fn try_from(length: IndefiniteLength) -> Result<Length> {
392+
length.0.ok_or_else(|| ErrorKind::IndefiniteLength.into())
393+
}
394+
}
395+
302396
#[cfg(test)]
303397
mod tests {
304-
use super::Length;
398+
use super::{IndefiniteLength, Length};
305399
use crate::{Decode, DerOrd, Encode, ErrorKind};
306400
use core::cmp::Ordering;
307401

@@ -368,8 +462,22 @@ mod tests {
368462
}
369463

370464
#[test]
371-
fn reject_indefinite_lengths() {
465+
fn indefinite_lengths() {
466+
// DER disallows indefinite lengths
372467
assert!(Length::from_der(&[0x80]).is_err());
468+
469+
// The `IndefiniteLength` type supports them
470+
let indefinite_length = IndefiniteLength::from_der(&[0x80]).unwrap();
471+
assert!(indefinite_length.is_indefinite());
472+
assert_eq!(indefinite_length, IndefiniteLength::INDEFINITE);
473+
474+
// It also supports definite lengths.
475+
let length = IndefiniteLength::from_der(&[0x83, 0x01, 0x00, 0x00]).unwrap();
476+
assert!(length.is_definite());
477+
assert_eq!(
478+
Length::try_from(0x10000u32).unwrap(),
479+
length.try_into().unwrap()
480+
);
373481
}
374482

375483
#[test]

der/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ pub use crate::{
360360
encode_ref::{EncodeRef, EncodeValueRef},
361361
error::{Error, ErrorKind, Result},
362362
header::Header,
363-
length::Length,
363+
length::{IndefiniteLength, Length},
364364
ord::{DerOrd, ValueOrd},
365365
reader::{slice::SliceReader, Reader},
366366
tag::{Class, FixedTag, Tag, TagMode, TagNumber, Tagged},

0 commit comments

Comments
 (0)