From 618f8e60b522b47730139a704cf3dfe94d5b6f86 Mon Sep 17 00:00:00 2001
From: jsing <>
Date: Thu, 19 May 2022 19:45:18 +0000
Subject: Reorder functions within file.

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.
---
 src/lib/libcrypto/asn1/tasn_dec.c | 1760 ++++++++++++++++++-------------------
 1 file changed, 872 insertions(+), 888 deletions(-)

diff --git a/src/lib/libcrypto/asn1/tasn_dec.c b/src/lib/libcrypto/asn1/tasn_dec.c
index 79cbd4c75b..7583019aaa 100644
--- a/src/lib/libcrypto/asn1/tasn_dec.c
+++ b/src/lib/libcrypto/asn1/tasn_dec.c
@@ -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.
  */
@@ -69,541 +69,559 @@
 #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;
 }
-- 
cgit v1.2.3-55-g6feb