Reorder functions within file.
authorjsing <jsing@openbsd.org>
Thu, 19 May 2022 19:45:18 +0000 (19:45 +0000)
committerjsing <jsing@openbsd.org>
Thu, 19 May 2022 19:45:18 +0000 (19:45 +0000)
Order functions by use, moving public API to the bottom and utility
functions to the top. This makes the code more logical/readable, plus we
can remove all except one of the static function prototypes.

No functional change.

lib/libcrypto/asn1/tasn_dec.c

index 79cbd4c..7583019 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tasn_dec.c,v 1.71 2022/05/19 19:31:39 jsing Exp $ */
+/* $OpenBSD: tasn_dec.c,v 1.72 2022/05/19 19:45:18 jsing Exp $ */
 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
  * project 2000.
  */
 #include "asn1_locl.h"
 #include "bytestring.h"
 
-/* Constructed types with a recursive definition (such as can be found in PKCS7)
+/*
+ * Constructed types with a recursive definition (such as can be found in PKCS7)
  * could eventually exceed the stack given malicious input with excessive
  * recursion. Therefore we limit the stack depth.
  */
 #define ASN1_MAX_CONSTRUCTED_NEST 30
 
-static int asn1_check_eoc(CBS *cbs);
-static int asn1_find_end(CBS *cbs, size_t length, char indefinite);
-
-static int asn1_collect(CBB *cbb, CBS *cbs, char indefinite, int expected_tag,
-    int expected_class, int depth);
+#ifndef ASN1_MAX_STRING_NEST
+/*
+ * This determines how many levels of recursion are permitted in ASN.1 string
+ * types. If it is not limited stack overflows can occur. If set to zero no
+ * recursion is allowed at all. Although zero should be adequate examples exist
+ * that require a value of 1. So 5 should be more than enough.
+ */
+#define ASN1_MAX_STRING_NEST 5
+#endif
 
 static int asn1_template_ex_d2i(ASN1_VALUE **pval, CBS *cbs,
     const ASN1_TEMPLATE *tt, char optional, int depth);
-static int asn1_template_noexp_d2i(ASN1_VALUE **pval, CBS *cbs,
-    const ASN1_TEMPLATE *tt, char optional, int depth);
-static int asn1_d2i_ex_mstring(ASN1_VALUE **pval, CBS *CBS,
-    const ASN1_ITEM *it, int tag_number, int tag_class, char optional);
-static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs,
-    const ASN1_ITEM *it, int tag_number, int tag_class, char optional);
-static int asn1_ex_c2i(ASN1_VALUE **pval, CBS *content, int utype,
-    const ASN1_ITEM *it);
-
-static int asn1_check_tag(CBS *cbs, size_t *out_len, int *out_tag,
-    uint8_t *out_class, char *out_indefinite, char *out_constructed,
-    int expected_tag, int expected_class, char optional);
-
-ASN1_VALUE *
-ASN1_item_d2i(ASN1_VALUE **pval, const unsigned char **in, long inlen,
-    const ASN1_ITEM *it)
-{
-       ASN1_VALUE *ptmpval = NULL;
-
-       if (pval == NULL)
-               pval = &ptmpval;
-       if (ASN1_item_ex_d2i(pval, in, inlen, it, -1, 0, 0, 0) <= 0)
-               return NULL;
 
-       return *pval;
-}
-
-int
-ASN1_template_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
-    const ASN1_TEMPLATE *tt)
+static int
+asn1_check_eoc(CBS *cbs)
 {
-       CBS cbs;
-       int ret;
+       uint16_t eoc;
 
-       if (len < 0)
+       if (!CBS_peek_u16(cbs, &eoc))
+               return 0;
+       if (eoc != 0)
                return 0;
 
-       CBS_init(&cbs, *in, len);
-       if ((ret = asn1_template_ex_d2i(pval, &cbs, tt, 0, 0)) == 1)
-               *in = CBS_data(&cbs);
-
-       return ret;
+       return CBS_skip(cbs, 2);
 }
 
 static int
