11//! Context-specific field.
22
33use 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 tag:: IsConstructed ,
77} ;
@@ -47,7 +47,10 @@ impl<T> ContextSpecific<T> {
4747 where
4848 T : Decode < ' a > ,
4949 {
50- Self :: decode_with ( reader, tag_number, |reader| Self :: decode ( reader) )
50+ if !peek_tag_matches ( reader, Class :: ContextSpecific , tag_number) ? {
51+ return Ok ( None ) ;
52+ }
53+ Ok ( Some ( Self :: decode ( reader) ?) )
5154 }
5255
5356 /// Attempt to decode an `IMPLICIT` ASN.1 `CONTEXT-SPECIFIC` field with the
@@ -63,53 +66,54 @@ impl<T> ContextSpecific<T> {
6366 where
6467 T : DecodeValue < ' a > + IsConstructed ,
6568 {
66- Self :: decode_with :: < _ , _ , T :: Error > ( reader, tag_number, |reader| {
67- // Decode IMPLICIT header
68- let header = Header :: decode ( reader) ?;
69-
70- // read_nested checks if header matches decoded length
71- let value = reader. read_nested ( header. length , |reader| {
72- // Decode inner IMPLICIT value
73- T :: decode_value ( reader, header)
74- } ) ?;
75-
76- if header. tag . is_constructed ( ) != T :: CONSTRUCTED {
77- return Err ( header. tag . non_canonical_error ( ) . into ( ) ) ;
78- }
79-
80- Ok ( Self {
81- tag_number,
82- tag_mode : TagMode :: Implicit ,
83- value,
84- } )
85- } )
86- }
69+ // Peek tag number
70+ if !peek_tag_matches :: < _ , T :: Error > ( reader, Class :: ContextSpecific , tag_number) ? {
71+ return Ok ( None ) ;
72+ }
73+ // Decode IMPLICIT header
74+ let header = Header :: decode ( reader) ?;
8775
88- /// Attempt to decode a context-specific field with the given
89- /// helper callback.
90- fn decode_with < ' a , F , R : Reader < ' a > , E > (
91- reader : & mut R ,
92- tag_number : TagNumber ,
93- f : F ,
94- ) -> Result < Option < Self > , E >
95- where
96- F : FnOnce ( & mut R ) -> Result < Self , E > ,
97- E : From < Error > ,
98- {
99- while let Some ( tag) = Tag :: peek_optional ( reader) ? {
100- if !tag. is_context_specific ( ) || ( tag. number ( ) > tag_number) {
101- break ;
102- } else if tag. number ( ) == tag_number {
103- return Some ( f ( reader) ) . transpose ( ) ;
104- } else {
105- AnyRef :: decode ( reader) ?;
106- }
76+ // read_nested checks if header matches decoded length
77+ let value = reader. read_nested ( header. length , |reader| {
78+ // Decode inner IMPLICIT value
79+ T :: decode_value ( reader, header)
80+ } ) ?;
81+
82+ // the encoding shall be constructed if the base encoding is constructed
83+ if header. tag . is_constructed ( ) != T :: CONSTRUCTED {
84+ return Err ( header. tag . non_canonical_error ( ) . into ( ) ) ;
10785 }
10886
109- Ok ( None )
87+ Ok ( Some ( Self {
88+ tag_number,
89+ tag_mode : TagMode :: Implicit ,
90+ value,
91+ } ) )
11092 }
11193}
11294
95+ /// Returns true if given context-specific (or any given class) field
96+ /// should be decoded, based on peeked tag.
97+ fn peek_tag_matches < ' a , R : Reader < ' a > , E > (
98+ reader : & mut R ,
99+ expected_class : Class ,
100+ expected_tag_number : TagNumber ,
101+ ) -> Result < bool , E >
102+ where
103+ E : From < Error > ,
104+ {
105+ // Peek tag or ignore end of stream
106+ let Some ( tag) = Tag :: peek_optional ( reader) ? else {
107+ return Ok ( false ) ;
108+ } ;
109+ // Ignore tags with different numbers
110+ if tag. class ( ) != expected_class || tag. number ( ) != expected_tag_number {
111+ return Ok ( false ) ;
112+ }
113+ // Tag matches
114+ Ok ( true )
115+ }
116+
113117impl < ' a , T > Choice < ' a > for ContextSpecific < T >
114118where
115119 T : Decode < ' a > + Tagged ,
@@ -132,6 +136,7 @@ where
132136 match header. tag {
133137 Tag :: ContextSpecific {
134138 number,
139+ // encoding shall be constructed
135140 constructed : true ,
136141 } => Ok ( Self {
137142 tag_number : number,
@@ -171,7 +176,17 @@ where
171176{
172177 fn tag ( & self ) -> Tag {
173178 let constructed = match self . tag_mode {
179+ // ISO/IEC 8825-1:2021
180+ // 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
181+ // encoding shall be constructed and the contents octets shall be the complete base encoding [Encode].
174182 TagMode :: Explicit => true ,
183+
184+ // ISO/IEC 8825-1:2021
185+ // 8.14.4 If implicit tagging was used in the definition of the type, then:
186+ // a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise; and
187+ // b) the contents octets shall be the same as the contents octets [EncodeValue] of the base encoding.
188+ //
189+ // TODO(dishmaker): use IsConstructed trait for IMPLICIT
175190 TagMode :: Implicit => self . value . tag ( ) . is_constructed ( ) ,
176191 } ;
177192
@@ -349,13 +364,11 @@ mod tests {
349364 }
350365
351366 #[ test]
352- fn context_specific_skipping_unknown_field ( ) {
367+ fn context_specific_not_skipping_unknown_field ( ) {
353368 let tag = TagNumber ( 1 ) ;
354369 let mut reader = SliceReader :: new ( & hex ! ( "A003020100A103020101" ) ) . unwrap ( ) ;
355- let field = ContextSpecific :: < u8 > :: decode_explicit ( & mut reader, tag)
356- . unwrap ( )
357- . unwrap ( ) ;
358- assert_eq ! ( field. value, 1 ) ;
370+ let field = ContextSpecific :: < u8 > :: decode_explicit ( & mut reader, tag) . unwrap ( ) ;
371+ assert_eq ! ( field, None ) ;
359372 }
360373
361374 #[ test]
0 commit comments