@@ -7,8 +7,9 @@ use pyo3::types::{PyAnyMethods, PyListMethods};
77
88use crate :: asn1:: big_byte_slice_to_py_int;
99use crate :: declarative_asn1:: types:: {
10- check_size_constraint, type_to_tag, AnnotatedType , Annotation , BitString , Encoding ,
11- GeneralizedTime , IA5String , PrintableString , Type , UtcTime ,
10+ check_size_constraint, expected_tags_for_type, expected_tags_for_variant, AnnotatedType ,
11+ Annotation , BitString , Encoding , GeneralizedTime , IA5String , PrintableString , Type , UtcTime ,
12+ Variant ,
1213} ;
1314use crate :: error:: CryptographyError ;
1415
@@ -160,6 +161,46 @@ fn decode_bitstring<'a>(
160161 ) ?)
161162}
162163
164+ // Utility function to handle explicit encoding when parsing
165+ // CHOICE fields.
166+ fn decode_choice_with_encoding < ' a > (
167+ py : pyo3:: Python < ' a > ,
168+ parser : & mut Parser < ' a > ,
169+ ann_type : & AnnotatedType ,
170+ encoding : & Encoding ,
171+ ) -> ParseResult < pyo3:: Bound < ' a , pyo3:: PyAny > > {
172+ match encoding {
173+ Encoding :: Implicit ( _) => Err ( CryptographyError :: Py (
174+ pyo3:: exceptions:: PyValueError :: new_err (
175+ "invalid type definition: CHOICE fields cannot be implicitly encoded" . to_string ( ) ,
176+ ) ,
177+ ) ) ?,
178+ Encoding :: Explicit ( n) => {
179+ // Since we don't know which of the variants is present for this
180+ // CHOICE field, we'll parse this as a generic TLV encoded with
181+ // EXPLICIT, so `read_explicit_element` will consume the EXPLICIT
182+ // wrapper tag, and the TLV data will contain the variant.
183+ let tlv = parser. read_explicit_element :: < asn1:: Tlv < ' _ > > ( * n) ?;
184+ let type_without_explicit = AnnotatedType {
185+ inner : ann_type. inner . clone_ref ( py) ,
186+ annotation : pyo3:: Py :: new (
187+ py,
188+ Annotation {
189+ default : None ,
190+ encoding : None ,
191+ size : None ,
192+ } ,
193+ ) ?,
194+ } ;
195+ // Parse the TLV data (which contains the field without the EXPLICIT
196+ // wrapper)
197+ asn1:: parse ( tlv. full_data ( ) , |d| {
198+ decode_annotated_type ( py, d, & type_without_explicit)
199+ } )
200+ }
201+ }
202+ }
203+
163204pub ( crate ) fn decode_annotated_type < ' a > (
164205 py : pyo3:: Python < ' a > ,
165206 parser : & mut Parser < ' a > ,
@@ -172,10 +213,10 @@ pub(crate) fn decode_annotated_type<'a>(
172213 // Handle DEFAULT annotation if field is not present (by
173214 // returning the default value)
174215 if let Some ( default) = & ann_type. annotation . get ( ) . default {
175- let expected_tag = type_to_tag ( inner, encoding) ;
176- let next_tag = parser. peek_tag ( ) ;
177- if next_tag != Some ( expected_tag ) {
178- return Ok ( default. clone_ref ( py) . into_bound ( py) ) ;
216+ let expected_tags = expected_tags_for_type ( py , inner, encoding) ;
217+ match parser. peek_tag ( ) {
218+ Some ( next_tag ) if expected_tags . contains ( & next_tag) => ( ) ,
219+ _ => return Ok ( default. clone_ref ( py) . into_bound ( py) ) ,
179220 }
180221 }
181222
@@ -210,9 +251,9 @@ pub(crate) fn decode_annotated_type<'a>(
210251 } ) ?
211252 }
212253 Type :: Option ( cls) => {
213- let inner_tag = type_to_tag ( cls. get ( ) . inner . get ( ) , encoding) ;
254+ let expected_tags = expected_tags_for_type ( py , cls. get ( ) . inner . get ( ) , encoding) ;
214255 match parser. peek_tag ( ) {
215- Some ( t) if t == inner_tag => {
256+ Some ( t) if expected_tags . contains ( & t ) => {
216257 // For optional types, annotations will always be associated to the `Optional` type
217258 // i.e: `Annotated[Optional[T], annotation]`, as opposed to the inner `T` type.
218259 // Therefore, when decoding the inner type `T` we must pass the annotation of the `Optional`
@@ -225,6 +266,34 @@ pub(crate) fn decode_annotated_type<'a>(
225266 _ => pyo3:: types:: PyNone :: get ( py) . to_owned ( ) . into_any ( ) ,
226267 }
227268 }
269+ Type :: Choice ( ts) => match encoding {
270+ Some ( e) => decode_choice_with_encoding ( py, parser, ann_type, e. get ( ) ) ?,
271+ None => {
272+ for t in ts. bind ( py) {
273+ let variant = t. cast :: < Variant > ( ) ?. get ( ) ;
274+ let expected_tags = expected_tags_for_variant ( py, variant) ;
275+ match parser. peek_tag ( ) {
276+ Some ( tag) if expected_tags. contains ( & tag) => {
277+ let decoded_value =
278+ decode_annotated_type ( py, parser, variant. ann_type . get ( ) ) ?;
279+ return match & variant. tag_name {
280+ Some ( tag_name) => Ok ( variant
281+ . python_class
282+ . call1 ( py, ( decoded_value, tag_name) ) ?
283+ . into_bound ( py) ) ,
284+ None => Ok ( decoded_value) ,
285+ } ;
286+ }
287+ _ => continue ,
288+ }
289+ }
290+ Err ( CryptographyError :: Py (
291+ pyo3:: exceptions:: PyValueError :: new_err (
292+ "could not find matching variant when parsing CHOICE field" . to_string ( ) ,
293+ ) ,
294+ ) ) ?
295+ }
296+ } ,
228297 Type :: PyBool ( ) => decode_pybool ( py, parser, encoding) ?. into_any ( ) ,
229298 Type :: PyInt ( ) => decode_pyint ( py, parser, encoding) ?. into_any ( ) ,
230299 Type :: PyBytes ( ) => decode_pybytes ( py, parser, annotation) ?. into_any ( ) ,
@@ -246,3 +315,33 @@ pub(crate) fn decode_annotated_type<'a>(
246315 _ => Ok ( decoded) ,
247316 }
248317}
318+
319+ #[ cfg( test) ]
320+ mod tests {
321+ use crate :: declarative_asn1:: types:: { AnnotatedType , Annotation , Encoding , Type , Variant } ;
322+ #[ test]
323+ fn test_decode_implicit_choice ( ) {
324+ pyo3:: Python :: initialize ( ) ;
325+ pyo3:: Python :: attach ( |py| {
326+ let result = asn1:: parse ( & [ ] , |parser| {
327+ let variants: Vec < Variant > = vec ! [ ] ;
328+ let choice = Type :: Choice ( pyo3:: types:: PyList :: new ( py, variants) ?. unbind ( ) ) ;
329+ let annotation = Annotation {
330+ default : None ,
331+ encoding : None ,
332+ size : None ,
333+ } ;
334+ let ann_type = AnnotatedType {
335+ inner : pyo3:: Py :: new ( py, choice) ?,
336+ annotation : pyo3:: Py :: new ( py, annotation) ?,
337+ } ;
338+ let encoding = Encoding :: Implicit ( 0 ) ;
339+ super :: decode_choice_with_encoding ( py, parser, & ann_type, & encoding)
340+ } ) ;
341+ assert ! ( result. is_err( ) ) ;
342+ let error = result. unwrap_err ( ) ;
343+ assert ! ( format!( "{error}" )
344+ . contains( "invalid type definition: CHOICE fields cannot be implicitly encoded" ) ) ;
345+ } ) ;
346+ }
347+ }
0 commit comments