|
1 | 1 | //! Length calculations for encoded ASN.1 DER values
|
2 | 2 |
|
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 | +}; |
4 | 7 | use core::{
|
5 | 8 | cmp::Ordering,
|
6 | 9 | fmt,
|
@@ -211,10 +214,14 @@ impl<'a> Decode<'a> for Length {
|
211 | 214 |
|
212 | 215 | fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Length> {
|
213 | 216 | 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. |
216 | 217 | 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 | + }, |
218 | 225 | // 1-4 byte variable-sized length prefix
|
219 | 226 | tag @ 0x81..=0x84 => {
|
220 | 227 | let nbytes = tag.checked_sub(0x80).ok_or(ErrorKind::Overlength)? as usize;
|
@@ -308,12 +315,65 @@ impl<'a> arbitrary::Arbitrary<'a> for Length {
|
308 | 315 | }
|
309 | 316 | }
|
310 | 317 |
|
| 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 | + |
311 | 370 | #[cfg(test)]
|
312 | 371 | #[allow(clippy::unwrap_used)]
|
313 | 372 | mod tests {
|
314 | 373 | use super::Length;
|
315 |
| - use crate::{Decode, DerOrd, Encode, ErrorKind}; |
| 374 | + use crate::{Decode, DerOrd, Encode, EncodingRules, ErrorKind, Reader, SliceReader, Tag}; |
316 | 375 | use core::cmp::Ordering;
|
| 376 | + use hex_literal::hex; |
317 | 377 |
|
318 | 378 | #[test]
|
319 | 379 | fn decode() {
|
@@ -404,4 +464,39 @@ mod tests {
|
404 | 464 | Ordering::Greater
|
405 | 465 | );
|
406 | 466 | }
|
| 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 | + } |
407 | 502 | }
|
0 commit comments