-asn1_item_ex_d2i_choice(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
-    int tag_number, int tag_class, char optional, int depth)
+asn1_check_tag(CBS *cbs, size_t *out_len, int *out_tag, uint8_t *out_class,
+    char *out_indefinite, char *out_constructed, int expected_tag,
+    int expected_class, char optional)
 {
-       const ASN1_TEMPLATE *tt, *errtt = NULL;
-       const ASN1_AUX *aux;
-       ASN1_aux_cb *asn1_cb = NULL;
-       ASN1_VALUE *achoice = NULL;
-       ASN1_VALUE **pchptr;
-       int i, ret;
-
-       if ((aux = it->funcs) != NULL)
-               asn1_cb = aux->asn1_cb;
+       int constructed, indefinite;
+       uint32_t tag_number;
+       uint8_t tag_class;
+       size_t length;
 
-       if (it->itype != ASN1_ITYPE_CHOICE)
-               goto err;
+       if (out_len != NULL)
+               *out_len = 0;
+       if (out_tag != NULL)
+               *out_tag = 0;
+       if (out_class != NULL)
+               *out_class = 0;
+       if (out_indefinite != NULL)
+               *out_indefinite = 0;
+       if (out_constructed != NULL)
+               *out_constructed = 0;
 
-       /*
-        * It never makes sense for CHOICE types to have implicit tagging, so
-        * if tag_number != -1, then this looks like an error in the template.
-        */
-       if (tag_number != -1) {
-               ASN1error(ASN1_R_BAD_TEMPLATE);
-               goto err;
+       if (!asn1_get_identifier_cbs(cbs, 0, &tag_class, &constructed,
+           &tag_number)) {
+               ASN1error(ASN1_R_BAD_OBJECT_HEADER);
+               return 0;
        }
+       if (expected_tag >= 0) {
+               if (expected_tag != tag_number ||
+                   expected_class != tag_class << 6) {
+                       /* Indicate missing type if this is OPTIONAL. */
+                       if (optional)
+                               return -1;
 
-       if (*pval != NULL) {
-               ASN1_item_ex_free(pval, it);
-               *pval = NULL;
+                       ASN1error(ASN1_R_WRONG_TAG);
+                       return 0;
+               }
        }
-
-       if (!ASN1_item_ex_new(&achoice, it)) {
-               ASN1error(ERR_R_NESTED_ASN1_ERROR);
-               goto err;
+       if (!asn1_get_length_cbs(cbs, 0, &indefinite, &length)) {
+               ASN1error(ASN1_R_BAD_OBJECT_HEADER);
+               return 0;
        }
 
-       if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_PRE, &achoice, it, NULL)) {
-               ASN1error(ASN1_R_AUX_ERROR);
-               goto err;
+       /* Indefinite length can only be used with constructed encoding. */
+       if (indefinite && !constructed) {
+               ASN1error(ASN1_R_BAD_OBJECT_HEADER);
+               return 0;
        }
 
-       /* Try each possible CHOICE in turn. */
-       for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) {
-               pchptr = asn1_get_field_ptr(&achoice, tt);
-
-               /* Mark field as OPTIONAL so its absence can be identified. */
-               ret = asn1_template_ex_d2i(pchptr, cbs, tt, 1, depth);
-               if (ret == -1)
-                       continue;
-               if (ret != 1) {
-                       ASN1error(ERR_R_NESTED_ASN1_ERROR);
-                       errtt = tt;
-                       goto err;
-               }
-
-               /* We've successfully decoded an ASN.1 object. */
-               asn1_set_choice_selector(&achoice, i, it);
-               break;
+       if (!indefinite && CBS_len(cbs) < length) {
+               ASN1error(ASN1_R_TOO_LONG);
+               return 0;
        }
 
-       /* Did we fall off the end without reading anything? */
-       if (i == it->tcount) {
-               if (optional) {
-                       ASN1_item_ex_free(&achoice, it);
-                       return -1;
-               }
-               ASN1error(ASN1_R_NO_MATCHING_CHOICE_TYPE);
-               goto err;
+       if (tag_number > INT_MAX) {
+               ASN1error(ASN1_R_TOO_LONG);
+               return 0;
        }
 
-       if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_POST, &achoice, it, NULL)) {
-               ASN1error(ASN1_R_AUX_ERROR);
-               goto err;
-       }
+       if (indefinite)
+               length = CBS_len(cbs);
 
-       *pval = achoice;
-       achoice = NULL;
+       if (out_len != NULL)
+               *out_len = length;
+       if (out_tag != NULL)
+               *out_tag = tag_number;
+       if (out_class != NULL)
+               *out_class = tag_class << 6;
+       if (out_indefinite != NULL && indefinite)
+               *out_indefinite = 1 << 0;
+       if (out_constructed != NULL && constructed)
+               *out_constructed = 1 << 5;
 
        return 1;
-
- err:
-       ASN1_item_ex_free(&achoice, it);
-
-       if (errtt)
-               ERR_asprintf_error_data("Field=%s, Type=%s", errtt->field_name,
-                   it->sname);
-       else
-               ERR_asprintf_error_data("Type=%s", it->sname);
-
-       return 0;
 }
 
+/* Collect the contents from a constructed ASN.1 object. */
 static int
-asn1_item_ex_d2i_sequence(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
-    int tag_number, int tag_class, char optional, int depth)
+asn1_collect(CBB *cbb, CBS *cbs, char indefinite, int expected_tag,
+    int expected_class, int depth)
 {
-       CBS cbs_seq, cbs_seq_content, cbs_object;
-       char constructed, indefinite, optional_field;
-       const ASN1_TEMPLATE *errtt = NULL;
-       const ASN1_TEMPLATE *seqtt, *tt;
-       ASN1_aux_cb *asn1_cb = NULL;
-       const ASN1_AUX *aux;
-       ASN1_VALUE *aseq = NULL;
-       ASN1_VALUE **pseqval;
-       int eoc_needed, i;
+       char constructed;
        size_t length;
-       int ret = 0;
-
-       CBS_init(&cbs_seq, CBS_data(cbs), CBS_len(cbs));
+       CBS content;
+       int need_eoc;
 
-       if ((aux = it->funcs) != NULL)
-               asn1_cb = aux->asn1_cb;
+       if (depth > ASN1_MAX_STRING_NEST) {
+               ASN1error(ASN1_R_NESTED_ASN1_STRING);
+               return 0;
+       }
 
-       if (it->itype != ASN1_ITYPE_NDEF_SEQUENCE &&
-           it->itype != ASN1_ITYPE_SEQUENCE)
-               goto err;
+       need_eoc = indefinite;
 
-       if (*pval != NULL) {
-               ASN1_item_ex_free(pval, it);
-               *pval = NULL;
-       }
+       while (CBS_len(cbs) > 0) {
+               if (asn1_check_eoc(cbs)) {
+                       if (!need_eoc) {
+                               ASN1error(ASN1_R_UNEXPECTED_EOC);
+                               return 0;
+                       }
+                       return 1;
+               }
+               if (!asn1_check_tag(cbs, &length, NULL, NULL, &indefinite,
+                   &constructed, expected_tag, expected_class, 0)) {
+                       ASN1error(ERR_R_NESTED_ASN1_ERROR);
+                       return 0;
+               }
 
-       /* If no IMPLICIT tagging use UNIVERSAL/SEQUENCE. */
-       if (tag_number == -1) {
-               tag_class = V_ASN1_UNIVERSAL;
-               tag_number = V_ASN1_SEQUENCE;
-       }
+               if (constructed) {
+                       if (!asn1_collect(cbb, cbs, indefinite, expected_tag,
+                           expected_class, depth + 1))
+                               return 0;
+                       continue;
+               }
 
-       /* Read ASN.1 SEQUENCE header. */
-       ret = asn1_check_tag(&cbs_seq, &length, NULL, NULL, &indefinite,
-           &constructed, tag_number, tag_class, optional);
-       if (ret == -1)
-               return -1;
-       if (ret != 1) {
-               ASN1error(ERR_R_NESTED_ASN1_ERROR);
-               goto err;
+               if (!CBS_get_bytes(cbs, &content, length)) {
+                       ASN1error(ERR_R_NESTED_ASN1_ERROR);
+                       return 0;
+               }
+               if (!CBB_add_bytes(cbb, CBS_data(&content), CBS_len(&content)))
+                       return 0;
        }
 
-       if (!constructed) {
-               ASN1error(ASN1_R_SEQUENCE_NOT_CONSTRUCTED);
-               goto err;
+       if (need_eoc) {
+               ASN1error(ASN1_R_MISSING_EOC);
+               return 0;
        }
 
-       if (indefinite) {
-               eoc_needed = 1;
-               CBS_init(&cbs_seq_content, CBS_data(&cbs_seq), CBS_len(&cbs_seq));
-       } else {
-               eoc_needed = 0;
-               if (!CBS_get_bytes(&cbs_seq, &cbs_seq_content, length))
-                       goto err;
-       }
+       return 1;
+}
 
-       if (!ASN1_item_ex_new(&aseq, it)) {
-               ASN1error(ERR_R_NESTED_ASN1_ERROR);
-               goto err;
-       }
-
-       if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_PRE, &aseq, it, NULL)) {
-               ASN1error(ASN1_R_AUX_ERROR);
-               goto err;
-       }
+/* Find the end of an ASN.1 object. */
+static int
+asn1_find_end(CBS *cbs, size_t length, char indefinite)
+{
+       size_t eoc_count;
 
-       for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) {
-               if (asn1_check_eoc(&cbs_seq_content)) {
-                       if (!indefinite) {
-                               ASN1error(ASN1_R_UNEXPECTED_EOC);
-                               goto err;
-                       }
-                       eoc_needed = 0;
-                       break;
+       if (!indefinite) {
+               if (!CBS_skip(cbs, length)) {
+                       ASN1error(ERR_R_NESTED_ASN1_ERROR);
+                       return 0;
                }
-               if (CBS_len(&cbs_seq_content) == 0)
-                       break;
-
-               if ((seqtt = asn1_do_adb(&aseq, tt, 1)) == NULL)
-                       goto err;
-
-               pseqval = asn1_get_field_ptr(&aseq, seqtt);
+               return 1;
+       }
 
-               /*
-                * This was originally implemented to "increase efficiency",
-                * however it currently needs to remain since it papers over
-                * the use of ASN.1 ANY with OPTIONAL in SEQUENCEs (which
-                * asn1_d2i_ex_primitive() currently rejects).
-                */
-               optional_field = (seqtt->flags & ASN1_TFLG_OPTIONAL) != 0;
-               if (i == it->tcount - 1)
-                       optional_field = 0;
+       eoc_count = 1;
 
-               ret = asn1_template_ex_d2i(pseqval, &cbs_seq_content,
-                   seqtt, optional_field, depth);
-               if (ret == -1) {
-                       /* Absent OPTIONAL component. */
-                       ASN1_template_free(pseqval, seqtt);
+       while (CBS_len(cbs) > 0) {
+               if (asn1_check_eoc(cbs)) {
+                       if (--eoc_count == 0)
+                               break;
                        continue;
                }
-               if (ret != 1) {
-                       errtt = seqtt;
-                       goto err;
+               if (!asn1_check_tag(cbs, &length, NULL, NULL,
+                   &indefinite, NULL, -1, 0, 0)) {
+                       ASN1error(ERR_R_NESTED_ASN1_ERROR);
+                       return 0;
+               }
+               if (indefinite) {
+                       eoc_count++;
+                       continue;
                }
+               if (!CBS_skip(cbs, length))
+                       return 0;
        }
 
-       if (eoc_needed && !asn1_check_eoc(&cbs_seq_content)) {
+       if (eoc_count > 0) {
                ASN1error(ASN1_R_MISSING_EOC);
-               goto err;
+               return 0;
        }
 
-       if (indefinite) {
-               if (!CBS_skip(&cbs_seq, CBS_offset(&cbs_seq_content)))
+       return 1;
+}
+
+static int
+asn1_ex_c2i_primitive(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it)
+{
+       ASN1_STRING *stmp;
+       ASN1_INTEGER **tint;
+       ASN1_BOOLEAN *tbool;
+       uint8_t u8val;
+       int ret = 0;
+
+       if (it->funcs != NULL)
+               return 0;
+
+       if (CBS_len(content) > INT_MAX)
+               return 0;
+
+       switch (utype) {
+       case V_ASN1_OBJECT:
+               if (!c2i_ASN1_OBJECT_cbs((ASN1_OBJECT **)pval, content))
                        goto err;
-       } else if (CBS_len(&cbs_seq_content) != 0) {
-               ASN1error(ASN1_R_SEQUENCE_LENGTH_MISMATCH);
-               goto err;
-       }
+               break;
 
-       /*
-        * There is no more data in the ASN.1 SEQUENCE, however we may not have
-        * populated all fields - check that any remaining are OPTIONAL.
-        */
-       for (; i < it->tcount; tt++, i++) {
-               if ((seqtt = asn1_do_adb(&aseq, tt, 1)) == NULL)
+       case V_ASN1_NULL:
+               if (CBS_len(content) != 0) {
+                       ASN1error(ASN1_R_NULL_IS_WRONG_LENGTH);
                        goto err;
+               }
+               *pval = (ASN1_VALUE *)1;
+               break;
 
-               if ((seqtt->flags & ASN1_TFLG_OPTIONAL) == 0) {
-                       ASN1error(ASN1_R_FIELD_MISSING);
-                       errtt = seqtt;
+       case V_ASN1_BOOLEAN:
+               tbool = (ASN1_BOOLEAN *)pval;
+               if (CBS_len(content) != 1) {
+                       ASN1error(ASN1_R_BOOLEAN_IS_WRONG_LENGTH);
                        goto err;
                }
+               if (!CBS_get_u8(content, &u8val))
+                       goto err;
+               *tbool = u8val;
+               break;
 
-               /* XXX - this is probably unnecessary with earlier free. */
-               pseqval = asn1_get_field_ptr(&aseq, seqtt);
-               ASN1_template_free(pseqval, seqtt);
+       case V_ASN1_BIT_STRING:
+               if (!c2i_ASN1_BIT_STRING_cbs((ASN1_BIT_STRING **)pval, content))
+                       goto err;
+               break;
+
+       case V_ASN1_INTEGER:
+       case V_ASN1_ENUMERATED:
+               tint = (ASN1_INTEGER **)pval;
+               if (!c2i_ASN1_INTEGER_cbs(tint, content))
+                       goto err;
+               /* Fixup type to match the expected form */
+               (*tint)->type = utype | ((*tint)->type & V_ASN1_NEG);
+               break;
+
+       case V_ASN1_OCTET_STRING:
+       case V_ASN1_NUMERICSTRING:
+       case V_ASN1_PRINTABLESTRING:
+       case V_ASN1_T61STRING:
+       case V_ASN1_VIDEOTEXSTRING:
+       case V_ASN1_IA5STRING:
+       case V_ASN1_UTCTIME:
+       case V_ASN1_GENERALIZEDTIME:
+       case V_ASN1_GRAPHICSTRING:
+       case V_ASN1_VISIBLESTRING:
+       case V_ASN1_GENERALSTRING:
+       case V_ASN1_UNIVERSALSTRING:
+       case V_ASN1_BMPSTRING:
+       case V_ASN1_UTF8STRING:
+       case V_ASN1_OTHER:
+       case V_ASN1_SET:
+       case V_ASN1_SEQUENCE:
+       default:
+               if (utype == V_ASN1_BMPSTRING && (CBS_len(content) & 1)) {
+                       ASN1error(ASN1_R_BMPSTRING_IS_WRONG_LENGTH);
+                       goto err;
+               }
+               if (utype == V_ASN1_UNIVERSALSTRING && (CBS_len(content) & 3)) {
+                       ASN1error(ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH);
+                       goto err;
+               }
+               /* All based on ASN1_STRING and handled the same way. */
+               if (*pval == NULL) {
+                       if ((stmp = ASN1_STRING_type_new(utype)) == NULL) {
+                               ASN1error(ERR_R_MALLOC_FAILURE);
+                               goto err;
+                       }
+                       *pval = (ASN1_VALUE *)stmp;
+               } else {
+                       stmp = (ASN1_STRING *)*pval;
+                       stmp->type = utype;
+               }
+               if (!ASN1_STRING_set(stmp, CBS_data(content), CBS_len(content))) {
+                       ASN1_STRING_free(stmp);
+                       *pval = NULL;
+                       goto err;
+               }
+               break;
        }
 
-       if (!CBS_get_bytes(cbs, &cbs_object, CBS_offset(&cbs_seq)))
-               goto err;
+       ret = 1;
 
-       if (!asn1_enc_save(&aseq, &cbs_object, it)) {
-               ASN1error(ERR_R_MALLOC_FAILURE);
-               goto err;
+ err:
+       return ret;
+}
+
+static int
+asn1_ex_c2i_any(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it)
+{
+       ASN1_TYPE *atype;
+
+       if (it->utype != V_ASN1_ANY || it->funcs != NULL)
+               return 0;
+
+       if (*pval != NULL) {
+               ASN1_TYPE_free((ASN1_TYPE *)*pval);
+               *pval = NULL;
        }
 
-       if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_POST, &aseq, it, NULL)) {
-               ASN1error(ASN1_R_AUX_ERROR);
-               goto err;
+       if ((atype = ASN1_TYPE_new()) == NULL)
+               return 0;
+
+       if (!asn1_ex_c2i_primitive(&atype->value.asn1_value, content, utype, it)) {
+               ASN1_TYPE_free(atype);
+               return 0;
        }
+       atype->type = utype;
 
-       *pval = aseq;
-       aseq = NULL;
+       /* Fix up value for ASN.1 NULL. */
+       if (atype->type == V_ASN1_NULL)
+               atype->value.ptr = NULL;
+
+       *pval = (ASN1_VALUE *)atype;
 
        return 1;
+}
 
- err:
-       ASN1_item_ex_free(&aseq, it);
+static int
+asn1_ex_c2i(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it)
+{
+       if (CBS_len(content) > INT_MAX)
+               return 0;
 
-       if (errtt != NULL)
-               ERR_asprintf_error_data("Field=%s, Type=%s", errtt->field_name,
-                   it->sname);
-       else
-               ERR_asprintf_error_data("Type=%s", it->sname);
+       if (it->funcs != NULL) {
+               const ASN1_PRIMITIVE_FUNCS *pf = it->funcs;
+               char free_content = 0;
 
-       return 0;
+               if (pf->prim_c2i == NULL)
+                       return 0;
+
+               return pf->prim_c2i(pval, CBS_data(content), CBS_len(content),
+                   utype, &free_content, it);
+       }
+
+       if (it->utype == V_ASN1_ANY)
+               return asn1_ex_c2i_any(pval, content, utype, it);
+
+       return asn1_ex_c2i_primitive(pval, content, utype, it);
 }
 
 /*
- * Decode an item, taking care of IMPLICIT tagging, if any.
- * If 'opt' set and tag mismatch return -1 to handle OPTIONAL
+ * Decode ASN.1 content into a primitive type. There are three possible forms -
+ * a SEQUENCE/SET/OTHER that is stored verbatim (including the ASN.1 tag and
+ * length octets), constructed objects and non-constructed objects. In the
+ * first two cases indefinite length is permitted, which we may need to handle.
+ * When this function is called the *cbs should reference the start of the
+ * ASN.1 object (i.e. the tag/length header), while *cbs_object should
+ * reference the start of the object contents (i.e. after the tag/length
+ * header. Additionally, the *cbs_object offset should be relative to the
+ * ASN.1 object being parsed. On success the *cbs will point at the octet
+ * after the object.
  */
 static int
-asn1_item_ex_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
-    int tag_number, int tag_class, char optional, int depth)
+asn1_d2i_ex_primitive_content(ASN1_VALUE **pval, CBS *cbs, CBS *cbs_object,
+    int utype, char constructed, char indefinite, size_t length,
+    const ASN1_ITEM *it)
 {
-       const ASN1_EXTERN_FUNCS *ef = it->funcs;
-       const unsigned char *p = NULL;
-       ASN1_TLC ctx = { 0 };
+       CBS cbs_content, cbs_initial;
+       uint8_t *data = NULL;
+       size_t data_len = 0;
+       CBB cbb;
        int ret = 0;
 
-       if (pval == NULL)
-               return 0;
+       memset(&cbb, 0, sizeof(cbb));
 
-       if (++depth > ASN1_MAX_CONSTRUCTED_NEST) {
-               ASN1error(ASN1_R_NESTED_TOO_DEEP);
+       CBS_dup(cbs, &cbs_initial);
+       CBS_init(&cbs_content, NULL, 0);
+
+       /* XXX - check primitive vs constructed based on utype. */
+
+       /* SEQUENCE and SET must be constructed. */
+       if ((utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET) && !constructed) {
+               ASN1error(ASN1_R_TYPE_NOT_CONSTRUCTED);
                goto err;
        }
 
-       switch (it->itype) {
-       case ASN1_ITYPE_PRIMITIVE:
-               if (it->templates != NULL) {
-                       /*
-                        * Tagging or OPTIONAL is currently illegal on an item
-                        * template because the flags can't get passed down.
-                        * In practice this isn't a problem: we include the
-                        * relevant flags from the item template in the
-                        * template itself.
-                        */
-                       if (tag_number != -1 || optional) {
-                               ASN1error(ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE);
-                               goto err;
-                       }
-                       return asn1_template_ex_d2i(pval, cbs,
-                           it->templates, optional, depth);
-               }
-               return asn1_d2i_ex_primitive(pval, cbs, it, tag_number,
-                   tag_class, optional);
-
-       case ASN1_ITYPE_MSTRING:
-               return asn1_d2i_ex_mstring(pval, cbs, it, tag_number, tag_class,
-                   optional);
+       /* SEQUENCE, SET and "OTHER" are left in encoded form. */
+       if (utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET ||
+           utype == V_ASN1_OTHER) {
+               if (!asn1_find_end(cbs_object, length, indefinite))
+                       goto err;
+               if (!CBS_get_bytes(&cbs_initial, &cbs_content,
+                   CBS_offset(cbs_object)))
+                       goto err;
+       } else if (constructed) {
+               /*
+                * Should really check the internal tags are correct but
+                * some things may get this wrong. The relevant specs
+                * say that constructed string types should be OCTET STRINGs
+                * internally irrespective of the type. So instead just check
+                * for UNIVERSAL class and ignore the tag.
+                */
+               if (!CBB_init(&cbb, 0))
+                       goto err;
+               if (!asn1_collect(&cbb, cbs_object, indefinite, -1,
+                   V_ASN1_UNIVERSAL, 0))
+                       goto err;
+               if (!CBB_finish(&cbb, &data, &data_len))
+                       goto err;
 
-       case ASN1_ITYPE_EXTERN:
-               if (CBS_len(cbs) > LONG_MAX)
-                       return 0;
-               p = CBS_data(cbs);
-               if ((ret = ef->asn1_ex_d2i(pval, &p, (long)CBS_len(cbs), it,
-                   tag_number, tag_class, optional, &ctx)) == 1) {
-                       if (!CBS_skip(cbs, p - CBS_data(cbs)))
-                               goto err;
-               }
-               return ret;
+               CBS_init(&cbs_content, data, data_len);
+       } else {
+               if (!CBS_get_bytes(cbs_object, &cbs_content, length))
+                       goto err;
+       }
 
-       case ASN1_ITYPE_CHOICE:
-               return asn1_item_ex_d2i_choice(pval, cbs, it, tag_number,
-                   tag_class, optional, depth);
+       if (!asn1_ex_c2i(pval, &cbs_content, utype, it))
+               goto err;
 
-       case ASN1_ITYPE_NDEF_SEQUENCE:
-       case ASN1_ITYPE_SEQUENCE:
-               return asn1_item_ex_d2i_sequence(pval, cbs, it, tag_number,
-                   tag_class, optional, depth);
+       if (!CBS_skip(cbs, CBS_offset(cbs_object)))
+               goto err;
 
-       default:
-               return 0;
-       }
+       ret = 1;
 
  err:
-       ASN1_item_ex_free(pval, it);
-
-       ERR_asprintf_error_data("Type=%s", it->sname);
+       CBB_cleanup(&cbb);
+       freezero(data, data_len);
 
-       return 0;
+       return ret;
 }
 
-int
-ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long inlen,
-    const ASN1_ITEM *it, int tag_number, int tag_class, char optional,
-    ASN1_TLC *ctx)
+static int
+asn1_d2i_ex_any(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
+    int tag_number, int tag_class, char optional)
 {
-       CBS cbs;
-       int ret;
+       char constructed, indefinite;
+       unsigned char object_class;
+       int object_type;
+       CBS cbs_object;
+       size_t length;
 
-       if (inlen < 0)
+       CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs));
+
+       if (it->utype != V_ASN1_ANY)
                return 0;
 
-       CBS_init(&cbs, *in, inlen);
+       if (tag_number >= 0) {
+               ASN1error(ASN1_R_ILLEGAL_TAGGED_ANY);
+               return 0;
+       }
+       if (optional) {
+               ASN1error(ASN1_R_ILLEGAL_OPTIONAL_ANY);
+               return 0;
+       }
 
-       if ((ret = asn1_item_ex_d2i(pval, &cbs, it, tag_number, tag_class,
-           optional, 0)) == 1)
-               *in = CBS_data(&cbs);
+       /* Determine type from ASN.1 tag. */
+       if (asn1_check_tag(&cbs_object, &length, &object_type, &object_class,
+           &indefinite, &constructed, -1, 0, 0) != 1) {
+               ASN1error(ERR_R_NESTED_ASN1_ERROR);
+               return 0;
+       }
+       if (object_class != V_ASN1_UNIVERSAL)
+               object_type = V_ASN1_OTHER;
 
-       return ret;
+       return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, object_type,
+           constructed, indefinite, length, it);
 }
 
 static int
-asn1_template_ex_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt,
-    char optional, int depth)
+asn1_d2i_ex_mstring(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
+    int tag_number, int tag_class, char optional)
 {
-       CBS cbs_exp, cbs_exp_content;
        char constructed, indefinite;
+       unsigned char object_class;
+       int object_tag;
+       CBS cbs_object;
        size_t length;
-       int ret;
-
-       if (pval == NULL)
-               return 0;
-
-       /* Check if EXPLICIT tag is expected. */
-       if ((tt->flags & ASN1_TFLG_EXPTAG) == 0)
-               return asn1_template_noexp_d2i(pval, cbs, tt, optional, depth);
-
-       CBS_init(&cbs_exp, CBS_data(cbs), CBS_len(cbs));
 
-       /* Read ASN.1 header for EXPLICIT tagged object. */
-       ret = asn1_check_tag(&cbs_exp, &length, NULL, NULL, &indefinite,
-           &constructed, tt->tag, tt->flags & ASN1_TFLG_TAG_CLASS, optional);
-       if (ret == -1)
-               return -1;
-       if (ret != 1) {
-               ASN1error(ERR_R_NESTED_ASN1_ERROR);
-               return 0;
-       }
+       CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs));
 
-       if (!constructed) {
-               ASN1error(ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED);
+       /*
+        * It never makes sense for multi-strings to have implicit tagging, so
+        * if tag_number != -1, then this looks like an error in the template.
+        */
+       if (tag_number != -1) {
+               ASN1error(ASN1_R_BAD_TEMPLATE);
                return 0;
        }
 
-       if (indefinite) {
-               CBS_init(&cbs_exp_content, CBS_data(&cbs_exp), CBS_len(&cbs_exp));
-       } else {
-               if (!CBS_get_bytes(&cbs_exp, &cbs_exp_content, length))
-                       goto err;
-       }
-
-       if ((ret = asn1_template_noexp_d2i(pval, &cbs_exp_content, tt, 0,
-           depth)) != 1) {
+       if (asn1_check_tag(&cbs_object, &length, &object_tag, &object_class,
+           &indefinite, &constructed, -1, 0, 1) != 1) {
                ASN1error(ERR_R_NESTED_ASN1_ERROR);
                return 0;
        }
 
-       if (indefinite) {
-               if (!asn1_check_eoc(&cbs_exp_content)) {
-                       ASN1error(ASN1_R_MISSING_EOC);
-                       goto err;
-               }
-               if (!CBS_skip(&cbs_exp, CBS_offset(&cbs_exp_content)))
-                       goto err;
-       } else if (CBS_len(&cbs_exp_content) != 0) {
-               ASN1error(ASN1_R_SEQUENCE_LENGTH_MISMATCH);
-               goto err;
+       /* Class must be UNIVERSAL. */
+       if (object_class != V_ASN1_UNIVERSAL) {
+               if (optional)
+                       return -1;
+               ASN1error(ASN1_R_MSTRING_NOT_UNIVERSAL);
+               return 0;
        }
-
-       if (!CBS_skip(cbs, CBS_offset(&cbs_exp)))
-               goto err;
-
-       return 1;
-
- err:
-       ASN1_template_free(pval, tt);
-       return 0;
-}
-
-static void
-asn1_template_stack_of_free(STACK_OF(ASN1_VALUE) *avals, const ASN1_TEMPLATE *tt) {
-       ASN1_VALUE *aval;
-
-       if (avals == NULL)
-               return;
-
-       while (sk_ASN1_VALUE_num(avals) > 0) {
-               aval = sk_ASN1_VALUE_pop(avals);
-               ASN1_item_ex_free(&aval, tt->item);
+       /* Check tag matches bit map. */
+       if ((ASN1_tag2bit(object_tag) & it->utype) == 0) {
+               if (optional)
+                       return -1;
+               ASN1error(ASN1_R_MSTRING_WRONG_TAG);
+               return 0;
        }
-       sk_ASN1_VALUE_free(avals);
+
+       return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object,
+           object_tag, constructed, indefinite, length, it);
 }
 
 static int
-asn1_template_stack_of_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt,
-    char optional, int depth)
+asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
+    int tag_number, int tag_class, char optional)
 {
-       CBS cbs_object, cbs_object_content;
-       STACK_OF(ASN1_VALUE) *avals = NULL;
-       ASN1_VALUE *aval = NULL;
-       int tag_number, tag_class;
-       int eoc_needed;
-       char indefinite;
+       CBS cbs_object;
+       char constructed, indefinite;
+       int utype = it->utype;
        size_t length;
        int ret;
 
        CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs));
 
