Skip to content

Commit 5239f9e

Browse files
committed
der: rename decode_with -> peek_tag_matches
der: replace tag skip condition to `tag.number() != tag_number` der: add class param to peek_decode_optional der: peek_tag_matches bool
1 parent d933c0d commit 5239f9e

File tree

1 file changed

+59
-48
lines changed

1 file changed

+59
-48
lines changed

der/src/asn1/context_specific.rs

+59-48
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Context-specific field.
22
33
use crate::{
4-
Choice, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, Error, Header,
4+
Choice, Class, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, Error, Header,
55
Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer, asn1::AnyRef,
66
};
77
use core::cmp::Ordering;
@@ -46,7 +46,10 @@ impl<T> ContextSpecific<T> {
4646
where
4747
T: Decode<'a>,
4848
{
49-
Self::decode_with(reader, tag_number, |reader| Self::decode(reader))
49+
if !peek_tag_matches(reader, Class::ContextSpecific, tag_number)? {
50+
return Ok(None);
51+
}
52+
Ok(Some(Self::decode(reader)?))
5053
}
5154

5255
/// Attempt to decode an `IMPLICIT` ASN.1 `CONTEXT-SPECIFIC` field with the
@@ -62,53 +65,54 @@ impl<T> ContextSpecific<T> {
6265
where
6366
T: DecodeValue<'a> + Tagged,
6467
{
65-
Self::decode_with::<_, _, T::Error>(reader, tag_number, |reader| {
66-
// Decode IMPLICIT header
67-
let header = Header::decode(reader)?;
68-
69-
// read_nested checks if header matches decoded length
70-
let value = reader.read_nested(header.length, |reader| {
71-
// Decode inner IMPLICIT value
72-
T::decode_value(reader, header)
73-
})?;
74-
75-
if header.tag.is_constructed() != value.tag().is_constructed() {
76-
return Err(header.tag.non_canonical_error().into());
77-
}
78-
79-
Ok(Self {
80-
tag_number,
81-
tag_mode: TagMode::Implicit,
82-
value,
83-
})
84-
})
85-
}
68+
// Peek tag number
69+
if !peek_tag_matches::<_, T::Error>(reader, Class::ContextSpecific, tag_number)? {
70+
return Ok(None);
71+
}
72+
// Decode IMPLICIT header
73+
let header = Header::decode(reader)?;
8674

87-
/// Attempt to decode a context-specific field with the given
88-
/// helper callback.
89-
fn decode_with<'a, F, R: Reader<'a>, E>(
90-
reader: &mut R,
91-
tag_number: TagNumber,
92-
f: F,
93-
) -> Result<Option<Self>, E>
94-
where
95-
F: FnOnce(&mut R) -> Result<Self, E>,
96-
E: From<Error>,
97-
{
98-
while let Some(tag) = Tag::peek_optional(reader)? {
99-
if !tag.is_context_specific() || (tag.number() > tag_number) {
100-
break;
101-
} else if tag.number() == tag_number {
102-
return Some(f(reader)).transpose();
103-
} else {
104-
AnyRef::decode(reader)?;
105-
}
75+
// read_nested checks if header matches decoded length
76+
let value = reader.read_nested(header.length, |reader| {
77+
// Decode inner IMPLICIT value
78+
T::decode_value(reader, header)
79+
})?;
80+
81+
// the encoding shall be constructed if the base encoding is constructed
82+
if header.tag.is_constructed() != value.tag().is_constructed() {
83+
return Err(header.tag.non_canonical_error().into());
10684
}
10785

108-
Ok(None)
86+
Ok(Some(Self {
87+
tag_number,
88+
tag_mode: TagMode::Implicit,
89+
value,
90+
}))
10991
}
11092
}
11193

94+
/// Returns true if given context-specific (or any given class) field
95+
/// should be decoded, based on peeked tag.
96+
fn peek_tag_matches<'a, R: Reader<'a>, E>(
97+
reader: &mut R,
98+
expected_class: Class,
99+
expected_tag_number: TagNumber,
100+
) -> Result<bool, E>
101+
where
102+
E: From<Error>,
103+
{
104+
// Peek tag or ignore end of stream
105+
let Some(tag) = Tag::peek_optional(reader)? else {
106+
return Ok(false);
107+
};
108+
// Ignore tags with different numbers
109+
if tag.class() != expected_class || tag.number() != expected_tag_number {
110+
return Ok(false);
111+
}
112+
// Tag matches
113+
Ok(true)
114+
}
115+
112116
impl<'a, T> Choice<'a> for ContextSpecific<T>
113117
where
114118
T: Decode<'a> + Tagged,
@@ -131,6 +135,7 @@ where
131135
match header.tag {
132136
Tag::ContextSpecific {
133137
number,
138+
// encoding shall be constructed
134139
constructed: true,
135140
} => Ok(Self {
136141
tag_number: number,
@@ -170,7 +175,15 @@ where
170175
{
171176
fn tag(&self) -> Tag {
172177
let constructed = match self.tag_mode {
178+
// ISO/IEC 8825-1:2021
179+
// 8.14.3 If implicit tagging (see Rec. ITU-T X.680 | ISO/IEC 8824-1, 31.2.7) was not used in the definition of the type, the
180+
// encoding shall be constructed and the contents octets shall be the complete base encoding [Encode].
173181
TagMode::Explicit => true,
182+
183+
// ISO/IEC 8825-1:2021
184+
// 8.14.4 If implicit tagging was used in the definition of the type, then:
185+
// a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise; and
186+
// b) the contents octets shall be the same as the contents octets [EncodeValue] of the base encoding.
174187
TagMode::Implicit => self.value.tag().is_constructed(),
175188
};
176189

@@ -348,13 +361,11 @@ mod tests {
348361
}
349362

350363
#[test]
351-
fn context_specific_skipping_unknown_field() {
364+
fn context_specific_not_skipping_unknown_field() {
352365
let tag = TagNumber(1);
353366
let mut reader = SliceReader::new(&hex!("A003020100A103020101")).unwrap();
354-
let field = ContextSpecific::<u8>::decode_explicit(&mut reader, tag)
355-
.unwrap()
356-
.unwrap();
357-
assert_eq!(field.value, 1);
367+
let field = ContextSpecific::<u8>::decode_explicit(&mut reader, tag).unwrap();
368+
assert_eq!(field, None);
358369
}
359370

360371
#[test]

0 commit comments

Comments
 (0)