Skip to content

Commit 7f98cc0

Browse files
authored
der: extract length::indefinite module (#1910)
Extracts a module containing everything related to indefinite length handling in a single place
1 parent bcc59be commit 7f98cc0

File tree

4 files changed

+185
-172
lines changed

4 files changed

+185
-172
lines changed

der/src/bytes_owned.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
use crate::{
55
BytesRef, DecodeValue, DerOrd, EncodeValue, Error, Header, Length, Reader, Result, StrRef,
6-
Writer, reader::read_constructed_vec, referenced::OwnedToRef,
6+
Writer, length::indefinite::read_constructed_vec, referenced::OwnedToRef,
77
};
88
use alloc::{boxed::Box, vec::Vec};
99
use core::cmp::Ordering;

der/src/length.rs

Lines changed: 7 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
//! Length calculations for encoded ASN.1 DER values
22
3+
pub(crate) mod indefinite;
4+
5+
use self::indefinite::INDEFINITE_LENGTH_OCTET;
36
use crate::{
4-
Decode, DerOrd, Encode, EncodingRules, Error, ErrorKind, Header, Reader, Result, SliceWriter,
5-
Tag, Writer,
7+
Decode, DerOrd, Encode, EncodingRules, Error, ErrorKind, Reader, Result, SliceWriter, Tag,
8+
Writer,
69
};
710
use core::{
811
cmp::Ordering,
912
fmt,
1013
ops::{Add, Sub},
1114
};
1215

13-
/// Octet identifying an indefinite length as described in X.690 Section
14-
/// 8.1.3.6.1:
15-
///
16-
/// > The single octet shall have bit 8 set to one, and bits 7 to
17-
/// > 1 set to zero.
18-
const INDEFINITE_LENGTH_OCTET: u8 = 0b10000000; // 0x80
19-
2016
/// ASN.1-encoded length.
2117
#[derive(Copy, Clone, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
2218
pub struct Length {
@@ -257,7 +253,7 @@ impl<'a> Decode<'a> for Length {
257253
// Note: per X.690 Section 8.1.3.6.1 the byte 0x80 encodes indefinite lengths
258254
INDEFINITE_LENGTH_OCTET => match reader.encoding_rules() {
259255
// Indefinite lengths are allowed when decoding BER
260-
EncodingRules::Ber => decode_indefinite_length(&mut reader.clone()),
256+
EncodingRules::Ber => indefinite::decode_indefinite_length(&mut reader.clone()),
261257
// Indefinite lengths are disallowed when decoding DER
262258
EncodingRules::Der => Err(reader.error(ErrorKind::IndefiniteLength)),
263259
},
@@ -370,75 +366,12 @@ impl<'a> arbitrary::Arbitrary<'a> for Length {
370366
}
371367
}
372368

373-
/// Decode indefinite lengths as used by ASN.1 BER and described in X.690 Section 8.1.3.6:
374-
///
375-
/// > 8.1.3.6 For the indefinite form, the length octets indicate that the
376-
/// > contents octets are terminated by end-of-contents
377-
/// > octets (see 8.1.5), and shall consist of a single octet.
378-
/// >
379-
/// > 8.1.3.6.1 The single octet shall have bit 8 set to one, and bits 7 to
380-
/// > 1 set to zero.
381-
/// >
382-
/// > 8.1.3.6.2 If this form of length is used, then end-of-contents octets
383-
/// > (see 8.1.5) shall be present in the encoding following the contents
384-
/// > octets.
385-
/// >
386-
/// > [...]
387-
/// >
388-
/// > 8.1.5 End-of-contents octets
389-
/// > The end-of-contents octets shall be present if the length is encoded as specified in 8.1.3.6,
390-
/// > otherwise they shall not be present.
391-
/// >
392-
/// > The end-of-contents octets shall consist of two zero octets.
393-
///
394-
/// This function decodes TLV records until it finds an end-of-contents marker (`00 00`), and
395-
/// computes the resulting length as the amount of data decoded.
396-
fn decode_indefinite_length<'a, R: Reader<'a>>(reader: &mut R) -> Result<Length> {
397-
/// The end-of-contents octets can be considered as the encoding of a value whose tag is
398-
/// universal class, whose form is primitive, whose number of the tag is zero, and whose
399-
/// contents are absent.
400-
const EOC_TAG: u8 = 0x00;
401-
402-
let start_pos = reader.position();
403-
404-
loop {
405-
// Look for the end-of-contents marker
406-
if reader.peek_byte() == Some(EOC_TAG) {
407-
read_eoc(reader)?;
408-
409-
// Compute how much we read and flag the decoded length as indefinite
410-
let mut ret = (reader.position() - start_pos)?;
411-
ret.indefinite = true;
412-
return Ok(ret);
413-
}
414-
415-
let header = Header::decode(reader)?;
416-
reader.drain(header.length)?;
417-
}
418-
}
419-
420-
/// Read an expected end-of-contents (EOC) marker: `00 00`.
421-
///
422-
/// # Errors
423-
///
424-
/// - Returns `ErrorKind::IndefiniteLength` if the EOC marker isn't present as expected.
425-
pub(crate) fn read_eoc<'a>(reader: &mut impl Reader<'a>) -> Result<()> {
426-
for _ in 0..Length::EOC_LEN.inner as usize {
427-
if reader.read_byte()? != 0 {
428-
return Err(reader.error(ErrorKind::IndefiniteLength));
429-
}
430-
}
431-
432-
Ok(())
433-
}
434-
435369
#[cfg(test)]
436370
#[allow(clippy::unwrap_used)]
437371
mod tests {
438372
use super::Length;
439-
use crate::{Decode, DerOrd, Encode, EncodingRules, ErrorKind, Reader, SliceReader, Tag};
373+
use crate::{Decode, DerOrd, Encode, ErrorKind};
440374
use core::cmp::Ordering;
441-
use hex_literal::hex;
442375

443376
#[test]
444377
fn decode() {
@@ -529,64 +462,4 @@ mod tests {
529462
Ordering::Greater
530463
);
531464
}
532-
533-
#[test]
534-
fn indefinite() {
535-
/// Length of example in octets.
536-
const EXAMPLE_LEN: usize = 68;
537-
538-
/// Test vector from: <https://github.com/RustCrypto/formats/issues/779#issuecomment-2902948789>
539-
///
540-
/// Notably this example contains nested indefinite lengths to ensure the decoder handles
541-
/// them correctly.
542-
const EXAMPLE_BER: [u8; EXAMPLE_LEN] = hex!(
543-
"30 80 06 09 2A 86 48 86 F7 0D 01 07 01
544-
30 1D 06 09 60 86 48 01 65 03 04 01 2A 04 10 37
545-
34 3D F1 47 0D F6 25 EE B6 F4 BF D2 F1 AC C3 A0
546-
80 04 10 CC 74 AD F6 5D 97 3C 8B 72 CD 51 E1 B9
547-
27 F0 F0 00 00 00 00"
548-
);
549-
550-
// Ensure the indefinite bit isn't set when decoding DER
551-
assert!(!Length::from_der(&[0x00]).unwrap().indefinite);
552-
553-
let mut reader =
554-
SliceReader::new_with_encoding_rules(&EXAMPLE_BER, EncodingRules::Ber).unwrap();
555-
556-
// Decode initial tag of the message, leaving the reader at the length
557-
let tag = Tag::decode(&mut reader).unwrap();
558-
assert_eq!(tag, Tag::Sequence);
559-
560-
// Decode indefinite length
561-
let length = Length::decode(&mut reader).unwrap();
562-
assert!(length.is_indefinite());
563-
564-
// Decoding the length should leave the position at the end of the indefinite length octet
565-
let pos = usize::try_from(reader.position()).unwrap();
566-
assert_eq!(pos, 2);
567-
568-
// The first two bytes are the header and the rest is the length of the message.
569-
// The last four are two end-of-content markers (2 * 2 bytes).
570-
assert_eq!(usize::try_from(length).unwrap(), EXAMPLE_LEN - pos);
571-
572-
// Read OID
573-
reader.tlv_bytes().unwrap();
574-
// Read SEQUENCE
575-
reader.tlv_bytes().unwrap();
576-
577-
// We're now at the next indefinite length record
578-
let tag = Tag::decode(&mut reader).unwrap();
579-
assert_eq!(
580-
tag,
581-
Tag::ContextSpecific {
582-
constructed: true,
583-
number: 0u32.into()
584-
}
585-
);
586-
587-
// Parse the inner indefinite length
588-
let length = Length::decode(&mut reader).unwrap();
589-
assert!(length.is_indefinite());
590-
assert_eq!(usize::try_from(length).unwrap(), 20);
591-
}
592465
}

der/src/length/indefinite.rs

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
//! Support for indefinite lengths as used by ASN.1 BER and described in X.690 Section 8.1.3.6:
2+
//!
3+
//! > 8.1.3.6 For the indefinite form, the length octets indicate that the
4+
//! > contents octets are terminated by end-of-contents
5+
//! > octets (see 8.1.5), and shall consist of a single octet.
6+
//! >
7+
//! > 8.1.3.6.1 The single octet shall have bit 8 set to one, and bits 7 to
8+
//! > 1 set to zero.
9+
//! >
10+
//! > 8.1.3.6.2 If this form of length is used, then end-of-contents octets
11+
//! > (see 8.1.5) shall be present in the encoding following the contents
12+
//! > octets.
13+
//! >
14+
//! > [...]
15+
//! >
16+
//! > 8.1.5 End-of-contents octets
17+
//! > The end-of-contents octets shall be present if the length is encoded as specified in 8.1.3.6,
18+
//! > otherwise they shall not be present.
19+
//! >
20+
//! > The end-of-contents octets shall consist of two zero octets.
21+
22+
use crate::{Decode, ErrorKind, Header, Length, Reader};
23+
24+
#[cfg(feature = "alloc")]
25+
use alloc::vec::Vec;
26+
27+
/// Octet identifying an indefinite length as described in X.690 Section
28+
/// 8.1.3.6.1:
29+
///
30+
/// > The single octet shall have bit 8 set to one, and bits 7 to
31+
/// > 1 set to zero.
32+
pub(super) const INDEFINITE_LENGTH_OCTET: u8 = 0b10000000; // 0x80
33+
34+
/// The end-of-contents octets can be considered as the encoding of a value whose tag is
35+
/// universal class, whose form is primitive, whose number of the tag is zero, and whose
36+
/// contents are absent.
37+
const EOC_TAG: u8 = 0x00;
38+
39+
/// Decode TLV records until an end-of-contents marker (`00 00`) is found, computing the
40+
/// resulting length based on the amount of data decoded.
41+
pub(super) fn decode_indefinite_length<'a>(reader: &mut impl Reader<'a>) -> crate::Result<Length> {
42+
let start_pos = reader.position();
43+
44+
loop {
45+
// Look for the end-of-contents marker
46+
if reader.peek_byte() == Some(EOC_TAG) {
47+
read_eoc(reader)?;
48+
49+
// Compute how much we read and flag the decoded length as indefinite
50+
let mut ret = (reader.position() - start_pos)?;
51+
ret.indefinite = true;
52+
return Ok(ret);
53+
}
54+
55+
let header = Header::decode(reader)?;
56+
reader.drain(header.length)?;
57+
}
58+
}
59+
60+
/// Read an expected end-of-contents (EOC) marker: `00 00`.
61+
///
62+
/// # Errors
63+
///
64+
/// - Returns `ErrorKind::IndefiniteLength` if the EOC marker isn't present as expected.
65+
pub(crate) fn read_eoc<'a>(reader: &mut impl Reader<'a>) -> crate::Result<()> {
66+
for _ in 0..Length::EOC_LEN.inner as usize {
67+
if reader.read_byte()? != 0 {
68+
return Err(reader.error(ErrorKind::IndefiniteLength));
69+
}
70+
}
71+
72+
Ok(())
73+
}
74+
75+
/// Read a constructed value into a [`Vec`], removing intermediate headers and assembling the result
76+
/// into a single contiguous bytestring.
77+
///
78+
/// The end-of-content marker is not handled by this function. Instead, it's expected for this to
79+
/// be called with a nested reader which ends immediately before the EOC.
80+
#[cfg(feature = "alloc")]
81+
pub(crate) fn read_constructed_vec<'r, R: Reader<'r>>(
82+
reader: &mut R,
83+
header: Header,
84+
) -> crate::Result<Vec<u8>> {
85+
if !header.length.is_indefinite() {
86+
return Err(reader.error(ErrorKind::IndefiniteLength));
87+
}
88+
89+
let mut bytes = Vec::with_capacity(header.length.try_into()?);
90+
let mut offset = 0;
91+
92+
while !reader.is_finished() {
93+
let h = Header::decode(reader)?;
94+
h.tag.assert_eq(header.tag)?;
95+
96+
// Indefinite length headers can't be indefinite
97+
if h.length.is_indefinite() {
98+
return Err(reader.error(ErrorKind::IndefiniteLength));
99+
}
100+
101+
// Add enough zeroes into the `Vec` to store the chunk
102+
let l = usize::try_from(h.length)?;
103+
bytes.extend(core::iter::repeat_n(0, l));
104+
reader.read_into(&mut bytes[offset..(offset + l)])?;
105+
offset += l;
106+
}
107+
108+
Ok(bytes)
109+
}
110+
111+
#[cfg(test)]
112+
#[allow(clippy::unwrap_used)]
113+
mod tests {
114+
use crate::{Decode, EncodingRules, Length, Reader, SliceReader, Tag};
115+
use hex_literal::hex;
116+
117+
#[test]
118+
fn decode() {
119+
/// Length of example in octets.
120+
const EXAMPLE_LEN: usize = 68;
121+
122+
/// Test vector from: <https://github.com/RustCrypto/formats/issues/779#issuecomment-2902948789>
123+
///
124+
/// Notably this example contains nested indefinite lengths to ensure the decoder handles
125+
/// them correctly.
126+
const EXAMPLE_BER: [u8; EXAMPLE_LEN] = hex!(
127+
"30 80 06 09 2A 86 48 86 F7 0D 01 07 01
128+
30 1D 06 09 60 86 48 01 65 03 04 01 2A 04 10 37
129+
34 3D F1 47 0D F6 25 EE B6 F4 BF D2 F1 AC C3 A0
130+
80 04 10 CC 74 AD F6 5D 97 3C 8B 72 CD 51 E1 B9
131+
27 F0 F0 00 00 00 00"
132+
);
133+
134+
// Ensure the indefinite bit isn't set when decoding DER
135+
assert!(!Length::from_der(&[0x00]).unwrap().indefinite);
136+
137+
let mut reader =
138+
SliceReader::new_with_encoding_rules(&EXAMPLE_BER, EncodingRules::Ber).unwrap();
139+
140+
// Decode initial tag of the message, leaving the reader at the length
141+
let tag = Tag::decode(&mut reader).unwrap();
142+
assert_eq!(tag, Tag::Sequence);
143+
144+
// Decode indefinite length
145+
let length = Length::decode(&mut reader).unwrap();
146+
assert!(length.is_indefinite());
147+
148+
// Decoding the length should leave the position at the end of the indefinite length octet
149+
let pos = usize::try_from(reader.position()).unwrap();
150+
assert_eq!(pos, 2);
151+
152+
// The first two bytes are the header and the rest is the length of the message.
153+
// The last four are two end-of-content markers (2 * 2 bytes).
154+
assert_eq!(usize::try_from(length).unwrap(), EXAMPLE_LEN - pos);
155+
156+
// Read OID
157+
reader.tlv_bytes().unwrap();
158+
// Read SEQUENCE
159+
reader.tlv_bytes().unwrap();
160+
161+
// We're now at the next indefinite length record
162+
let tag = Tag::decode(&mut reader).unwrap();
163+
assert_eq!(
164+
tag,
165+
Tag::ContextSpecific {
166+
constructed: true,
167+
number: 0u32.into()
168+
}
169+
);
170+
171+
// Parse the inner indefinite length
172+
let length = Length::decode(&mut reader).unwrap();
173+
assert!(length.is_indefinite());
174+
assert_eq!(usize::try_from(length).unwrap(), 20);
175+
}
176+
}

0 commit comments

Comments
 (0)