-       if (pval == NULL)
+       if (it->itype == ASN1_ITYPE_MSTRING)
                return 0;
 
-       asn1_template_stack_of_free((STACK_OF(ASN1_VALUE) *)*pval, tt);
-       *pval = NULL;
-
-       tag_number = tt->tag;
-       tag_class = tt->flags & ASN1_TFLG_TAG_CLASS;
+       if (it->utype == V_ASN1_ANY)
+               return asn1_d2i_ex_any(pval, cbs, it, tag_number, tag_class, optional);
 
-       /* Determine the inner tag value for SET OF or SEQUENCE OF. */
-       if ((tt->flags & ASN1_TFLG_IMPTAG) == 0) {
-               tag_number = V_ASN1_SEQUENCE;
+       if (tag_number == -1) {
+               tag_number = it->utype;
                tag_class = V_ASN1_UNIVERSAL;
-               if ((tt->flags & ASN1_TFLG_SET_OF) != 0)
-                       tag_number = V_ASN1_SET;
        }
 
        ret = asn1_check_tag(&cbs_object, &length, NULL, NULL, &indefinite,
-           NULL, tag_number, tag_class, optional);
+           &constructed, tag_number, tag_class, optional);
        if (ret == -1)
                return -1;
        if (ret != 1) {
@@ -611,296 +629,399 @@ asn1_template_stack_of_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt,
                return 0;
        }
 
