From f8a4c45f5198a3d7bca3b41aacf323d2bb079b2e Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Sat, 10 May 2025 17:38:22 +0200 Subject: [PATCH] der: fix `Header::peek` + add tests --- der/src/header.rs | 46 ++++++++++++++++++++++++++++++++++++---------- der/src/length.rs | 17 ++++++++++++++++- der/src/tag.rs | 8 ++++++-- 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/der/src/header.rs b/der/src/header.rs index 4a2bfbf20..0964174ff 100644 --- a/der/src/header.rs +++ b/der/src/header.rs @@ -33,14 +33,13 @@ impl Header { for i in 2..Self::MAX_SIZE { let slice = &mut buf[0..i]; - if reader.peek_into(slice).is_ok() { - if let Ok(header) = Self::from_der(slice) { - return Ok(header); - } + reader.peek_into(slice)?; + if let Ok(header) = Self::from_der(slice) { + return Ok(header); } } - - Self::from_der(&buf) + reader.peek_into(&mut buf[..])?; + Self::from_der(&buf[..]) } } @@ -85,21 +84,48 @@ impl DerOrd for Header { #[cfg(test)] mod tests { use super::Header; - use crate::{Length, Reader, SliceReader, Tag}; + use crate::{Encode, Length, Reader, SliceReader, Tag, TagNumber}; use hex_literal::hex; #[test] - #[allow(clippy::unwrap_used)] fn peek() { // INTEGER: 42 const EXAMPLE_MSG: &[u8] = &hex!("02012A00"); - let reader = SliceReader::new(EXAMPLE_MSG).unwrap(); + let reader = SliceReader::new(EXAMPLE_MSG).expect("slice to be valid length"); assert_eq!(reader.position(), Length::ZERO); - let header = Header::peek(&reader).unwrap(); + let header = Header::peek(&reader).expect("peeked tag"); assert_eq!(header.tag, Tag::Integer); assert_eq!(header.length, Length::ONE); assert_eq!(reader.position(), Length::ZERO); // Position unchanged } + + #[test] + fn peek_max_header() { + const MAX_HEADER: [u8; 11] = hex!("BF8FFFFFFF7F 84FFFFFFFF"); + let reader = SliceReader::new(&MAX_HEADER).expect("slice to be valid length"); + + let header = Header::peek(&reader).expect("peeked tag"); + assert_eq!( + header.tag, + Tag::ContextSpecific { + constructed: true, + number: TagNumber(0xFFFFFFFF) + } + ); + assert_eq!( + header.length, + Length::new_usize(0xFFFFFFFF).expect("u32 to fit") + ); + assert_eq!(header.encoded_len(), Ok(Length::new(11))); + assert_eq!(reader.position(), Length::ZERO); // Position unchanged + } + #[test] + fn negative_peek_overlength_header() { + const MAX_HEADER: [u8; 12] = hex!("BF8FFFFFFFFF7F 84FFFFFFFF"); + let reader = SliceReader::new(&MAX_HEADER).expect("slice to be valid length"); + // Should not decode + Header::peek(&reader).expect_err("overlength error"); + } } diff --git a/der/src/length.rs b/der/src/length.rs index 345431d54..d1a2b16da 100644 --- a/der/src/length.rs +++ b/der/src/length.rs @@ -340,11 +340,15 @@ mod tests { Length::from(0x10000u32), Length::from_der(&[0x83, 0x01, 0x00, 0x00]).unwrap() ); + assert_eq!( + Length::from(0xFFFFFFFFu32), + Length::from_der(&[0x84, 0xFF, 0xFF, 0xFF, 0xFF]).unwrap() + ); } #[test] fn encode() { - let mut buffer = [0u8; 4]; + let mut buffer = [0u8; 5]; assert_eq!(&[0x00], Length::ZERO.encode_to_slice(&mut buffer).unwrap()); @@ -374,6 +378,12 @@ mod tests { .encode_to_slice(&mut buffer) .unwrap() ); + assert_eq!( + &[0x84, 0xFF, 0xFF, 0xFF, 0xFF], + Length::from(0xFFFFFFFFu32) + .encode_to_slice(&mut buffer) + .unwrap() + ); } #[test] @@ -388,5 +398,10 @@ mod tests { #[test] fn der_ord() { assert_eq!(Length::ONE.der_cmp(&Length::MAX).unwrap(), Ordering::Less); + assert_eq!(Length::ONE.der_cmp(&Length::ONE).unwrap(), Ordering::Equal); + assert_eq!( + Length::ONE.der_cmp(&Length::ZERO).unwrap(), + Ordering::Greater + ); } } diff --git a/der/src/tag.rs b/der/src/tag.rs index 54ff3012a..6477edde5 100644 --- a/der/src/tag.rs +++ b/der/src/tag.rs @@ -169,8 +169,8 @@ impl Tag { pub(crate) fn peek_optional<'a>(reader: &impl Reader<'a>) -> Result> { let mut buf = [0u8; Self::MAX_SIZE]; - if reader.peek_into(&mut buf[0..1]).is_err() { + // Ignore empty buffer return Ok(None); } @@ -750,8 +750,12 @@ mod tests { #[test] fn peek_long_tags() { let reader = SliceReader::new(&hex!("DF8FFFFFFF7F")).expect("valid reader"); + let tag = Tag::peek(&reader).expect("peeked tag"); + assert!(!tag.is_context_specific()); + assert!(!tag.is_application()); + assert!(tag.is_private()); assert_eq!( - Tag::peek(&reader).expect("peeked tag"), + tag, Tag::Private { constructed: false, number: TagNumber(u32::MAX)