1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.apksig.internal.asn1;
18 
19 import com.android.apksig.internal.asn1.ber.BerDataValue;
20 import com.android.apksig.internal.asn1.ber.BerDataValueFormatException;
21 import com.android.apksig.internal.asn1.ber.BerDataValueReader;
22 import com.android.apksig.internal.asn1.ber.BerEncoding;
23 import com.android.apksig.internal.asn1.ber.ByteBufferBerDataValueReader;
24 import com.android.apksig.internal.util.ByteBufferUtils;
25 
26 import java.lang.reflect.Field;
27 import java.lang.reflect.Modifier;
28 import java.math.BigInteger;
29 import java.nio.ByteBuffer;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
33 
34 /**
35  * Parser of ASN.1 BER-encoded structures.
36  *
37  * <p>Structure is described to the parser by providing a class annotated with {@link Asn1Class},
38  * containing fields annotated with {@link Asn1Field}.
39  */
40 public final class Asn1BerParser {
Asn1BerParser()41     private Asn1BerParser() {}
42 
43     /**
44      * Returns the ASN.1 structure contained in the BER encoded input.
45      *
46      * @param encoded encoded input. If the decoding operation succeeds, the position of this buffer
47      *        is advanced to the first position following the end of the consumed structure.
48      * @param containerClass class describing the structure of the input. The class must meet the
49      *        following requirements:
50      *        <ul>
51      *        <li>The class must be annotated with {@link Asn1Class}.</li>
52      *        <li>The class must expose a public no-arg constructor.</li>
53      *        <li>Member fields of the class which are populated with parsed input must be
54      *            annotated with {@link Asn1Field} and be public and non-final.</li>
55      *        </ul>
56      *
57      * @throws Asn1DecodingException if the input could not be decoded into the specified Java
58      *         object
59      */
parse(ByteBuffer encoded, Class<T> containerClass)60     public static <T> T parse(ByteBuffer encoded, Class<T> containerClass)
61             throws Asn1DecodingException {
62         BerDataValue containerDataValue;
63         try {
64             containerDataValue = new ByteBufferBerDataValueReader(encoded).readDataValue();
65         } catch (BerDataValueFormatException e) {
66             throw new Asn1DecodingException("Failed to decode top-level data value", e);
67         }
68         if (containerDataValue == null) {
69             throw new Asn1DecodingException("Empty input");
70         }
71         return parse(containerDataValue, containerClass);
72     }
73 
74     /**
75      * Returns the implicit {@code SET OF} contained in the provided ASN.1 BER input. Implicit means
76      * that this method does not care whether the tag number of this data structure is
77      * {@code SET OF} and whether the tag class is {@code UNIVERSAL}.
78      *
79      * <p>Note: The returned type is {@link List} rather than {@link java.util.Set} because ASN.1
80      * SET may contain duplicate elements.
81      *
82      * @param encoded encoded input. If the decoding operation succeeds, the position of this buffer
83      *        is advanced to the first position following the end of the consumed structure.
84      * @param elementClass class describing the structure of the values/elements contained in this
85      *        container. The class must meet the following requirements:
86      *        <ul>
87      *        <li>The class must be annotated with {@link Asn1Class}.</li>
88      *        <li>The class must expose a public no-arg constructor.</li>
89      *        <li>Member fields of the class which are populated with parsed input must be
90      *            annotated with {@link Asn1Field} and be public and non-final.</li>
91      *        </ul>
92      *
93      * @throws Asn1DecodingException if the input could not be decoded into the specified Java
94      *         object
95      */
parseImplicitSetOf(ByteBuffer encoded, Class<T> elementClass)96     public static <T> List<T> parseImplicitSetOf(ByteBuffer encoded, Class<T> elementClass)
97             throws Asn1DecodingException {
98         BerDataValue containerDataValue;
99         try {
100             containerDataValue = new ByteBufferBerDataValueReader(encoded).readDataValue();
101         } catch (BerDataValueFormatException e) {
102             throw new Asn1DecodingException("Failed to decode top-level data value", e);
103         }
104         if (containerDataValue == null) {
105             throw new Asn1DecodingException("Empty input");
106         }
107         return parseSetOf(containerDataValue, elementClass);
108     }
109 
parse(BerDataValue container, Class<T> containerClass)110     private static <T> T parse(BerDataValue container, Class<T> containerClass)
111             throws Asn1DecodingException {
112         if (container == null) {
113             throw new NullPointerException("container == null");
114         }
115         if (containerClass == null) {
116             throw new NullPointerException("containerClass == null");
117         }
118 
119         Asn1Type dataType = getContainerAsn1Type(containerClass);
120         switch (dataType) {
121             case CHOICE:
122                 return parseChoice(container, containerClass);
123 
124             case SEQUENCE:
125             {
126                 int expectedTagClass = BerEncoding.TAG_CLASS_UNIVERSAL;
127                 int expectedTagNumber = BerEncoding.getTagNumber(dataType);
128                 if ((container.getTagClass() != expectedTagClass)
129                         || (container.getTagNumber() != expectedTagNumber)) {
130                     throw new Asn1UnexpectedTagException(
131                             "Unexpected data value read as " + containerClass.getName()
132                             + ". Expected " + BerEncoding.tagClassAndNumberToString(
133                                     expectedTagClass, expectedTagNumber)
134                             + ", but read: " + BerEncoding.tagClassAndNumberToString(
135                                     container.getTagClass(), container.getTagNumber()));
136                 }
137                 return parseSequence(container, containerClass);
138             }
139 
140             default:
141                 throw new Asn1DecodingException("Parsing container " + dataType + " not supported");
142         }
143     }
144 
parseChoice(BerDataValue dataValue, Class<T> containerClass)145     private static <T> T parseChoice(BerDataValue dataValue, Class<T> containerClass)
146             throws Asn1DecodingException {
147         List<AnnotatedField> fields = getAnnotatedFields(containerClass);
148         if (fields.isEmpty()) {
149             throw new Asn1DecodingException(
150                     "No fields annotated with " + Asn1Field.class.getName()
151                             + " in CHOICE class " + containerClass.getName());
152         }
153 
154         // Check that class + tagNumber don't clash between the choices
155         for (int i = 0; i < fields.size() - 1; i++) {
156             AnnotatedField f1 = fields.get(i);
157             int tagNumber1 = f1.getBerTagNumber();
158             int tagClass1 = f1.getBerTagClass();
159             for (int j = i + 1; j < fields.size(); j++) {
160                 AnnotatedField f2 = fields.get(j);
161                 int tagNumber2 = f2.getBerTagNumber();
162                 int tagClass2 = f2.getBerTagClass();
163                 if ((tagNumber1 == tagNumber2) && (tagClass1 == tagClass2)) {
164                     throw new Asn1DecodingException(
165                             "CHOICE fields are indistinguishable because they have the same tag"
166                                     + " class and number: " + containerClass.getName()
167                                     + "." + f1.getField().getName()
168                                     + " and ." + f2.getField().getName());
169                 }
170             }
171         }
172 
173         // Instantiate the container object / result
174         T obj;
175         try {
176             obj = containerClass.getConstructor().newInstance();
177         } catch (IllegalArgumentException | ReflectiveOperationException e) {
178             throw new Asn1DecodingException("Failed to instantiate " + containerClass.getName(), e);
179         }
180 
181         // Set the matching field's value from the data value
182         for (AnnotatedField field : fields) {
183             try {
184                 field.setValueFrom(dataValue, obj);
185                 return obj;
186             } catch (Asn1UnexpectedTagException expected) {
187                 // not a match
188             }
189         }
190 
191         throw new Asn1DecodingException(
192                 "No options of CHOICE " + containerClass.getName() + " matched");
193     }
194 
parseSequence(BerDataValue container, Class<T> containerClass)195     private static <T> T parseSequence(BerDataValue container, Class<T> containerClass)
196             throws Asn1DecodingException {
197         List<AnnotatedField> fields = getAnnotatedFields(containerClass);
198         Collections.sort(
199                 fields, (f1, f2) -> f1.getAnnotation().index() - f2.getAnnotation().index());
200         // Check that there are no fields with the same index
201         if (fields.size() > 1) {
202             AnnotatedField lastField = null;
203             for (AnnotatedField field : fields) {
204                 if ((lastField != null)
205                         && (lastField.getAnnotation().index() == field.getAnnotation().index())) {
206                     throw new Asn1DecodingException(
207                             "Fields have the same index: " + containerClass.getName()
208                                     + "." + lastField.getField().getName()
209                                     + " and ." + field.getField().getName());
210                 }
211                 lastField = field;
212             }
213         }
214 
215         // Instantiate the container object / result
216         T t;
217         try {
218             t = containerClass.getConstructor().newInstance();
219         } catch (IllegalArgumentException | ReflectiveOperationException e) {
220             throw new Asn1DecodingException("Failed to instantiate " + containerClass.getName(), e);
221         }
222 
223         // Parse fields one by one. A complication is that there may be optional fields.
224         int nextUnreadFieldIndex = 0;
225         BerDataValueReader elementsReader = container.contentsReader();
226         while (nextUnreadFieldIndex < fields.size()) {
227             BerDataValue dataValue;
228             try {
229                 dataValue = elementsReader.readDataValue();
230             } catch (BerDataValueFormatException e) {
231                 throw new Asn1DecodingException("Malformed data value", e);
232             }
233             if (dataValue == null) {
234                 break;
235             }
236 
237             for (int i = nextUnreadFieldIndex; i < fields.size(); i++) {
238                 AnnotatedField field = fields.get(i);
239                 try {
240                     if (field.isOptional()) {
241                         // Optional field -- might not be present and we may thus be trying to set
242                         // it from the wrong tag.
243                         try {
244                             field.setValueFrom(dataValue, t);
245                             nextUnreadFieldIndex = i + 1;
246                             break;
247                         } catch (Asn1UnexpectedTagException e) {
248                             // This field is not present, attempt to use this data value for the
249                             // next / iteration of the loop
250                             continue;
251                         }
252                     } else {
253                         // Mandatory field -- if we can't set its value from this data value, then
254                         // it's an error
255                         field.setValueFrom(dataValue, t);
256                         nextUnreadFieldIndex = i + 1;
257                         break;
258                     }
259                 } catch (Asn1DecodingException e) {
260                     throw new Asn1DecodingException(
261                             "Failed to parse " + containerClass.getName()
262                                     + "." + field.getField().getName(),
263                             e);
264                 }
265             }
266         }
267 
268         return t;
269     }
270 
271     // NOTE: This method returns List rather than Set because ASN.1 SET_OF does require uniqueness
272     // of elements -- it's an unordered collection.
273     @SuppressWarnings("unchecked")
parseSetOf(BerDataValue container, Class<T> elementClass)274     private static <T> List<T> parseSetOf(BerDataValue container, Class<T> elementClass)
275             throws Asn1DecodingException {
276         List<T> result = new ArrayList<>();
277         BerDataValueReader elementsReader = container.contentsReader();
278         while (true) {
279             BerDataValue dataValue;
280             try {
281                 dataValue = elementsReader.readDataValue();
282             } catch (BerDataValueFormatException e) {
283                 throw new Asn1DecodingException("Malformed data value", e);
284             }
285             if (dataValue == null) {
286                 break;
287             }
288             T element;
289             if (ByteBuffer.class.equals(elementClass)) {
290                 element = (T) dataValue.getEncodedContents();
291             } else if (Asn1OpaqueObject.class.equals(elementClass)) {
292                 element = (T) new Asn1OpaqueObject(dataValue.getEncoded());
293             } else {
294                 element = parse(dataValue, elementClass);
295             }
296             result.add(element);
297         }
298         return result;
299     }
300 
getContainerAsn1Type(Class<?> containerClass)301     private static Asn1Type getContainerAsn1Type(Class<?> containerClass)
302             throws Asn1DecodingException {
303         Asn1Class containerAnnotation = containerClass.getDeclaredAnnotation(Asn1Class.class);
304         if (containerAnnotation == null) {
305             throw new Asn1DecodingException(
306                     containerClass.getName() + " is not annotated with "
307                             + Asn1Class.class.getName());
308         }
309 
310         switch (containerAnnotation.type()) {
311             case CHOICE:
312             case SEQUENCE:
313                 return containerAnnotation.type();
314             default:
315                 throw new Asn1DecodingException(
316                         "Unsupported ASN.1 container annotation type: "
317                                 + containerAnnotation.type());
318         }
319     }
320 
getElementType(Field field)321     private static Class<?> getElementType(Field field)
322             throws Asn1DecodingException, ClassNotFoundException {
323         String type = field.getGenericType().getTypeName();
324         int delimiterIndex =  type.indexOf('<');
325         if (delimiterIndex == -1) {
326             throw new Asn1DecodingException("Not a container type: " + field.getGenericType());
327         }
328         int startIndex = delimiterIndex + 1;
329         int endIndex = type.indexOf('>', startIndex);
330         // TODO: handle comma?
331         if (endIndex == -1) {
332             throw new Asn1DecodingException("Not a container type: " + field.getGenericType());
333         }
334         String elementClassName = type.substring(startIndex, endIndex);
335         return Class.forName(elementClassName);
336     }
337 
338     private static final class AnnotatedField {
339         private final Field mField;
340         private final Asn1Field mAnnotation;
341         private final Asn1Type mDataType;
342         private final Asn1TagClass mTagClass;
343         private final int mBerTagClass;
344         private final int mBerTagNumber;
345         private final Asn1Tagging mTagging;
346         private final boolean mOptional;
347 
AnnotatedField(Field field, Asn1Field annotation)348         public AnnotatedField(Field field, Asn1Field annotation) throws Asn1DecodingException {
349             mField = field;
350             mAnnotation = annotation;
351             mDataType = annotation.type();
352 
353             Asn1TagClass tagClass = annotation.cls();
354             if (tagClass == Asn1TagClass.AUTOMATIC) {
355                 if (annotation.tagNumber() != -1) {
356                     tagClass = Asn1TagClass.CONTEXT_SPECIFIC;
357                 } else {
358                     tagClass = Asn1TagClass.UNIVERSAL;
359                 }
360             }
361             mTagClass = tagClass;
362             mBerTagClass = BerEncoding.getTagClass(mTagClass);
363 
364             int tagNumber;
365             if (annotation.tagNumber() != -1) {
366                 tagNumber = annotation.tagNumber();
367             } else if ((mDataType == Asn1Type.CHOICE) || (mDataType == Asn1Type.ANY)) {
368                 tagNumber = -1;
369             } else {
370                 tagNumber = BerEncoding.getTagNumber(mDataType);
371             }
372             mBerTagNumber = tagNumber;
373 
374             mTagging = annotation.tagging();
375             if (((mTagging == Asn1Tagging.EXPLICIT) || (mTagging == Asn1Tagging.IMPLICIT))
376                     && (annotation.tagNumber() == -1)) {
377                 throw new Asn1DecodingException(
378                         "Tag number must be specified when tagging mode is " + mTagging);
379             }
380 
381             mOptional = annotation.optional();
382         }
383 
getField()384         public Field getField() {
385             return mField;
386         }
387 
getAnnotation()388         public Asn1Field getAnnotation() {
389             return mAnnotation;
390         }
391 
isOptional()392         public boolean isOptional() {
393             return mOptional;
394         }
395 
getBerTagClass()396         public int getBerTagClass() {
397             return mBerTagClass;
398         }
399 
getBerTagNumber()400         public int getBerTagNumber() {
401             return mBerTagNumber;
402         }
403 
setValueFrom(BerDataValue dataValue, Object obj)404         public void setValueFrom(BerDataValue dataValue, Object obj) throws Asn1DecodingException {
405             int readTagClass = dataValue.getTagClass();
406             if (mBerTagNumber != -1) {
407                 int readTagNumber = dataValue.getTagNumber();
408                 if ((readTagClass != mBerTagClass) || (readTagNumber != mBerTagNumber)) {
409                     throw new Asn1UnexpectedTagException(
410                             "Tag mismatch. Expected: "
411                             + BerEncoding.tagClassAndNumberToString(mBerTagClass, mBerTagNumber)
412                             + ", but found "
413                             + BerEncoding.tagClassAndNumberToString(readTagClass, readTagNumber));
414                 }
415             } else {
416                 if (readTagClass != mBerTagClass) {
417                     throw new Asn1UnexpectedTagException(
418                             "Tag mismatch. Expected class: "
419                             + BerEncoding.tagClassToString(mBerTagClass)
420                             + ", but found "
421                             + BerEncoding.tagClassToString(readTagClass));
422                 }
423             }
424 
425             if (mTagging == Asn1Tagging.EXPLICIT) {
426                 try {
427                     dataValue = dataValue.contentsReader().readDataValue();
428                 } catch (BerDataValueFormatException e) {
429                     throw new Asn1DecodingException(
430                             "Failed to read contents of EXPLICIT data value", e);
431                 }
432             }
433 
434             BerToJavaConverter.setFieldValue(obj, mField, mDataType, dataValue);
435         }
436     }
437 
438     private static class Asn1UnexpectedTagException extends Asn1DecodingException {
439         private static final long serialVersionUID = 1L;
440 
Asn1UnexpectedTagException(String message)441         public Asn1UnexpectedTagException(String message) {
442             super(message);
443         }
444     }
445 
oidToString(ByteBuffer encodedOid)446     private static String oidToString(ByteBuffer encodedOid) throws Asn1DecodingException {
447         if (!encodedOid.hasRemaining()) {
448             throw new Asn1DecodingException("Empty OBJECT IDENTIFIER");
449         }
450 
451         // First component encodes the first two nodes, X.Y, as X * 40 + Y, with 0 <= X <= 2
452         long firstComponent = decodeBase128UnsignedLong(encodedOid);
453         int firstNode = (int) Math.min(firstComponent / 40, 2);
454         long secondNode = firstComponent - firstNode * 40;
455         StringBuilder result = new StringBuilder();
456         result.append(Long.toString(firstNode)).append('.')
457                 .append(Long.toString(secondNode));
458 
459         // Each consecutive node is encoded as a separate component
460         while (encodedOid.hasRemaining()) {
461             long node = decodeBase128UnsignedLong(encodedOid);
462             result.append('.').append(Long.toString(node));
463         }
464 
465         return result.toString();
466     }
467 
decodeBase128UnsignedLong(ByteBuffer encoded)468     private static long decodeBase128UnsignedLong(ByteBuffer encoded) throws Asn1DecodingException {
469         if (!encoded.hasRemaining()) {
470             return 0;
471         }
472         long result = 0;
473         while (encoded.hasRemaining()) {
474             if (result > Long.MAX_VALUE >>> 7) {
475                 throw new Asn1DecodingException("Base-128 number too large");
476             }
477             int b = encoded.get() & 0xff;
478             result <<= 7;
479             result |= b & 0x7f;
480             if ((b & 0x80) == 0) {
481                 return result;
482             }
483         }
484         throw new Asn1DecodingException(
485                 "Truncated base-128 encoded input: missing terminating byte, with highest bit not"
486                         + " set");
487     }
488 
integerToBigInteger(ByteBuffer encoded)489     private static BigInteger integerToBigInteger(ByteBuffer encoded) {
490         if (!encoded.hasRemaining()) {
491             return BigInteger.ZERO;
492         }
493         return new BigInteger(ByteBufferUtils.toByteArray(encoded));
494     }
495 
integerToInt(ByteBuffer encoded)496     private static int integerToInt(ByteBuffer encoded) throws Asn1DecodingException {
497         BigInteger value = integerToBigInteger(encoded);
498         try {
499             return value.intValueExact();
500         } catch (ArithmeticException e) {
501             throw new Asn1DecodingException(
502                     String.format("INTEGER cannot be represented as int: %1$d (0x%1$x)", value), e);
503         }
504     }
505 
integerToLong(ByteBuffer encoded)506     private static long integerToLong(ByteBuffer encoded) throws Asn1DecodingException {
507         BigInteger value = integerToBigInteger(encoded);
508         try {
509             return value.longValueExact();
510         } catch (ArithmeticException e) {
511             throw new Asn1DecodingException(
512                     String.format("INTEGER cannot be represented as long: %1$d (0x%1$x)", value),
513                     e);
514         }
515     }
516 
getAnnotatedFields(Class<?> containerClass)517     private static List<AnnotatedField> getAnnotatedFields(Class<?> containerClass)
518             throws Asn1DecodingException {
519         Field[] declaredFields = containerClass.getDeclaredFields();
520         List<AnnotatedField> result = new ArrayList<>(declaredFields.length);
521         for (Field field : declaredFields) {
522             Asn1Field annotation = field.getDeclaredAnnotation(Asn1Field.class);
523             if (annotation == null) {
524                 continue;
525             }
526             if (Modifier.isStatic(field.getModifiers())) {
527                 throw new Asn1DecodingException(
528                         Asn1Field.class.getName() + " used on a static field: "
529                                 + containerClass.getName() + "." + field.getName());
530             }
531 
532             AnnotatedField annotatedField;
533             try {
534                 annotatedField = new AnnotatedField(field, annotation);
535             } catch (Asn1DecodingException e) {
536                 throw new Asn1DecodingException(
537                         "Invalid ASN.1 annotation on "
538                                 + containerClass.getName() + "." + field.getName(),
539                         e);
540             }
541             result.add(annotatedField);
542         }
543         return result;
544     }
545 
546     private static final class BerToJavaConverter {
BerToJavaConverter()547         private BerToJavaConverter() {}
548 
setFieldValue( Object obj, Field field, Asn1Type type, BerDataValue dataValue)549         public static void setFieldValue(
550                 Object obj, Field field, Asn1Type type, BerDataValue dataValue)
551                         throws Asn1DecodingException {
552             try {
553                 switch (type) {
554                     case SET_OF:
555                     case SEQUENCE_OF:
556                         if (Asn1OpaqueObject.class.equals(field.getType())) {
557                             field.set(obj, convert(type, dataValue, field.getType()));
558                         } else {
559                             field.set(obj, parseSetOf(dataValue, getElementType(field)));
560                         }
561                         return;
562                     default:
563                         field.set(obj, convert(type, dataValue, field.getType()));
564                         break;
565                 }
566             } catch (ReflectiveOperationException e) {
567                 throw new Asn1DecodingException(
568                         "Failed to set value of " + obj.getClass().getName()
569                                 + "." + field.getName(),
570                         e);
571             }
572         }
573 
574         private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
575 
576         @SuppressWarnings("unchecked")
convert( Asn1Type sourceType, BerDataValue dataValue, Class<T> targetType)577         public static <T> T convert(
578                 Asn1Type sourceType,
579                 BerDataValue dataValue,
580                 Class<T> targetType) throws Asn1DecodingException {
581             if (ByteBuffer.class.equals(targetType)) {
582                 return (T) dataValue.getEncodedContents();
583             } else if (byte[].class.equals(targetType)) {
584                 ByteBuffer resultBuf = dataValue.getEncodedContents();
585                 if (!resultBuf.hasRemaining()) {
586                     return (T) EMPTY_BYTE_ARRAY;
587                 }
588                 byte[] result = new byte[resultBuf.remaining()];
589                 resultBuf.get(result);
590                 return (T) result;
591             } else if (Asn1OpaqueObject.class.equals(targetType)) {
592                 return (T) new Asn1OpaqueObject(dataValue.getEncoded());
593             }
594 
595             ByteBuffer encodedContents = dataValue.getEncodedContents();
596             switch (sourceType) {
597                 case INTEGER:
598                     if ((int.class.equals(targetType)) || (Integer.class.equals(targetType))) {
599                         return (T) Integer.valueOf(integerToInt(encodedContents));
600                     } else if ((long.class.equals(targetType)) || (Long.class.equals(targetType))) {
601                         return (T) Long.valueOf(integerToLong(encodedContents));
602                     } else if (BigInteger.class.equals(targetType)) {
603                         return (T) integerToBigInteger(encodedContents);
604                     }
605                     break;
606                 case OBJECT_IDENTIFIER:
607                     if (String.class.equals(targetType)) {
608                         return (T) oidToString(encodedContents);
609                     }
610                     break;
611                 case SEQUENCE:
612                 {
613                     Asn1Class containerAnnotation =
614                             targetType.getDeclaredAnnotation(Asn1Class.class);
615                     if ((containerAnnotation != null)
616                             && (containerAnnotation.type() == Asn1Type.SEQUENCE)) {
617                         return parseSequence(dataValue, targetType);
618                     }
619                     break;
620                 }
621                 case CHOICE:
622                 {
623                     Asn1Class containerAnnotation =
624                             targetType.getDeclaredAnnotation(Asn1Class.class);
625                     if ((containerAnnotation != null)
626                             && (containerAnnotation.type() == Asn1Type.CHOICE)) {
627                         return parseChoice(dataValue, targetType);
628                     }
629                     break;
630                 }
631                 default:
632                     break;
633             }
634 
635             throw new Asn1DecodingException(
636                     "Unsupported conversion: ASN.1 " + sourceType + " to " + targetType.getName());
637         }
638     }
639 }
640