-       if (indefinite) {
-               eoc_needed = 1;
-               CBS_init(&cbs_object_content, CBS_data(&cbs_object),
-                   CBS_len(&cbs_object));
-       } else {
-               eoc_needed = 0;
-               if (!CBS_get_bytes(&cbs_object, &cbs_object_content,
-                   length))
-                       goto err;
+       return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, utype,
+           constructed, indefinite, length, it);
+}
+
+static int
+asn1_item_ex_d2i_choice(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
+    int tag_number, int tag_class, char optional, int depth)
+{
+       const ASN1_TEMPLATE *tt, *errtt = NULL;
+       const ASN1_AUX *aux;
+       ASN1_aux_cb *asn1_cb = NULL;
+       ASN1_VALUE *achoice = NULL;
+       ASN1_VALUE **pchptr;
+       int i, ret;
+
+       if ((aux = it->funcs) != NULL)
+               asn1_cb = aux->asn1_cb;
+
+       if (it->itype != ASN1_ITYPE_CHOICE)
+               goto err;
+
+       /*
+        * It never makes sense for CHOICE types to have implicit tagging, so
+        * if tag_number != -1, then this looks like an error in the template.
+        */
+       if (tag_number != -1) {
+               ASN1error(ASN1_R_BAD_TEMPLATE);
+               goto err;
        }
 
-       if ((avals = sk_ASN1_VALUE_new_null()) == NULL) {
-               ASN1error(ERR_R_MALLOC_FAILURE);
+       if (*pval != NULL) {
+               ASN1_item_ex_free(pval, it);
+               *pval = NULL;
+       }
+
+       if (!ASN1_item_ex_new(&achoice, it)) {
+               ASN1error(ERR_R_NESTED_ASN1_ERROR);
                goto err;
        }
 
-       /* Read as many items as possible. */
-       while (CBS_len(&cbs_object_content) > 0) {
-               if (asn1_check_eoc(&cbs_object_content)) {
-                       if (!eoc_needed) {
-                               ASN1error(ASN1_R_UNEXPECTED_EOC);
-                               goto err;
-                       }
-                       eoc_needed = 0;
-                       break;
-               }
-               if (!asn1_item_ex_d2i(&aval, &cbs_object_content, tt->item,
-                   -1, 0, 0, depth)) {
+       if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_PRE, &achoice, it, NULL)) {
+               ASN1error(ASN1_R_AUX_ERROR);
+               goto err;
+       }
+
+       /* Try each possible CHOICE in turn. */
+       for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) {
+               pchptr = asn1_get_field_ptr(&achoice, tt);
+
+               /* Mark field as OPTIONAL so its absence can be identified. */
+               ret = asn1_template_ex_d2i(pchptr, cbs, tt, 1, depth);
+               if (ret == -1)
+                       continue;
+               if (ret != 1) {
                        ASN1error(ERR_R_NESTED_ASN1_ERROR);
+                       errtt = tt;
                        goto err;
                }
-               if (!sk_ASN1_VALUE_push(avals, aval)) {
-                       ASN1error(ERR_R_MALLOC_FAILURE);
-                       goto err;
-               }
-               aval = NULL;
-       }
-       if (eoc_needed) {
-               ASN1error(ASN1_R_MISSING_EOC);
-               goto err;
+
+               /* We've successfully decoded an ASN.1 object. */
+               asn1_set_choice_selector(&achoice, i, it);
+               break;
        }
 
-       if (indefinite) {
-               if (!CBS_skip(&cbs_object, CBS_offset(&cbs_object_content)))
-                       goto err;
+       /* Did we fall off the end without reading anything? */
+       if (i == it->tcount) {
+               if (optional) {
+                       ASN1_item_ex_free(&achoice, it);
+                       return -1;
+               }
+               ASN1error(ASN1_R_NO_MATCHING_CHOICE_TYPE);
+               goto err;
        }
 
-       if (!CBS_skip(cbs, CBS_offset(&cbs_object)))
+       if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_POST, &achoice, it, NULL)) {
+               ASN1error(ASN1_R_AUX_ERROR);
                goto err;
+       }
 
-       *pval = (ASN1_VALUE *)avals;
-       avals = NULL;
+       *pval = achoice;
+       achoice = NULL;
 
        return 1;
 
  err:
-       asn1_template_stack_of_free(avals, tt);
-       ASN1_item_ex_free(&aval, tt->item);
+       ASN1_item_ex_free(&achoice, it);
+
+       if (errtt)
+               ERR_asprintf_error_data("Field=%s, Type=%s", errtt->field_name,
+                   it->sname);
+       else
+               ERR_asprintf_error_data("Type=%s", it->sname);
 
        return 0;
 }
 
 static int
-asn1_template_noexp_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt,
-    char optional, int depth)
+asn1_item_ex_d2i_sequence(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
+    int tag_number, int tag_class, char optional, int depth)
 {
-       int tag_number, tag_class;
-       int ret;
+       CBS cbs_seq, cbs_seq_content, cbs_object;
+       char constructed, indefinite, optional_field;
+       const ASN1_TEMPLATE *errtt = NULL;
+       const ASN1_TEMPLATE *seqtt, *tt;
+       ASN1_aux_cb *asn1_cb = NULL;
+       const ASN1_AUX *aux;
+       ASN1_VALUE *aseq = NULL;
+       ASN1_VALUE **pseqval;
+       int eoc_needed, i;
+       size_t length;
+       int ret = 0;
 
-       if (pval == NULL)
-               return 0;
+       CBS_init(&cbs_seq, CBS_data(cbs), CBS_len(cbs));
 
-       if ((tt->flags & ASN1_TFLG_SK_MASK) != 0)
-               return asn1_template_stack_of_d2i(pval, cbs, tt, optional, depth);
+       if ((aux = it->funcs) != NULL)
+               asn1_cb = aux->asn1_cb;
 
-       tag_number = -1;
-       tag_class = V_ASN1_UNIVERSAL;
+       if (it->itype != ASN1_ITYPE_NDEF_SEQUENCE &&
+           it->itype != ASN1_ITYPE_SEQUENCE)
+               goto err;
 
-       /* See if we need to use IMPLICIT tagging. */
-       if ((tt->flags & ASN1_TFLG_IMPTAG) != 0) {
-               tag_number = tt->tag;
-               tag_class = tt->flags & ASN1_TFLG_TAG_CLASS;
+       if (*pval != NULL) {
+               ASN1_item_ex_free(pval, it);
+               *pval = NULL;
+       }
+
+       /* If no IMPLICIT tagging use UNIVERSAL/SEQUENCE. */
+       if (tag_number == -1) {
+               tag_class = V_ASN1_UNIVERSAL;
+               tag_number = V_ASN1_SEQUENCE;
+       }
+
+       /* Read ASN.1 SEQUENCE header. */
+       ret = asn1_check_tag(&cbs_seq, &length, NULL, NULL, &indefinite,
+           &constructed, tag_number, tag_class, optional);
+       if (ret == -1)
+               return -1;
+       if (ret != 1) {
+               ASN1error(ERR_R_NESTED_ASN1_ERROR);
+               goto err;
+       }
+
+       if (!constructed) {
+               ASN1error(ASN1_R_SEQUENCE_NOT_CONSTRUCTED);
+               goto err;
+       }
+
+       if (indefinite) {
+               eoc_needed = 1;
+               CBS_init(&cbs_seq_content, CBS_data(&cbs_seq), CBS_len(&cbs_seq));
+       } else {
+               eoc_needed = 0;
+               if (!CBS_get_bytes(&cbs_seq, &cbs_seq_content, length))
+                       goto err;
        }
 
-       ret = asn1_item_ex_d2i(pval, cbs, tt->item, tag_number, tag_class,
-           optional, depth);
-       if (ret == -1)
-               return -1;
-       if (ret != 1) {
+       if (!ASN1_item_ex_new(&aseq, it)) {
                ASN1error(ERR_R_NESTED_ASN1_ERROR);
                goto err;
        }
 
-       return 1;
+       if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_PRE, &aseq, it, NULL)) {
+               ASN1error(ASN1_R_AUX_ERROR);
+               goto err;
+       }
 
- err:
-       /* XXX - The called function should have freed already. */
-       ASN1_template_free(pval, tt);
-       return 0;
-}
+       for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) {
+               if (asn1_check_eoc(&cbs_seq_content)) {
+                       if (!indefinite) {
+                               ASN1error(ASN1_R_UNEXPECTED_EOC);
+                               goto err;
+                       }
+                       eoc_needed = 0;
+                       break;
+               }
+               if (CBS_len(&cbs_seq_content) == 0)
+                       break;
 
-/*
- * Decode ASN.1 content into a primitive type. There are three possible forms -
- * a SEQUENCE/SET/OTHER that is stored verbatim (including the ASN.1 tag and
- * length octets), constructed objects and non-constructed objects. In the
- * first two cases indefinite length is permitted, which we may need to handle.
- * When this function is called the *cbs should reference the start of the
- * ASN.1 object (i.e. the tag/length header), while *cbs_object should
- * reference the start of the object contents (i.e. after the tag/length
- * header. Additionally, the *cbs_object offset should be relative to the
- * ASN.1 object being parsed. On success the *cbs will point at the octet
- * after the object.
- */
-static int
-asn1_d2i_ex_primitive_content(ASN1_VALUE **pval, CBS *cbs, CBS *cbs_object,
-    int utype, char constructed, char indefinite, size_t length,
-    const ASN1_ITEM *it)
-{
-       CBS cbs_content, cbs_initial;
-       uint8_t *data = NULL;
-       size_t data_len = 0;
-       CBB cbb;
-       int ret = 0;
+               if ((seqtt = asn1_do_adb(&aseq, tt, 1)) == NULL)
+                       goto err;
 
-       memset(&cbb, 0, sizeof(cbb));
+               pseqval = asn1_get_field_ptr(&aseq, seqtt);
 
-       CBS_dup(cbs, &cbs_initial);
-       CBS_init(&cbs_content, NULL, 0);
+               /*
+                * This was originally implemented to "increase efficiency",
+                * however it currently needs to remain since it papers over
+                * the use of ASN.1 ANY with OPTIONAL in SEQUENCEs (which
+                * asn1_d2i_ex_primitive() currently rejects).
+                */
+               optional_field = (seqtt->flags & ASN1_TFLG_OPTIONAL) != 0;
+               if (i == it->tcount - 1)
+                       optional_field = 0;
 
-       /* XXX - check primitive vs constructed based on utype. */
+               ret = asn1_template_ex_d2i(pseqval, &cbs_seq_content,
+                   seqtt, optional_field, depth);
+               if (ret == -1) {
+                       /* Absent OPTIONAL component. */
+                       ASN1_template_free(pseqval, seqtt);
+                       continue;
+               }
+               if (ret != 1) {
+                       errtt = seqtt;
+                       goto err;
+               }
+       }
 
-       /* SEQUENCE and SET must be constructed. */
-       if ((utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET) && !constructed) {
-               ASN1error(ASN1_R_TYPE_NOT_CONSTRUCTED);
+       if (eoc_needed && !asn1_check_eoc(&cbs_seq_content)) {
+               ASN1error(ASN1_R_MISSING_EOC);
                goto err;
        }
 
-       /* SEQUENCE, SET and "OTHER" are left in encoded form. */
-       if (utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET ||
-           utype == V_ASN1_OTHER) {
-               if (!asn1_find_end(cbs_object, length, indefinite))
-                       goto err;
-               if (!CBS_get_bytes(&cbs_initial, &cbs_content,
-                   CBS_offset(cbs_object)))
-                       goto err;
-       } else if (constructed) {
-               /*
-                * Should really check the internal tags are correct but
-                * some things may get this wrong. The relevant specs
-                * say that constructed string types should be OCTET STRINGs
-                * internally irrespective of the type. So instead just check
-                * for UNIVERSAL class and ignore the tag.
-                */
-               if (!CBB_init(&cbb, 0))
-                       goto err;
-               if (!asn1_collect(&cbb, cbs_object, indefinite, -1,
-                   V_ASN1_UNIVERSAL, 0))
+       if (indefinite) {
+               if (!CBS_skip(&cbs_seq, CBS_offset(&cbs_seq_content)))
                        goto err;
-               if (!CBB_finish(&cbb, &data, &data_len))
+       } else if (CBS_len(&cbs_seq_content) != 0) {
+               ASN1error(ASN1_R_SEQUENCE_LENGTH_MISMATCH);
+               goto err;
+       }
+
+       /*
+        * There is no more data in the ASN.1 SEQUENCE, however we may not have
+        * populated all fields - check that any remaining are OPTIONAL.
+        */
+       for (; i < it->tcount; tt++, i++) {
+               if ((seqtt = asn1_do_adb(&aseq, tt, 1)) == NULL)
                        goto err;
 
-               CBS_init(&cbs_content, data, data_len);
-       } else {
-               if (!CBS_get_bytes(cbs_object, &cbs_content, length))
+               if ((seqtt->flags & ASN1_TFLG_OPTIONAL) == 0) {
+                       ASN1error(ASN1_R_FIELD_MISSING);
+                       errtt = seqtt;
                        goto err;
+               }
+
+               /* XXX - this is probably unnecessary with earlier free. */
+               pseqval = asn1_get_field_ptr(&aseq, seqtt);
+               ASN1_template_free(pseqval, seqtt);
        }
 
-       if (!asn1_ex_c2i(pval, &cbs_content, utype, it))
+       if (!CBS_get_bytes(cbs, &cbs_object, CBS_offset(&cbs_seq)))
                goto err;
 
-       if (!CBS_skip(cbs, CBS_offset(cbs_object)))
+       if (!asn1_enc_save(&aseq, &cbs_object, it)) {
+               ASN1error(ERR_R_MALLOC_FAILURE);
                goto err;
+       }
 
-       ret = 1;
+       if (asn1_cb != NULL && !asn1_cb(ASN1_OP_D2I_POST, &aseq, it, NULL)) {
+               ASN1error(ASN1_R_AUX_ERROR);
+               goto err;
+       }
+
+       *pval = aseq;
+       aseq = NULL;
+
+       return 1;
 
  err:
-       CBB_cleanup(&cbb);
-       freezero(data, data_len);
+       ASN1_item_ex_free(&aseq, it);
 
-       return ret;
+       if (errtt != NULL)
+               ERR_asprintf_error_data("Field=%s, Type=%s", errtt->field_name,
+                   it->sname);
+       else
+               ERR_asprintf_error_data("Type=%s", it->sname);
+
+       return 0;
 }
 
+/*
+ * Decode an item, taking care of IMPLICIT tagging, if any.
+ * If 'opt' set and tag mismatch return -1 to handle OPTIONAL
+ */
 static int
-asn1_d2i_ex_any(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
-    int tag_number, int tag_class, char optional)
+asn1_item_ex_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
+    int tag_number, int tag_class, char optional, int depth)
 {
-       char constructed, indefinite;
-       unsigned char object_class;
-       int object_type;
-       CBS cbs_object;
-       size_t length;
-
-       CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs));
+       const ASN1_EXTERN_FUNCS *ef = it->funcs;
+       const unsigned char *p = NULL;
+       ASN1_TLC ctx = { 0 };
+       int ret = 0;
 
-       if (it->utype != V_ASN1_ANY)
+       if (pval == NULL)
                return 0;
 
-       if (tag_number >= 0) {
-               ASN1error(ASN1_R_ILLEGAL_TAGGED_ANY);
-               return 0;
-       }
-       if (optional) {
-               ASN1error(ASN1_R_ILLEGAL_OPTIONAL_ANY);
-               return 0;
+       if (++depth > ASN1_MAX_CONSTRUCTED_NEST) {
+               ASN1error(ASN1_R_NESTED_TOO_DEEP);
+               goto err;
        }
 
-       /* Determine type from ASN.1 tag. */
-       if (asn1_check_tag(&cbs_object, &length, &object_type, &object_class,
-           &indefinite, &constructed, -1, 0, 0) != 1) {
-               ASN1error(ERR_R_NESTED_ASN1_ERROR);
-               return 0;
-       }
-       if (object_class != V_ASN1_UNIVERSAL)
-               object_type = V_ASN1_OTHER;
+       switch (it->itype) {
+       case ASN1_ITYPE_PRIMITIVE:
+               if (it->templates != NULL) {
+                       /*
+                        * Tagging or OPTIONAL is currently illegal on an item
+                        * template because the flags can't get passed down.
+                        * In practice this isn't a problem: we include the
+                        * relevant flags from the item template in the
+                        * template itself.
+                        */
+                       if (tag_number != -1 || optional) {
+                               ASN1error(ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE);
+                               goto err;
+                       }
+                       return asn1_template_ex_d2i(pval, cbs,
+                           it->templates, optional, depth);
+               }
+               return asn1_d2i_ex_primitive(pval, cbs, it, tag_number,
+                   tag_class, optional);
 
-       return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, object_type,
-           constructed, indefinite, length, it);
-}
+       case ASN1_ITYPE_MSTRING:
+               return asn1_d2i_ex_mstring(pval, cbs, it, tag_number, tag_class,
+                   optional);
 
-static int
-asn1_d2i_ex_mstring(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
-    int tag_number, int tag_class, char optional)
-{
-       char constructed, indefinite;
-       unsigned char object_class;
-       int object_tag;
-       CBS cbs_object;
-       size_t length;
+       case ASN1_ITYPE_EXTERN:
+               if (CBS_len(cbs) > LONG_MAX)
+                       return 0;
+               p = CBS_data(cbs);
+               if ((ret = ef->asn1_ex_d2i(pval, &p, (long)CBS_len(cbs), it,
+                   tag_number, tag_class, optional, &ctx)) == 1) {
+                       if (!CBS_skip(cbs, p - CBS_data(cbs)))
+                               goto err;
+               }
+               return ret;
 
-       CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs));
+       case ASN1_ITYPE_CHOICE:
+               return asn1_item_ex_d2i_choice(pval, cbs, it, tag_number,
+                   tag_class, optional, depth);
 
-       /*
-        * It never makes sense for multi-strings to have implicit tagging, so
-        * if tag_number != -1, then this looks like an error in the template.
-        */
-       if (tag_number != -1) {
-               ASN1error(ASN1_R_BAD_TEMPLATE);
-               return 0;
-       }
+       case ASN1_ITYPE_NDEF_SEQUENCE:
+       case ASN1_ITYPE_SEQUENCE:
+               return asn1_item_ex_d2i_sequence(pval, cbs, it, tag_number,
+                   tag_class, optional, depth);
 
-       if (asn1_check_tag(&cbs_object, &length, &object_tag, &object_class,
-           &indefinite, &constructed, -1, 0, 1) != 1) {
-               ASN1error(ERR_R_NESTED_ASN1_ERROR);
+       default:
                return 0;
        }
 
-       /* Class must be UNIVERSAL. */
-       if (object_class != V_ASN1_UNIVERSAL) {
-               if (optional)
-                       return -1;
-               ASN1error(ASN1_R_MSTRING_NOT_UNIVERSAL);
-               return 0;
-       }
-       /* Check tag matches bit map. */
-       if ((ASN1_tag2bit(object_tag) & it->utype) == 0) {
-               if (optional)
-                       return -1;
-               ASN1error(ASN1_R_MSTRING_WRONG_TAG);
-               return 0;
-       }
+ err:
+       ASN1_item_ex_free(pval, it);
 
-       return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object,
-           object_tag, constructed, indefinite, length, it);
+       ERR_asprintf_error_data("Type=%s", it->sname);
+
+       return 0;
+}
+
+static void
+asn1_template_stack_of_free(STACK_OF(ASN1_VALUE) *avals, const ASN1_TEMPLATE *tt) {
+       ASN1_VALUE *aval;
+
+       if (avals == NULL)
+               return;
+
+       while (sk_ASN1_VALUE_num(avals) > 0) {
+               aval = sk_ASN1_VALUE_pop(avals);
+               ASN1_item_ex_free(&aval, tt->item);
+       }
+       sk_ASN1_VALUE_free(avals);
 }
 
 static int
-asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
-    int tag_number, int tag_class, char optional)
+asn1_template_stack_of_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt,
+    char optional, int depth)
 {
-       CBS cbs_object;
-       char constructed, indefinite;
-       int utype = it->utype;
+       CBS cbs_object, cbs_object_content;
+       STACK_OF(ASN1_VALUE) *avals = NULL;
+       ASN1_VALUE *aval = NULL;
+       int tag_number, tag_class;
+       int eoc_needed;
+       char indefinite;
        size_t length;
        int ret;
 
        CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs));
 
-       if (it->itype == ASN1_ITYPE_MSTRING)
+       if (pval == NULL)
                return 0;
 
-       if (it->utype == V_ASN1_ANY)
-               return asn1_d2i_ex_any(pval, cbs, it, tag_number, tag_class, optional);
+       asn1_template_stack_of_free((STACK_OF(ASN1_VALUE) *)*pval, tt);
+       *pval = NULL;
 
-       if (tag_number == -1) {
-               tag_number = it->utype;
+       tag_number = tt->tag;
+       tag_class = tt->flags & ASN1_TFLG_TAG_CLASS;
+
+       /* Determine the inner tag value for SET OF or SEQUENCE OF. */
+       if ((tt->flags & ASN1_TFLG_IMPTAG) == 0) {
+               tag_number = V_ASN1_SEQUENCE;
                tag_class = V_ASN1_UNIVERSAL;
+               if ((tt->flags & ASN1_TFLG_SET_OF) != 0)
+                       tag_number = V_ASN1_SET;
        }
 
        ret = asn1_check_tag(&cbs_object, &length, NULL, NULL, &indefinite,
-           &constructed, tag_number, tag_class, optional);
+           NULL, tag_number, tag_class, optional);
        if (ret == -1)
                return -1;
        if (ret != 1) {
@@ -908,359 +1029,222 @@ asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it,
                return 0;
        }
 
-       return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, utype,
-           constructed, indefinite, length, it);
-}
-
-static int
-asn1_ex_c2i_primitive(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it)
-{
-       ASN1_STRING *stmp;
-       ASN1_INTEGER **tint;
-       ASN1_BOOLEAN *tbool;
-       uint8_t u8val;
-       int ret = 0;
-
-       if (it->funcs != NULL)
-               return 0;
-
-       if (CBS_len(content) > INT_MAX)
-               return 0;
-
-       switch (utype) {
-       case V_ASN1_OBJECT:
-               if (!c2i_ASN1_OBJECT_cbs((ASN1_OBJECT **)pval, content))
-                       goto err;
-               break;
-
-       case V_ASN1_NULL:
-               if (CBS_len(content) != 0) {
-                       ASN1error(ASN1_R_NULL_IS_WRONG_LENGTH);
-                       goto err;
-               }
-               *pval = (ASN1_VALUE *)1;
-               break;
-
-       case V_ASN1_BOOLEAN:
-               tbool = (ASN1_BOOLEAN *)pval;
-               if (CBS_len(content) != 1) {
-                       ASN1error(ASN1_R_BOOLEAN_IS_WRONG_LENGTH);
-                       goto err;
-               }
-               if (!CBS_get_u8(content, &u8val))
-                       goto err;
-               *tbool = u8val;
-               break;
-
-       case V_ASN1_BIT_STRING:
-               if (!c2i_ASN1_BIT_STRING_cbs((ASN1_BIT_STRING **)pval, content))
+       if (indefinite) {
+               eoc_needed = 1;
+               CBS_init(&cbs_object_content, CBS_data(&cbs_object),
+                   CBS_len(&cbs_object));
+       } else {
+               eoc_needed = 0;
+               if (!CBS_get_bytes(&cbs_object, &cbs_object_content,
+                   length))
                        goto err;
-               break;
+       }
 
-       case V_ASN1_INTEGER:
-       case V_ASN1_ENUMERATED:
-               tint = (ASN1_INTEGER **)pval;
-               if (!c2i_ASN1_INTEGER_cbs(tint, content))
-                       goto err;
-               /* Fixup type to match the expected form */
-               (*tint)->type = utype | ((*tint)->type & V_ASN1_NEG);
-               break;
+       if ((avals = sk_ASN1_VALUE_new_null()) == NULL) {
+               ASN1error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
 
-       case V_ASN1_OCTET_STRING:
-       case V_ASN1_NUMERICSTRING:
-       case V_ASN1_PRINTABLESTRING:
-       case V_ASN1_T61STRING:
-       case V_ASN1_VIDEOTEXSTRING:
-       case V_ASN1_IA5STRING:
-       case V_ASN1_UTCTIME:
-       case V_ASN1_GENERALIZEDTIME:
-       case V_ASN1_GRAPHICSTRING:
-       case V_ASN1_VISIBLESTRING:
-       case V_ASN1_GENERALSTRING:
-       case V_ASN1_UNIVERSALSTRING:
-       case V_ASN1_BMPSTRING:
-       case V_ASN1_UTF8STRING:
-       case V_ASN1_OTHER:
-       case V_ASN1_SET:
-       case V_ASN1_SEQUENCE:
-       default:
-               if (utype == V_ASN1_BMPSTRING && (CBS_len(content) & 1)) {
-                       ASN1error(ASN1_R_BMPSTRING_IS_WRONG_LENGTH);
-                       goto err;
-               }
-               if (utype == V_ASN1_UNIVERSALSTRING && (CBS_len(content) & 3)) {
-                       ASN1error(ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH);
-                       goto err;
-               }
-               /* All based on ASN1_STRING and handled the same way. */
-               if (*pval == NULL) {
-                       if ((stmp = ASN1_STRING_type_new(utype)) == NULL) {
-                               ASN1error(ERR_R_MALLOC_FAILURE);
+       /* Read as many items as possible. */
+       while (CBS_len(&cbs_object_content) > 0) {
+               if (asn1_check_eoc(&cbs_object_content)) {
+                       if (!eoc_needed) {
+                               ASN1error(ASN1_R_UNEXPECTED_EOC);
                                goto err;
                        }
-                       *pval = (ASN1_VALUE *)stmp;
-               } else {
-                       stmp = (ASN1_STRING *)*pval;
-                       stmp->type = utype;
+                       eoc_needed = 0;
+                       break;
                }
-               if (!ASN1_STRING_set(stmp, CBS_data(content), CBS_len(content))) {
-                       ASN1_STRING_free(stmp);
-                       *pval = NULL;
+               if (!asn1_item_ex_d2i(&aval, &cbs_object_content, tt->item,
+                   -1, 0, 0, depth)) {
+                       ASN1error(ERR_R_NESTED_ASN1_ERROR);
                        goto err;
                }
-               break;
+               if (!sk_ASN1_VALUE_push(avals, aval)) {
+                       ASN1error(ERR_R_MALLOC_FAILURE);
+                       goto err;
+               }
+               aval = NULL;
        }
-
-       ret = 1;
-
- err:
-       return ret;
-}
-
-static int
-asn1_ex_c2i_any(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it)
-{
-       ASN1_TYPE *atype;
-
-       if (it->utype != V_ASN1_ANY || it->funcs != NULL)
-               return 0;
-
-       if (*pval != NULL) {
-               ASN1_TYPE_free((ASN1_TYPE *)*pval);
-               *pval = NULL;
+       if (eoc_needed) {
+               ASN1error(ASN1_R_MISSING_EOC);
+               goto err;
        }
 
-       if ((atype = ASN1_TYPE_new()) == NULL)
-               return 0;
-
-       if (!asn1_ex_c2i_primitive(&atype->value.asn1_value, content, utype, it)) {
-               ASN1_TYPE_free(atype);
-               return 0;
+       if (indefinite) {
+               if (!CBS_skip(&cbs_object, CBS_offset(&cbs_object_content)))
+                       goto err;
        }
-       atype->type = utype;
 
-       /* Fix up value for ASN.1 NULL. */
-       if (atype->type == V_ASN1_NULL)
-               atype->value.ptr = NULL;
+       if (!CBS_skip(cbs, CBS_offset(&cbs_object)))
+               goto err;
 
-       *pval = (ASN1_VALUE *)atype;
+       *pval = (ASN1_VALUE *)avals;
+       avals = NULL;
 
        return 1;
-}
-
-static int
-asn1_ex_c2i(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it)
-{
-       if (CBS_len(content) > INT_MAX)
-               return 0;
-
-       if (it->funcs != NULL) {
-               const ASN1_PRIMITIVE_FUNCS *pf = it->funcs;
-               char free_content = 0;
-
-               if (pf->prim_c2i == NULL)
-                       return 0;
-
-               return pf->prim_c2i(pval, CBS_data(content), CBS_len(content),
-                   utype, &free_content, it);
-       }
 
-       if (it->utype == V_ASN1_ANY)
-               return asn1_ex_c2i_any(pval, content, utype, it);
+ err:
+       asn1_template_stack_of_free(avals, tt);
+       ASN1_item_ex_free(&aval, tt->item);
 
-       return asn1_ex_c2i_primitive(pval, content, utype, it);
+       return 0;
 }
 
-/* Find the end of an ASN.1 object. */
 static int
-asn1_find_end(CBS *cbs, size_t length, char indefinite)
+asn1_template_noexp_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt,
+    char optional, int depth)
 {
-       size_t eoc_count;
+       int tag_number, tag_class;
+       int ret;
 
-       if (!indefinite) {
-               if (!CBS_skip(cbs, length)) {
-                       ASN1error(ERR_R_NESTED_ASN1_ERROR);
-                       return 0;
-               }
-               return 1;
-       }
+       if (pval == NULL)
+               return 0;
 
-       eoc_count = 1;
+       if ((tt->flags & ASN1_TFLG_SK_MASK) != 0)
+               return asn1_template_stack_of_d2i(pval, cbs, tt, optional, depth);
 
-       while (CBS_len(cbs) > 0) {
-               if (asn1_check_eoc(cbs)) {
-                       if (--eoc_count == 0)
-                               break;
-                       continue;
-               }
-               if (!asn1_check_tag(cbs, &length, NULL, NULL,
-                   &indefinite, NULL, -1, 0, 0)) {
-                       ASN1error(ERR_R_NESTED_ASN1_ERROR);
-                       return 0;
-               }
-               if (indefinite) {
-                       eoc_count++;
-                       continue;
-               }
-               if (!CBS_skip(cbs, length))
-                       return 0;
+       tag_number = -1;
+       tag_class = V_ASN1_UNIVERSAL;
+
+       /* See if we need to use IMPLICIT tagging. */
+       if ((tt->flags & ASN1_TFLG_IMPTAG) != 0) {
+               tag_number = tt->tag;
+               tag_class = tt->flags & ASN1_TFLG_TAG_CLASS;
        }
 
-       if (eoc_count > 0) {
-               ASN1error(ASN1_R_MISSING_EOC);
-               return 0;
+       ret = asn1_item_ex_d2i(pval, cbs, tt->item, tag_number, tag_class,
+           optional, depth);
+       if (ret == -1)
+               return -1;
+       if (ret != 1) {
+               ASN1error(ERR_R_NESTED_ASN1_ERROR);
+               goto err;
        }
 
        return 1;
-}
 
-#ifndef ASN1_MAX_STRING_NEST
-/* This determines how many levels of recursion are permitted in ASN1
- * string types. If it is not limited stack overflows can occur. If set
- * to zero no recursion is allowed at all. Although zero should be adequate
- * examples exist that require a value of 1. So 5 should be more than enough.
- */
-#define ASN1_MAX_STRING_NEST 5
-#endif
+ err:
+       /* XXX - The called function should have freed already. */
+       ASN1_template_free(pval, tt);
+       return 0;
+}
 
-/* Collect the contents from a constructed ASN.1 object. */
 static int
-asn1_collect(CBB *cbb, CBS *cbs, char indefinite, int expected_tag,
-    int expected_class, int depth)
+asn1_template_ex_d2i(ASN1_VALUE **pval, CBS *cbs, const ASN1_TEMPLATE *tt,
+    char optional, int depth)
 {
-       char constructed;
+       CBS cbs_exp, cbs_exp_content;
+       char constructed, indefinite;
        size_t length;
-       CBS content;
-       int need_eoc;
+       int ret;
 
-       if (depth > ASN1_MAX_STRING_NEST) {
-               ASN1error(ASN1_R_NESTED_ASN1_STRING);
+       if (pval == NULL)
                return 0;
-       }
 
-       need_eoc = indefinite;
+       /* Check if EXPLICIT tag is expected. */
+       if ((tt->flags & ASN1_TFLG_EXPTAG) == 0)
+               return asn1_template_noexp_d2i(pval, cbs, tt, optional, depth);
 
-       while (CBS_len(cbs) > 0) {
-               if (asn1_check_eoc(cbs)) {
-                       if (!need_eoc) {
-                               ASN1error(ASN1_R_UNEXPECTED_EOC);
-                               return 0;
-                       }
-                       return 1;
-               }
-               if (!asn1_check_tag(cbs, &length, NULL, NULL, &indefinite,
-                   &constructed, expected_tag, expected_class, 0)) {
-                       ASN1error(ERR_R_NESTED_ASN1_ERROR);
-                       return 0;
-               }
+       CBS_init(&cbs_exp, CBS_data(cbs), CBS_len(cbs));
 
-               if (constructed) {
-                       if (!asn1_collect(cbb, cbs, indefinite, expected_tag,
-                           expected_class, depth + 1))
-                               return 0;
-                       continue;
-               }
+       /* Read ASN.1 header for EXPLICIT tagged object. */
+       ret = asn1_check_tag(&cbs_exp, &length, NULL, NULL, &indefinite,
+           &constructed, tt->tag, tt->flags & ASN1_TFLG_TAG_CLASS, optional);
+       if (ret == -1)
+               return -1;
+       if (ret != 1) {
+               ASN1error(ERR_R_NESTED_ASN1_ERROR);
+               return 0;
+       }
 
-               if (!CBS_get_bytes(cbs, &content, length)) {
-                       ASN1error(ERR_R_NESTED_ASN1_ERROR);
-                       return 0;
-               }
-               if (!CBB_add_bytes(cbb, CBS_data(&content), CBS_len(&content)))
-                       return 0;
+       if (!constructed) {
+               ASN1error(ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED);
+               return 0;
        }
 
-       if (need_eoc) {
-               ASN1error(ASN1_R_MISSING_EOC);
+       if (indefinite) {
+               CBS_init(&cbs_exp_content, CBS_data(&cbs_exp), CBS_len(&cbs_exp));
+       } else {
+               if (!CBS_get_bytes(&cbs_exp, &cbs_exp_content, length))
+                       goto err;
+       }
+
+       if ((ret = asn1_template_noexp_d2i(pval, &cbs_exp_content, tt, 0,
+           depth)) != 1) {
+               ASN1error(ERR_R_NESTED_ASN1_ERROR);
                return 0;
        }
 
+       if (indefinite) {
+               if (!asn1_check_eoc(&cbs_exp_content)) {
+                       ASN1error(ASN1_R_MISSING_EOC);
+                       goto err;
+               }
+               if (!CBS_skip(&cbs_exp, CBS_offset(&cbs_exp_content)))
+                       goto err;
+       } else if (CBS_len(&cbs_exp_content) != 0) {
+               ASN1error(ASN1_R_SEQUENCE_LENGTH_MISMATCH);
+               goto err;
+       }
+
+       if (!CBS_skip(cbs, CBS_offset(&cbs_exp)))
+               goto err;
+
        return 1;
+
+ err:
+       ASN1_template_free(pval, tt);
+       return 0;
 }
 
-static int
-asn1_check_eoc(CBS *cbs)
+ASN1_VALUE *
+ASN1_item_d2i(ASN1_VALUE **pval, const unsigned char **in, long inlen,
+    const ASN1_ITEM *it)
 {
-       uint16_t eoc;
+       ASN1_VALUE *ptmpval = NULL;
 
-       if (!CBS_peek_u16(cbs, &eoc))
-               return 0;
-       if (eoc != 0)
-               return 0;
+       if (pval == NULL)
+               pval = &ptmpval;
+       if (ASN1_item_ex_d2i(pval, in, inlen, it, -1, 0, 0, 0) <= 0)
+               return NULL;
 
-       return CBS_skip(cbs, 2);
+       return *pval;
 }
 
-static int
-asn1_check_tag(CBS *cbs, size_t *out_len, int *out_tag, uint8_t *out_class,
-    char *out_indefinite, char *out_constructed, int expected_tag,
-    int expected_class, char optional)
+int
+ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long inlen,
+    const ASN1_ITEM *it, int tag_number, int tag_class, char optional,
+    ASN1_TLC *ctx)
 {
-       int constructed, indefinite;
-       uint32_t tag_number;
-       uint8_t tag_class;
-       size_t length;
-
-       if (out_len != NULL)
-               *out_len = 0;
-       if (out_tag != NULL)
-               *out_tag = 0;
-       if (out_class != NULL)
-               *out_class = 0;
-       if (out_indefinite != NULL)
-               *out_indefinite = 0;
-       if (out_constructed != NULL)
-               *out_constructed = 0;
+       CBS cbs;
+       int ret;
 
-       if (!asn1_get_identifier_cbs(cbs, 0, &tag_class, &constructed,
-           &tag_number)) {
-               ASN1error(ASN1_R_BAD_OBJECT_HEADER);
+       if (inlen < 0)
                return 0;
-       }
-       if (expected_tag >= 0) {
-               if (expected_tag != tag_number ||
-                   expected_class != tag_class << 6) {
-                       /* Indicate missing type if this is OPTIONAL. */
-                       if (optional)
-                               return -1;
 
-                       ASN1error(ASN1_R_WRONG_TAG);
-                       return 0;
-               }
-       }
-       if (!asn1_get_length_cbs(cbs, 0, &indefinite, &length)) {
-               ASN1error(ASN1_R_BAD_OBJECT_HEADER);
-               return 0;
-       }
+       CBS_init(&cbs, *in, inlen);
 
-       /* Indefinite length can only be used with constructed encoding. */
-       if (indefinite && !constructed) {
-               ASN1error(ASN1_R_BAD_OBJECT_HEADER);
-               return 0;
-       }
+       if ((ret = asn1_item_ex_d2i(pval, &cbs, it, tag_number, tag_class,
+           optional, 0)) == 1)
+               *in = CBS_data(&cbs);
 
-       if (!indefinite && CBS_len(cbs) < length) {
-               ASN1error(ASN1_R_TOO_LONG);
-               return 0;
-       }
+       return ret;
+}
 
-       if (tag_number > INT_MAX) {
-               ASN1error(ASN1_R_TOO_LONG);
-               return 0;
-       }
+int
+ASN1_template_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
+    const ASN1_TEMPLATE *tt)
+{
+       CBS cbs;
+       int ret;
 
-       if (indefinite)
-               length = CBS_len(cbs);
+       if (len < 0)
+               return 0;
 
-       if (out_len != NULL)
-               *out_len = length;
-       if (out_tag != NULL)
-               *out_tag = tag_number;
-       if (out_class != NULL)
-               *out_class = tag_class << 6;
-       if (out_indefinite != NULL && indefinite)
-               *out_indefinite = 1 << 0;
-       if (out_constructed != NULL && constructed)
-               *out_constructed = 1 << 5;
+       CBS_init(&cbs, *in, len);
+       if ((ret = asn1_template_ex_d2i(pval, &cbs, tt, 0, 0)) == 1)
+               *in = CBS_data(&cbs);
 
-       return 1;
+       return ret;
 }