Sync to trunk (r44371)
[reactos.git] / reactos / dll / win32 / crypt32 / decode.c
index e6ca94c..31a21fb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005 Juan Lang
+ * Copyright 2005-2009 Juan Lang
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -17,8 +17,8 @@
  *
  * This file implements ASN.1 DER decoding of a limited set of types.
  * It isn't a full ASN.1 implementation.  Microsoft implements BER
- * encoding of many of the basic types in msasn1.dll, but that interface is
- * undocumented, so I implement them here.
+ * encoding of many of the basic types in msasn1.dll, but that interface isn't
+ * implemented, so I implement them here.
  *
  * References:
  * "A Layman's Guide to a Subset of ASN.1, BER, and DER", by Burton Kaliski
  *
  * RFC3280, http://www.faqs.org/rfcs/rfc3280.html
  *
- * MSDN, especially:
- * http://msdn.microsoft.com/library/en-us/seccrypto/security/constants_for_cryptencodeobject_and_cryptdecodeobject.asp
+ * MSDN, especially "Constants for CryptEncodeObject and CryptDecodeObject"
  */
 
+#include "config.h"
+#include "wine/port.h"
+
 #include <assert.h>
 #include <stdarg.h>
 #include <stdio.h>
 
 #include "windef.h"
 #include "winbase.h"
-#include "excpt.h"
 #include "wincrypt.h"
 #include "winnls.h"
-#include "winreg.h"
 #include "snmp.h"
 #include "wine/debug.h"
 #include "wine/exception.h"
 #define ASN_FLAGS_MASK 0xe0
 #define ASN_TYPE_MASK  0x1f
 
-WINE_DEFAULT_DEBUG_CHANNEL(crypt);
-
-struct GenericArray
-{
-    DWORD cItems;
-    BYTE *rgItems;
-};
+WINE_DEFAULT_DEBUG_CHANNEL(cryptasn);
+WINE_DECLARE_DEBUG_CHANNEL(crypt);
 
 typedef BOOL (WINAPI *CryptDecodeObjectFunc)(DWORD, LPCSTR, const BYTE *,
  DWORD, DWORD, void *, DWORD *);
 typedef BOOL (WINAPI *CryptDecodeObjectExFunc)(DWORD, LPCSTR, const BYTE *,
  DWORD, DWORD, PCRYPT_DECODE_PARA, void *, DWORD *);
 
-/* Prototypes for built-in decoders.  They follow the Ex style prototypes.
- * The dwCertEncodingType and lpszStructType are ignored by the built-in
- * functions, but the parameters are retained to simplify CryptDecodeObjectEx,
- * since it must call functions in external DLLs that follow these signatures.
+/* Internal decoders don't do memory allocation or exception handling, and
+ * they report how many bytes they decoded.
  */
-static BOOL WINAPI CRYPT_AsnDecodeChoiceOfTime(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
-static BOOL WINAPI CRYPT_AsnDecodePubKeyInfoInternal(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
-/* Like CRYPT_AsnDecodeExtensions, except assumes rgExtension is set ahead of
- * time, doesn't do memory allocation, and doesn't do exception handling.
- * (This isn't intended to be the externally-called one.)
+typedef BOOL (*InternalDecodeFunc)(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded);
+
+static BOOL CRYPT_AsnDecodeChoiceOfTimeInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded);
+static BOOL CRYPT_AsnDecodePubKeyInfoInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded);
+/* Assumes pvStructInfo is a CERT_EXTENSION whose pszObjId is set ahead of time.
  */
-static BOOL WINAPI CRYPT_AsnDecodeExtensionsInternal(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
-/* Assumes algo->Parameters.pbData is set ahead of time.  Internal func. */
-static BOOL WINAPI CRYPT_AsnDecodeAlgorithmId(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
-/* Internal function */
-static BOOL WINAPI CRYPT_AsnDecodeBool(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
+static BOOL CRYPT_AsnDecodeExtension(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded);
+/* Assumes algo->Parameters.pbData is set ahead of time. */
+static BOOL CRYPT_AsnDecodeAlgorithmId(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded);
+static BOOL CRYPT_AsnDecodeBool(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded);
 /* Assumes the CRYPT_DATA_BLOB's pbData member has been initialized */
-static BOOL WINAPI CRYPT_AsnDecodeOctetsInternal(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
-/* Like CRYPT_AsnDecodeBits, but assumes the CRYPT_INTEGER_BLOB's pbData
- * member has been initialized, doesn't do exception handling, and doesn't do
- * memory allocation.
- */
-static BOOL WINAPI CRYPT_AsnDecodeBitsInternal(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
-static BOOL WINAPI CRYPT_AsnDecodeBits(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
-static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
+static BOOL CRYPT_AsnDecodeOctetsInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded);
+/* Doesn't check the tag, assumes the caller does so */
+static BOOL CRYPT_AsnDecodeBitsInternal(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded);
+static BOOL CRYPT_AsnDecodeIntInternal(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded);
 /* Like CRYPT_AsnDecodeInteger, but assumes the CRYPT_INTEGER_BLOB's pbData
  * member has been initialized, doesn't do exception handling, and doesn't do
  * memory allocation.  Also doesn't check tag, assumes the caller has checked
  * it.
  */
-static BOOL WINAPI CRYPT_AsnDecodeIntegerInternal(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
+static BOOL CRYPT_AsnDecodeIntegerInternal(const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded);
 /* Like CRYPT_AsnDecodeInteger, but unsigned.  */
-static BOOL WINAPI CRYPT_AsnDecodeUnsignedIntegerInternal(
- DWORD dwCertEncodingType, LPCSTR lpszStructType, const BYTE *pbEncoded,
- DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
- void *pvStructInfo, DWORD *pcbStructInfo);
-
-BOOL WINAPI CryptDecodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType,
- const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo,
- DWORD *pcbStructInfo)
-{
-    static HCRYPTOIDFUNCSET set = NULL;
-    BOOL ret = FALSE;
-    CryptDecodeObjectFunc pCryptDecodeObject;
-    HCRYPTOIDFUNCADDR hFunc;
-
-    TRACE("(0x%08x, %s, %p, %d, 0x%08x, %p, %p)\n", dwCertEncodingType,
-     debugstr_a(lpszStructType), pbEncoded, cbEncoded, dwFlags,
-     pvStructInfo, pcbStructInfo);
-
-    if (!pvStructInfo && !pcbStructInfo)
-    {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
-
-    /* Try registered DLL first.. */
-    if (!set)
-        set = CryptInitOIDFunctionSet(CRYPT_OID_DECODE_OBJECT_FUNC, 0);
-    CryptGetOIDFunctionAddress(set, dwCertEncodingType, lpszStructType, 0,
-     (void **)&pCryptDecodeObject, &hFunc);
-    if (pCryptDecodeObject)
-    {
-        ret = pCryptDecodeObject(dwCertEncodingType, lpszStructType,
-         pbEncoded, cbEncoded, dwFlags, pvStructInfo, pcbStructInfo);
-        CryptFreeOIDFunctionAddress(hFunc, 0);
-    }
-    else
-    {
-        /* If not, use CryptDecodeObjectEx */
-        ret = CryptDecodeObjectEx(dwCertEncodingType, lpszStructType, pbEncoded,
-         cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo);
-    }
-    return ret;
-}
+static BOOL CRYPT_AsnDecodeUnsignedIntegerInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded);
+static BOOL CRYPT_AsnDecodePKCSAttributeInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded);
 
 /* Gets the number of length bytes from the given (leading) length byte */
-#define GET_LEN_BYTES(b) ((b) <= 0x7f ? 1 : 1 + ((b) & 0x7f))
+#define GET_LEN_BYTES(b) ((b) <= 0x80 ? 1 : 1 + ((b) & 0x7f))
 
 /* Helper function to get the encoded length of the data starting at pbEncoded,
  * where pbEncoded[0] is the tag.  If the data are too short to contain a
  * length or if the length is too large for cbEncoded, sets an appropriate
- * error code and returns FALSE.
+ * error code and returns FALSE.  If the encoded length is unknown due to
+ * indefinite length encoding, *len is set to CMSG_INDEFINITE_LENGTH.
  */
-static BOOL WINAPI CRYPT_GetLen(const BYTE *pbEncoded, DWORD cbEncoded,
+static BOOL CRYPT_GetLengthIndefinite(const BYTE *pbEncoded, DWORD cbEncoded,
  DWORD *len)
 {
     BOOL ret;
@@ -195,6 +141,11 @@ static BOOL WINAPI CRYPT_GetLen(const BYTE *pbEncoded, DWORD cbEncoded,
             ret = TRUE;
         }
     }
+    else if (pbEncoded[1] == 0x80)
+    {
+        *len = CMSG_INDEFINITE_LENGTH;
+        ret = TRUE;
+    }
     else
     {
         BYTE lenLen = GET_LEN_BYTES(pbEncoded[1]);
@@ -234,6 +185,20 @@ static BOOL WINAPI CRYPT_GetLen(const BYTE *pbEncoded, DWORD cbEncoded,
     return ret;
 }
 
+/* Like CRYPT_GetLengthIndefinite, but disallows indefinite-length encoding. */
+static BOOL CRYPT_GetLen(const BYTE *pbEncoded, DWORD cbEncoded, DWORD *len)
+{
+    BOOL ret;
+
+    if ((ret = CRYPT_GetLengthIndefinite(pbEncoded, cbEncoded, len)) &&
+     *len == CMSG_INDEFINITE_LENGTH)
+    {
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        ret = FALSE;
+    }
+    return ret;
+}
+
 /* Helper function to check *pcbStructInfo, set it to the required size, and
  * optionally to allocate memory.  Assumes pvStructInfo is not NULL.
  * If CRYPT_DECODE_ALLOC_FLAG is set in dwFlags, *pvStructInfo will be set to a
@@ -262,6 +227,37 @@ static BOOL CRYPT_DecodeEnsureSpace(DWORD dwFlags,
         SetLastError(ERROR_MORE_DATA);
         ret = FALSE;
     }
+    else
+        *pcbStructInfo = bytesNeeded;
+    return ret;
+}
+
+static void CRYPT_FreeSpace(PCRYPT_DECODE_PARA pDecodePara, LPVOID pv)
+{
+    if (pDecodePara && pDecodePara->pfnFree)
+        pDecodePara->pfnFree(pv);
+    else
+        LocalFree(pv);
+}
+
+/* Helper function to check *pcbStructInfo and set it to the required size.
+ * Assumes pvStructInfo is not NULL.
+ */
+static BOOL CRYPT_DecodeCheckSpace(DWORD *pcbStructInfo, DWORD bytesNeeded)
+{
+    BOOL ret;
+
+    if (*pcbStructInfo < bytesNeeded)
+    {
+        *pcbStructInfo = bytesNeeded;
+        SetLastError(ERROR_MORE_DATA);
+        ret = FALSE;
+    }
+    else
+    {
+        *pcbStructInfo = bytesNeeded;
+        ret = TRUE;
+    }
     return ret;
 }
 
@@ -280,47 +276,66 @@ static BOOL CRYPT_DecodeEnsureSpace(DWORD dwFlags,
  *     If true, and the tag doesn't match the expected tag for this item,
  *     or the decodeFunc fails with CRYPT_E_ASN1_BADTAG, then minSize space is
  *     filled with 0 for this member.
- * hasPointer, pointerOffset, minSize:
+ * hasPointer, pointerOffset:
  *     If the item has dynamic data, set hasPointer to TRUE, pointerOffset to
- *     the offset within the (outer) struct of the data pointer (or to the
+ *     the offset within the struct of the data pointer (or to the
  *     first data pointer, if more than one exist).
  * size:
  *     Used by CRYPT_AsnDecodeSequence, not for your use.
  */
 struct AsnDecodeSequenceItem
 {
-    BYTE                    tag;
-    DWORD                   offset;
-    CryptDecodeObjectExFunc decodeFunc;
-    DWORD                   minSize;
-    BOOL                    optional;
-    BOOL                    hasPointer;
-    DWORD                   pointerOffset;
-    DWORD                   size;
+    BYTE               tag;
+    DWORD              offset;
+    InternalDecodeFunc decodeFunc;
+    DWORD              minSize;
+    BOOL               optional;
+    BOOL               hasPointer;
+    DWORD              pointerOffset;
+    DWORD              size;
 };
 
-static BOOL CRYPT_AsnDecodeSequenceItems(DWORD dwCertEncodingType,
- struct AsnDecodeSequenceItem items[], DWORD cItem, const BYTE *pbEncoded,
- DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, BYTE *nextData)
+#define FINALMEMBERSIZE(s, member) (sizeof(s) - offsetof(s, member))
+#define MEMBERSIZE(s, member, nextmember) \
+    (offsetof(s, nextmember) - offsetof(s, member))
+
+/* Decodes the items in a sequence, where the items are described in items,
+ * the encoded data are in pbEncoded with length cbEncoded.  Decodes into
+ * pvStructInfo.  nextData is a pointer to the memory location at which the
+ * first decoded item with a dynamic pointer should point.
+ * Upon decoding, *cbDecoded is the total number of bytes decoded.
+ * Each item decoder is never called with CRYPT_DECODE_ALLOC_FLAG set.
+ */
+static BOOL CRYPT_AsnDecodeSequenceItems(struct AsnDecodeSequenceItem items[],
+ DWORD cItem, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ void *pvStructInfo, BYTE *nextData, DWORD *cbDecoded)
 {
     BOOL ret;
-    DWORD i;
-    const BYTE *ptr;
+    DWORD i, decoded = 0;
+    const BYTE *ptr = pbEncoded;
+
+    TRACE("%p, %d, %p, %d, %08x, %p, %p, %p\n", items, cItem, pbEncoded,
+     cbEncoded, dwFlags, pvStructInfo, nextData, cbDecoded);
 
-    ptr = pbEncoded + 1 + GET_LEN_BYTES(pbEncoded[1]);
     for (i = 0, ret = TRUE; ret && i < cItem; i++)
     {
         if (cbEncoded - (ptr - pbEncoded) != 0)
         {
-            DWORD nextItemLen;
+            DWORD itemLen;
 
-            if ((ret = CRYPT_GetLen(ptr, cbEncoded - (ptr - pbEncoded),
-             &nextItemLen)))
+            if ((ret = CRYPT_GetLengthIndefinite(ptr,
+             cbEncoded - (ptr - pbEncoded), &itemLen)))
             {
-                BYTE nextItemLenBytes = GET_LEN_BYTES(ptr[1]);
+                BYTE itemLenBytes = GET_LEN_BYTES(ptr[1]);
 
                 if (ptr[0] == items[i].tag || !items[i].tag)
                 {
+                    DWORD itemEncodedLen;
+
+                    if (itemLen == CMSG_INDEFINITE_LENGTH)
+                        itemEncodedLen = cbEncoded - (ptr - pbEncoded);
+                    else
+                        itemEncodedLen = 1 + itemLenBytes + itemLen;
                     if (nextData && pvStructInfo && items[i].hasPointer)
                     {
                         TRACE("Setting next pointer to %p\n",
@@ -330,31 +345,43 @@ static BOOL CRYPT_AsnDecodeSequenceItems(DWORD dwCertEncodingType,
                     }
                     if (items[i].decodeFunc)
                     {
+                        DWORD itemDecoded;
+
                         if (pvStructInfo)
                             TRACE("decoding item %d\n", i);
                         else
                             TRACE("sizing item %d\n", i);
-                        ret = items[i].decodeFunc(dwCertEncodingType,
-                         NULL, ptr, 1 + nextItemLenBytes + nextItemLen,
-                         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL,
+                        ret = items[i].decodeFunc(ptr, itemEncodedLen,
+                         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG,
                          pvStructInfo ?  (BYTE *)pvStructInfo + items[i].offset
-                         : NULL, &items[i].size);
+                         : NULL, &items[i].size, &itemDecoded);
                         if (ret)
                         {
+                            if (items[i].size < items[i].minSize)
+                                items[i].size = items[i].minSize;
+                            else if (items[i].size > items[i].minSize)
+                            {
+                            /* Account for alignment padding */
+                            items[i].size = ALIGN_DWORD_PTR(items[i].size);
+                            }
+                            TRACE("item %d size: %d\n", i, items[i].size);
                             if (nextData && items[i].hasPointer &&
                              items[i].size > items[i].minSize)
-                            {
                                 nextData += items[i].size - items[i].minSize;
-                                /* align nextData to DWORD boundaries */
-                                if (items[i].size % sizeof(DWORD))
-                                    nextData += sizeof(DWORD) - items[i].size %
-                                     sizeof(DWORD);
+                            if (itemDecoded > itemEncodedLen)
+                            {
+                                WARN("decoded length %d exceeds encoded %d\n",
+                                 itemDecoded, itemEncodedLen);
+                                SetLastError(CRYPT_E_ASN1_CORRUPT);
+                                ret = FALSE;
+                            }
+                            else
+                            {
+                                ptr += itemDecoded;
+                                decoded += itemDecoded;
+                                TRACE("item %d: decoded %d bytes\n", i,
+                                 itemDecoded);
                             }
-                            /* Account for alignment padding */
-                            if (items[i].size % sizeof(DWORD))
-                                items[i].size += sizeof(DWORD) -
-                                 items[i].size % sizeof(DWORD);
-                            ptr += 1 + nextItemLenBytes + nextItemLen;
                         }
                         else if (items[i].optional &&
                          GetLastError() == CRYPT_E_ASN1_BADTAG)
@@ -368,8 +395,19 @@ static BOOL CRYPT_AsnDecodeSequenceItems(DWORD dwCertEncodingType,
                             TRACE("item %d failed: %08x\n", i,
                              GetLastError());
                     }
+                    else if (itemLen == CMSG_INDEFINITE_LENGTH)
+                    {
+                        ERR("can't use indefinite length encoding without a decoder\n");
+                        SetLastError(CRYPT_E_ASN1_CORRUPT);
+                        ret = FALSE;
+                    }
                     else
+                    {
+                        TRACE("item %d: decoded %d bytes\n", i, itemEncodedLen);
+                        ptr += itemEncodedLen;
+                        decoded += itemEncodedLen;
                         items[i].size = items[i].minSize;
+                    }
                 }
                 else if (items[i].optional)
                 {
@@ -378,8 +416,8 @@ static BOOL CRYPT_AsnDecodeSequenceItems(DWORD dwCertEncodingType,
                 }
                 else
                 {
-                    TRACE("tag %02x doesn't match expected %02x\n",
-                     ptr[0], items[i].tag);
+                    TRACE("item %d: tag %02x doesn't match expected %02x\n",
+                     i, ptr[0], items[i].tag);
                     SetLastError(CRYPT_E_ASN1_BADTAG);
                     ret = FALSE;
                 }
@@ -397,13 +435,9 @@ static BOOL CRYPT_AsnDecodeSequenceItems(DWORD dwCertEncodingType,
             ret = FALSE;
         }
     }
-    if (cbEncoded - (ptr - pbEncoded) != 0)
-    {
-        TRACE("%d remaining bytes, failing\n", cbEncoded -
-         (ptr - pbEncoded));
-        SetLastError(CRYPT_E_ASN1_CORRUPT);
-        ret = FALSE;
-    }
+    if (cbDecoded)
+        *cbDecoded = decoded;
+    TRACE("returning %d\n", ret);
     return ret;
 }
 
@@ -413,13 +447,11 @@ static BOOL CRYPT_AsnDecodeSequenceItems(DWORD dwCertEncodingType,
  * startingPointer is an optional pointer to the first place where dynamic
  * data will be stored.  If you know the starting offset, you may pass it
  * here.  Otherwise, pass NULL, and one will be inferred from the items.
- * Each item decoder is never called with CRYPT_DECODE_ALLOC_FLAG set.
- * If any undecoded data are left over, fails with CRYPT_E_ASN1_CORRUPT.
  */
-static BOOL CRYPT_AsnDecodeSequence(DWORD dwCertEncodingType,
struct AsnDecodeSequenceItem items[], DWORD cItem, const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
void *pvStructInfo, DWORD *pcbStructInfo, void *startingPointer)
+static BOOL CRYPT_AsnDecodeSequence(struct AsnDecodeSequenceItem items[],
DWORD cItem, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded, void *startingPointer)
 {
     BOOL ret;
 
@@ -427,25 +459,75 @@ static BOOL CRYPT_AsnDecodeSequence(DWORD dwCertEncodingType,
      cbEncoded, dwFlags, pDecodePara, pvStructInfo, *pcbStructInfo,
      startingPointer);
 
+    if (!cbEncoded)
+    {
+        SetLastError(CRYPT_E_ASN1_EOD);
+        return FALSE;
+    }
     if (pbEncoded[0] == ASN_SEQUENCE)
     {
         DWORD dataLen;
 
-        if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+        if ((ret = CRYPT_GetLengthIndefinite(pbEncoded, cbEncoded, &dataLen)))
         {
-            DWORD i;
+            DWORD lenBytes = GET_LEN_BYTES(pbEncoded[1]), cbDecoded;
+            const BYTE *ptr = pbEncoded + 1 + lenBytes;
+            BOOL indefinite = FALSE;
 
-            ret = CRYPT_AsnDecodeSequenceItems(dwFlags, items, cItem, pbEncoded,
-             cbEncoded, dwFlags, NULL, NULL);
+            cbEncoded -= 1 + lenBytes;
+            if (dataLen == CMSG_INDEFINITE_LENGTH)
+            {
+                dataLen = cbEncoded;
+                indefinite = TRUE;
+            }
+            else if (cbEncoded < dataLen)
+            {
+                TRACE("dataLen %d exceeds cbEncoded %d, failing\n", dataLen,
+                 cbEncoded);
+                SetLastError(CRYPT_E_ASN1_CORRUPT);
+                ret = FALSE;
+            }
+            if (ret)
+            {
+                ret = CRYPT_AsnDecodeSequenceItems(items, cItem,
+                 ptr, dataLen, dwFlags, NULL, NULL, &cbDecoded);
+                if (ret && dataLen == CMSG_INDEFINITE_LENGTH)
+                {
+                    if (cbDecoded > cbEncoded - 2)
+                    {
+                        /* Not enough space for 0 TLV */
+                        SetLastError(CRYPT_E_ASN1_CORRUPT);
+                        ret = FALSE;
+                    }
+                    else if (*(ptr + cbDecoded) != 0 ||
+                     *(ptr + cbDecoded + 1) != 0)
+                    {
+                        TRACE("expected 0 TLV\n");
+                        SetLastError(CRYPT_E_ASN1_CORRUPT);
+                        ret = FALSE;
+                    }
+                    else
+                        cbDecoded += 2;
+                }
+            }
+            if (ret && !indefinite && cbDecoded != dataLen)
+            {
+                TRACE("expected %d decoded, got %d, failing\n", dataLen,
+                 cbDecoded);
+                SetLastError(CRYPT_E_ASN1_CORRUPT);
+                ret = FALSE;
+            }
             if (ret)
             {
-                DWORD bytesNeeded = 0, structSize = 0;
+                DWORD i, bytesNeeded = 0, structSize = 0;
 
                 for (i = 0; i < cItem; i++)
                 {
                     bytesNeeded += items[i].size;
-                    structSize += items[i].minSize;
+                    structSize = max( structSize, items[i].offset + items[i].minSize );
                 }
+                if (pcbDecoded)
+                    *pcbDecoded = 1 + lenBytes + cbDecoded;
                 if (!pvStructInfo)
                     *pcbStructInfo = bytesNeeded;
                 else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags,
@@ -456,12 +538,15 @@ static BOOL CRYPT_AsnDecodeSequence(DWORD dwCertEncodingType,
                     if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
                         pvStructInfo = *(BYTE **)pvStructInfo;
                     if (startingPointer)
-                        nextData = (BYTE *)startingPointer;
+                        nextData = startingPointer;
                     else
                         nextData = (BYTE *)pvStructInfo + structSize;
                     memset(pvStructInfo, 0, structSize);
-                    ret = CRYPT_AsnDecodeSequenceItems(dwFlags, items, cItem,
-                     pbEncoded, cbEncoded, dwFlags, pvStructInfo, nextData);
+                    ret = CRYPT_AsnDecodeSequenceItems(items, cItem,
+                     ptr, dataLen, dwFlags, pvStructInfo, nextData,
+                     &cbDecoded);
+                    if (!ret && (dwFlags & CRYPT_DECODE_ALLOC_FLAG))
+                        CRYPT_FreeSpace(pDecodePara, pvStructInfo);
                 }
             }
         }
@@ -477,7 +562,21 @@ static BOOL CRYPT_AsnDecodeSequence(DWORD dwCertEncodingType,
 
 /* tag:
  *     The expected tag of the entire encoded array (usually a variant
- *     of ASN_SETOF or ASN_SEQUENCEOF.)
+ *     of ASN_SETOF or ASN_SEQUENCEOF.)  If tag is 0, decodeFunc is called
+ *     regardless of the tag seen.
+ * countOffset:
+ *     The offset within the outer structure at which the count exists.
+ *     For example, a structure such as CRYPT_ATTRIBUTES has countOffset == 0,
+ *     while CRYPT_ATTRIBUTE has countOffset ==
+ *     offsetof(CRYPT_ATTRIBUTE, cValue).
+ * arrayOffset:
+ *     The offset within the outer structure at which the array pointer exists.
+ *     For example, CRYPT_ATTRIBUTES has arrayOffset ==
+ *     offsetof(CRYPT_ATTRIBUTES, rgAttr).
+ * minArraySize:
+ *     The minimum size of the decoded array.  On WIN32, this is always 8:
+ *     sizeof(DWORD) + sizeof(void *).  On WIN64, it can be larger due to
+ *     alignment.
  * decodeFunc:
  *     used to decode each item in the array
  * itemSize:
@@ -489,11 +588,14 @@ static BOOL CRYPT_AsnDecodeSequence(DWORD dwCertEncodingType,
  */
 struct AsnArrayDescriptor
 {
-    BYTE                    tag;
-    CryptDecodeObjectExFunc decodeFunc;
-    DWORD                   itemSize;
-    BOOL                    hasPointer;
-    DWORD                   pointerOffset;
+    BYTE               tag;
+    DWORD              countOffset;
+    DWORD              arrayOffset;
+    DWORD              minArraySize;
+    InternalDecodeFunc decodeFunc;
+    DWORD              itemSize;
+    BOOL               hasPointer;
+    DWORD              pointerOffset;
 };
 
 struct AsnArrayItemSize
@@ -502,130 +604,157 @@ struct AsnArrayItemSize
     DWORD size;
 };
 
-/* Decodes an array of like types into a struct GenericArray.
- * The layout and decoding of the array are described by a struct
+/* Decodes an array of like types into a structure described by a struct
  * AsnArrayDescriptor.
  */
 static BOOL CRYPT_AsnDecodeArray(const struct AsnArrayDescriptor *arrayDesc,
  const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo,
void *startingPointer)
DWORD *pcbDecoded)
 {
     BOOL ret = TRUE;
 
-    TRACE("%p, %p, %d, %08x, %p, %p, %d, %p\n", arrayDesc, pbEncoded,
-     cbEncoded, dwFlags, pDecodePara, pvStructInfo, *pcbStructInfo,
-     startingPointer);
+    TRACE("%p, %p, %d, %p, %d\n", arrayDesc, pbEncoded,
+     cbEncoded, pvStructInfo, pvStructInfo ? *pcbStructInfo : 0);
 
-    if (pbEncoded[0] == arrayDesc->tag)
+    if (!cbEncoded)
+    {
+        SetLastError(CRYPT_E_ASN1_EOD);
+        ret = FALSE;
+    }
+    else if (!arrayDesc->tag || pbEncoded[0] == arrayDesc->tag)
     {
         DWORD dataLen;
 
-        if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+        if ((ret = CRYPT_GetLengthIndefinite(pbEncoded, cbEncoded, &dataLen)))
         {
-            DWORD bytesNeeded, cItems = 0;
+            DWORD bytesNeeded = arrayDesc->minArraySize, cItems = 0, decoded;
             BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
             /* There can be arbitrarily many items, but there is often only one.
              */
             struct AsnArrayItemSize itemSize = { 0 }, *itemSizes = &itemSize;
 
-            bytesNeeded = sizeof(struct GenericArray);
+            decoded = 1 + lenBytes;
             if (dataLen)
             {
                 const BYTE *ptr;
+                BOOL doneDecoding = FALSE;
 
-                for (ptr = pbEncoded + 1 + lenBytes; ret &&
-                 ptr - pbEncoded - 1 - lenBytes < dataLen; )
+                for (ptr = pbEncoded + 1 + lenBytes; ret && !doneDecoding; )
                 {
-                    DWORD itemLenBytes, itemDataLen, size;
-
-                    itemLenBytes = GET_LEN_BYTES(ptr[1]);
-                    /* Each item decoded may not tolerate extraneous bytes, so
-                     * get the length of the next element and pass it directly.
-                     */
-                    ret = CRYPT_GetLen(ptr, cbEncoded - (ptr - pbEncoded),
-                     &itemDataLen);
-                    if (ret)
-                        ret = arrayDesc->decodeFunc(X509_ASN_ENCODING, 0, ptr,
-                         1 + itemLenBytes + itemDataLen,
-                         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL,
-                         &size);
-                    if (ret)
+                    if (dataLen == CMSG_INDEFINITE_LENGTH)
+                    {
+                        if (ptr[0] == 0)
+                        {
+                            doneDecoding = TRUE;
+                            if (ptr[1] != 0)
+                            {
+                                SetLastError(CRYPT_E_ASN1_CORRUPT);
+                                ret = FALSE;
+                            }
+                            else
+                                decoded += 2;
+                        }
+                    }
+                    else if (ptr - pbEncoded - 1 - lenBytes >= dataLen)
+                        doneDecoding = TRUE;
+                    if (!doneDecoding)
                     {
-                        DWORD nextLen;
+                        DWORD itemEncoded, itemDataLen, itemDecoded, size = 0;
 
-                        cItems++;
-                        if (itemSizes != &itemSize)
-                            itemSizes = CryptMemRealloc(itemSizes,
-                             cItems * sizeof(struct AsnArrayItemSize));
-                        else
+                        /* Each item decoded may not tolerate extraneous bytes,
+                         * so get the length of the next element if known.
+                         */
+                        if ((ret = CRYPT_GetLengthIndefinite(ptr,
+                         cbEncoded - (ptr - pbEncoded), &itemDataLen)))
                         {
-                            itemSizes =
-                             CryptMemAlloc(
-                             cItems * sizeof(struct AsnArrayItemSize));
-                            if (itemSizes)
-                                memcpy(itemSizes, &itemSize, sizeof(itemSize));
+                            if (itemDataLen == CMSG_INDEFINITE_LENGTH)
+                                itemEncoded = cbEncoded - (ptr - pbEncoded);
+                            else
+                                itemEncoded = 1 + GET_LEN_BYTES(ptr[1]) +
+                                 itemDataLen;
                         }
-                        if (itemSizes)
+                        if (ret)
+                            ret = arrayDesc->decodeFunc(ptr, itemEncoded,
+                             dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, &size,
+                             &itemDecoded);
+                        if (ret)
                         {
-                            itemSizes[cItems - 1].encodedLen = 1 + itemLenBytes
-                             + itemDataLen;
-                            itemSizes[cItems - 1].size = size;
-                            bytesNeeded += size;
-                            ret = CRYPT_GetLen(ptr,
-                             cbEncoded - (ptr - pbEncoded), &nextLen);
-                            if (ret)
-                                ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
+                            cItems++;
+                            if (itemSizes != &itemSize)
+                                itemSizes = CryptMemRealloc(itemSizes,
+                                 cItems * sizeof(struct AsnArrayItemSize));
+                            else if (cItems > 1)
+                            {
+                                itemSizes =
+                                 CryptMemAlloc(
+                                 cItems * sizeof(struct AsnArrayItemSize));
+                                if (itemSizes)
+                                    memcpy(itemSizes, &itemSize,
+                                     sizeof(itemSize));
+                            }
+                            if (itemSizes)
+                            {
+                                decoded += itemDecoded;
+                                itemSizes[cItems - 1].encodedLen = itemEncoded;
+                                itemSizes[cItems - 1].size = size;
+                                bytesNeeded += size;
+                                ptr += itemEncoded;
+                            }
+                            else
+                                ret = FALSE;
                         }
-                        else
-                            ret = FALSE;
                     }
                 }
             }
             if (ret)
             {
+                if (pcbDecoded)
+                    *pcbDecoded = decoded;
                 if (!pvStructInfo)
                     *pcbStructInfo = bytesNeeded;
-                else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags,
-                 pDecodePara, pvStructInfo, pcbStructInfo, bytesNeeded)))
+                else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+                 pvStructInfo, pcbStructInfo, bytesNeeded)))
                 {
-                    DWORD i;
+                    DWORD i, *pcItems;
                     BYTE *nextData;
                     const BYTE *ptr;
-                    struct GenericArray *array;
+                    void *rgItems;
 
                     if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
-                        pvStructInfo = *(BYTE **)pvStructInfo;
-                    array = (struct GenericArray *)pvStructInfo;
-                    array->cItems = cItems;
-                    if (startingPointer)
-                        array->rgItems = startingPointer;
+                        pvStructInfo = *(void **)pvStructInfo;
+                    pcItems = pvStructInfo;
+                    *pcItems = cItems;
+                    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    {
+                        rgItems = (BYTE *)pvStructInfo +
+                         arrayDesc->minArraySize;
+                        *(void **)((BYTE *)pcItems -
+                         arrayDesc->countOffset + arrayDesc->arrayOffset) =
+                         rgItems;
+                    }
                     else
-                        array->rgItems = (BYTE *)array +
-                         sizeof(struct GenericArray);
-                    nextData = (BYTE *)array->rgItems +
-                     array->cItems * arrayDesc->itemSize;
+                        rgItems = *(void **)((BYTE *)pcItems -
+                         arrayDesc->countOffset + arrayDesc->arrayOffset);
+                    nextData = (BYTE *)rgItems + cItems * arrayDesc->itemSize;
                     for (i = 0, ptr = pbEncoded + 1 + lenBytes; ret &&
                      i < cItems && ptr - pbEncoded - 1 - lenBytes <
                      dataLen; i++)
                     {
+                        DWORD itemDecoded;
+
                         if (arrayDesc->hasPointer)
-                            *(BYTE **)(array->rgItems + i * arrayDesc->itemSize
+                            *(BYTE **)((BYTE *)rgItems + i * arrayDesc->itemSize
                              + arrayDesc->pointerOffset) = nextData;
-                        ret = arrayDesc->decodeFunc(X509_ASN_ENCODING, 0, ptr,
+                        ret = arrayDesc->decodeFunc(ptr,
                          itemSizes[i].encodedLen,
-                         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL,
-                         array->rgItems + i * arrayDesc->itemSize,
-                         &itemSizes[i].size);
+                         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG,
+                         (BYTE *)rgItems + i * arrayDesc->itemSize,
+                         &itemSizes[i].size, &itemDecoded);
                         if (ret)
                         {
-                            DWORD nextLen;
-
                             nextData += itemSizes[i].size - arrayDesc->itemSize;
-                            ret = CRYPT_GetLen(ptr,
-                             cbEncoded - (ptr - pbEncoded), &nextLen);
-                            if (ret)
-                                ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
+                            ptr += itemDecoded;
                         }
                     }
                 }
@@ -648,9 +777,8 @@ static BOOL CRYPT_AsnDecodeArray(const struct AsnArrayDescriptor *arrayDesc,
  * Warning: assumes the CRYPT_DER_BLOB pointed to by pvStructInfo has pbData
  * set!
  */
-static BOOL WINAPI CRYPT_AsnDecodeDerBlob(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeDerBlob(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
     BOOL ret;
     DWORD dataLen;
@@ -659,20 +787,21 @@ static BOOL WINAPI CRYPT_AsnDecodeDerBlob(DWORD dwCertEncodingType,
     {
         BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
         DWORD bytesNeeded = sizeof(CRYPT_DER_BLOB);
-
+       
         if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG))
             bytesNeeded += 1 + lenBytes + dataLen;
 
+        if (pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
         if (!pvStructInfo)
             *pcbStructInfo = bytesNeeded;
-        else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
-         pvStructInfo, pcbStructInfo, bytesNeeded)))
+        else if ((ret = CRYPT_DecodeCheckSpace(pcbStructInfo, bytesNeeded)))
         {
             CRYPT_DER_BLOB *blob;
 
             if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
                 pvStructInfo = *(BYTE **)pvStructInfo;
-            blob = (CRYPT_DER_BLOB *)pvStructInfo;
+            blob = pvStructInfo;
             blob->cbData = 1 + lenBytes + dataLen;
             if (blob->cbData)
             {
@@ -695,24 +824,24 @@ static BOOL WINAPI CRYPT_AsnDecodeDerBlob(DWORD dwCertEncodingType,
 }
 
 /* Like CRYPT_AsnDecodeBitsInternal, but swaps the bytes */
-static BOOL WINAPI CRYPT_AsnDecodeBitsSwapBytes(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeBitsSwapBytes(const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded)
 {
     BOOL ret;
 
-    TRACE("(%p, %d, 0x%08x, %p, %p, %d)\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
+    TRACE("(%p, %d, 0x%08x, %p, %d, %p)\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
 
     /* Can't use the CRYPT_DECODE_NOCOPY_FLAG, because we modify the bytes in-
      * place.
      */
-    ret = CRYPT_AsnDecodeBitsInternal(dwCertEncodingType, lpszStructType,
-     pbEncoded, cbEncoded, dwFlags & ~CRYPT_DECODE_NOCOPY_FLAG, pDecodePara,
-     pvStructInfo, pcbStructInfo);
+    ret = CRYPT_AsnDecodeBitsInternal(pbEncoded, cbEncoded,
+     dwFlags & ~CRYPT_DECODE_NOCOPY_FLAG, pvStructInfo, pcbStructInfo,
+     pcbDecoded);
     if (ret && pvStructInfo)
     {
-        CRYPT_BIT_BLOB *blob = (CRYPT_BIT_BLOB *)pvStructInfo;
+        CRYPT_BIT_BLOB *blob = pvStructInfo;
 
         if (blob->cbData)
         {
@@ -757,9 +886,9 @@ static BOOL WINAPI CRYPT_AsnDecodeCertSignedContent(DWORD dwCertEncodingType,
 
         if (dwFlags & CRYPT_DECODE_NO_SIGNATURE_BYTE_REVERSAL_FLAG)
             items[2].decodeFunc = CRYPT_AsnDecodeBitsInternal;
-        ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
-         sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+        ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+         pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo,
+         pcbStructInfo, NULL, NULL);
     }
     __EXCEPT_PAGE_FAULT
     {
@@ -772,10 +901,8 @@ static BOOL WINAPI CRYPT_AsnDecodeCertSignedContent(DWORD dwCertEncodingType,
     return ret;
 }
 
-/* Internal function */
-static BOOL WINAPI CRYPT_AsnDecodeCertVersion(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeCertVersion(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
     BOOL ret;
     DWORD dataLen;
@@ -784,36 +911,54 @@ static BOOL WINAPI CRYPT_AsnDecodeCertVersion(DWORD dwCertEncodingType,
     {
         BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
 
-        ret = CRYPT_AsnDecodeInt(dwCertEncodingType, X509_INTEGER,
-         pbEncoded + 1 + lenBytes, dataLen, dwFlags, pDecodePara,
-         pvStructInfo, pcbStructInfo);
+        ret = CRYPT_AsnDecodeIntInternal(pbEncoded + 1 + lenBytes, dataLen,
+         dwFlags, pvStructInfo, pcbStructInfo, NULL);
+        if (pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
     }
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeValidity(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeValidity(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
     BOOL ret;
 
     struct AsnDecodeSequenceItem items[] = {
      { 0, offsetof(CERT_PRIVATE_KEY_VALIDITY, NotBefore),
-       CRYPT_AsnDecodeChoiceOfTime, sizeof(FILETIME), FALSE, FALSE, 0 },
+       CRYPT_AsnDecodeChoiceOfTimeInternal, sizeof(FILETIME), FALSE, FALSE, 0 },
      { 0, offsetof(CERT_PRIVATE_KEY_VALIDITY, NotAfter),
-       CRYPT_AsnDecodeChoiceOfTime, sizeof(FILETIME), FALSE, FALSE, 0 },
+       CRYPT_AsnDecodeChoiceOfTimeInternal, sizeof(FILETIME), FALSE, FALSE, 0 },
     };
 
-    ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
-     sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, NULL);
     return ret;
 }
 
-/* Internal function */
-static BOOL WINAPI CRYPT_AsnDecodeCertExtensions(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeCertExtensionsInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret = TRUE;
+    struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+     offsetof(CERT_INFO, cExtension), offsetof(CERT_INFO, rgExtension),
+     FINALMEMBERSIZE(CERT_INFO, cExtension),
+     CRYPT_AsnDecodeExtension, sizeof(CERT_EXTENSION), TRUE,
+     offsetof(CERT_EXTENSION, pszObjId) };
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeCertExtensions(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
 {
     BOOL ret;
     DWORD dataLen;
@@ -822,14 +967,15 @@ static BOOL WINAPI CRYPT_AsnDecodeCertExtensions(DWORD dwCertEncodingType,
     {
         BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
 
-        ret = CRYPT_AsnDecodeExtensionsInternal(dwCertEncodingType,
-         X509_EXTENSIONS, pbEncoded + 1 + lenBytes, dataLen, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo);
+        ret = CRYPT_AsnDecodeCertExtensionsInternal(pbEncoded + 1 + lenBytes,
+         dataLen, dwFlags, pvStructInfo, pcbStructInfo, NULL);
+        if (ret && pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
     }
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeCertInfo(DWORD dwCertEncodingType,
+static BOOL CRYPT_AsnDecodeCertInfo(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
@@ -856,23 +1002,41 @@ static BOOL WINAPI CRYPT_AsnDecodeCertInfo(DWORD dwCertEncodingType,
        CRYPT_AsnDecodePubKeyInfoInternal, sizeof(CERT_PUBLIC_KEY_INFO),
        FALSE, TRUE, offsetof(CERT_INFO,
        SubjectPublicKeyInfo.Algorithm.Parameters.pbData), 0 },
-     { ASN_BITSTRING, offsetof(CERT_INFO, IssuerUniqueId),
+     { ASN_CONTEXT | 1, offsetof(CERT_INFO, IssuerUniqueId),
        CRYPT_AsnDecodeBitsInternal, sizeof(CRYPT_BIT_BLOB), TRUE, TRUE,
        offsetof(CERT_INFO, IssuerUniqueId.pbData), 0 },
-     { ASN_BITSTRING, offsetof(CERT_INFO, SubjectUniqueId),
+     { ASN_CONTEXT | 2, offsetof(CERT_INFO, SubjectUniqueId),
        CRYPT_AsnDecodeBitsInternal, sizeof(CRYPT_BIT_BLOB), TRUE, TRUE,
        offsetof(CERT_INFO, SubjectUniqueId.pbData), 0 },
      { ASN_CONTEXT | ASN_CONSTRUCTOR | 3, offsetof(CERT_INFO, cExtension),
-       CRYPT_AsnDecodeCertExtensions, sizeof(CERT_EXTENSIONS), TRUE, TRUE,
-       offsetof(CERT_INFO, rgExtension), 0 },
+       CRYPT_AsnDecodeCertExtensions, FINALMEMBERSIZE(CERT_INFO, cExtension),
+       TRUE, TRUE, offsetof(CERT_INFO, rgExtension), 0 },
     };
 
     TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
      pDecodePara, pvStructInfo, *pcbStructInfo);
 
-    ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
-     sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo, pcbStructInfo,
+     NULL, NULL);
+    if (ret && pvStructInfo)
+    {
+        CERT_INFO *info;
+
+        if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+            info = *(CERT_INFO **)pvStructInfo;
+        else
+            info = pvStructInfo;
+        if (!info->SerialNumber.cbData || !info->Issuer.cbData ||
+         !info->Subject.cbData)
+        {
+            SetLastError(CRYPT_E_ASN1_CORRUPT);
+            /* Don't need to deallocate, because it should have failed on the
+             * first pass (and no memory was allocated.)
+             */
+            ret = FALSE;
+        }
+    }
 
     TRACE("Returning %d (%08x)\n", ret, GetLastError());
     return ret;
@@ -882,28 +1046,32 @@ static BOOL WINAPI CRYPT_AsnDecodeCert(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
-    BOOL ret = TRUE;
+    BOOL ret = FALSE;
 
     TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
      pDecodePara, pvStructInfo, *pcbStructInfo);
 
     __TRY
     {
-        PCERT_SIGNED_CONTENT_INFO signedCert = NULL;
         DWORD size = 0;
 
-        /* First try to decode it as a signed cert. */
-        ret = CRYPT_AsnDecodeCertSignedContent(dwCertEncodingType, X509_CERT,
-         pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL,
-         (BYTE *)&signedCert, &size);
-        if (ret)
+        /* Unless told not to, first try to decode it as a signed cert. */
+        if (!(dwFlags & CRYPT_DECODE_TO_BE_SIGNED_FLAG))
         {
-            size = 0;
-            ret = CRYPT_AsnDecodeCertInfo(dwCertEncodingType,
-             X509_CERT_TO_BE_SIGNED, signedCert->ToBeSigned.pbData,
-             signedCert->ToBeSigned.cbData, dwFlags, pDecodePara, pvStructInfo,
-             pcbStructInfo);
-            LocalFree(signedCert);
+            PCERT_SIGNED_CONTENT_INFO signedCert = NULL;
+
+            ret = CRYPT_AsnDecodeCertSignedContent(dwCertEncodingType,
+             X509_CERT, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL,
+             &signedCert, &size);
+            if (ret)
+            {
+                size = 0;
+                ret = CRYPT_AsnDecodeCertInfo(dwCertEncodingType,
+                 X509_CERT_TO_BE_SIGNED, signedCert->ToBeSigned.pbData,
+                 signedCert->ToBeSigned.cbData, dwFlags, pDecodePara,
+                 pvStructInfo, pcbStructInfo);
+                LocalFree(signedCert);
+            }
         }
         /* Failing that, try it as an unsigned cert */
         if (!ret)
@@ -917,7 +1085,6 @@ static BOOL WINAPI CRYPT_AsnDecodeCert(DWORD dwCertEncodingType,
     __EXCEPT_PAGE_FAULT
     {
         SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
     }
     __ENDTRY
 
@@ -925,87 +1092,149 @@ static BOOL WINAPI CRYPT_AsnDecodeCert(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeCRLEntry(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeCRLEntryExtensions(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret = TRUE;
+    struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+     offsetof(CRL_ENTRY, cExtension), offsetof(CRL_ENTRY, rgExtension),
+     FINALMEMBERSIZE(CRL_ENTRY, cExtension),
+     CRYPT_AsnDecodeExtension, sizeof(CERT_EXTENSION), TRUE,
+     offsetof(CERT_EXTENSION, pszObjId) };
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeCRLEntry(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
     BOOL ret;
     struct AsnDecodeSequenceItem items[] = {
      { ASN_INTEGER, offsetof(CRL_ENTRY, SerialNumber),
        CRYPT_AsnDecodeIntegerInternal, sizeof(CRYPT_INTEGER_BLOB), FALSE, TRUE,
        offsetof(CRL_ENTRY, SerialNumber.pbData), 0 },
-     { 0, offsetof(CRL_ENTRY, RevocationDate), CRYPT_AsnDecodeChoiceOfTime,
-       sizeof(FILETIME), FALSE, FALSE, 0 },
+     { 0, offsetof(CRL_ENTRY, RevocationDate),
+       CRYPT_AsnDecodeChoiceOfTimeInternal, sizeof(FILETIME), FALSE, FALSE, 0 },
      { ASN_SEQUENCEOF, offsetof(CRL_ENTRY, cExtension),
-       CRYPT_AsnDecodeExtensionsInternal, sizeof(CERT_EXTENSIONS), TRUE, TRUE,
+       CRYPT_AsnDecodeCRLEntryExtensions,
+       FINALMEMBERSIZE(CRL_ENTRY, cExtension), TRUE, TRUE,
        offsetof(CRL_ENTRY, rgExtension), 0 },
     };
-    PCRL_ENTRY entry = (PCRL_ENTRY)pvStructInfo;
+    PCRL_ENTRY entry = pvStructInfo;
 
     TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags, entry,
      *pcbStructInfo);
 
-    ret = CRYPT_AsnDecodeSequence(X509_ASN_ENCODING, items,
-     sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-     NULL, entry, pcbStructInfo, entry ? entry->SerialNumber.pbData : NULL);
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, entry, pcbStructInfo, pcbDecoded,
+     entry ? entry->SerialNumber.pbData : NULL);
+    if (ret && entry && !entry->SerialNumber.cbData)
+    {
+        WARN("empty CRL entry serial number\n");
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        ret = FALSE;
+    }
     return ret;
 }
 
-/* Warning: assumes pvStructInfo is a struct GenericArray whose rgItems has
- * been set prior to calling.
+/* Warning: assumes pvStructInfo points to the cCRLEntry member of a CRL_INFO
+ * whose rgCRLEntry member has been set prior to calling.
  */
-static BOOL WINAPI CRYPT_AsnDecodeCRLEntries(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeCRLEntries(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
     BOOL ret;
     struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+     offsetof(CRL_INFO, cCRLEntry), offsetof(CRL_INFO, rgCRLEntry),
+     MEMBERSIZE(CRL_INFO, cCRLEntry, cExtension),
      CRYPT_AsnDecodeCRLEntry, sizeof(CRL_ENTRY), TRUE,
      offsetof(CRL_ENTRY, SerialNumber.pbData) };
-    struct GenericArray *entries = (struct GenericArray *)pvStructInfo;
 
-    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
 
-    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo,
-     entries ? entries->rgItems : NULL);
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
     TRACE("Returning %d (%08x)\n", ret, GetLastError());
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeCRLInfo(DWORD dwCertEncodingType,
+static BOOL CRYPT_AsnDecodeCRLExtensionsInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret = TRUE;
+    struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+     offsetof(CRL_INFO, cExtension), offsetof(CRL_INFO, rgExtension),
+     FINALMEMBERSIZE(CRL_INFO, cExtension),
+     CRYPT_AsnDecodeExtension, sizeof(CERT_EXTENSION), TRUE,
+     offsetof(CERT_EXTENSION, pszObjId) };
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeCRLExtensions(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    DWORD dataLen;
+
+    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+    {
+        BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+
+        ret = CRYPT_AsnDecodeCRLExtensionsInternal(pbEncoded + 1 + lenBytes,
+         dataLen, dwFlags, pvStructInfo, pcbStructInfo, NULL);
+        if (ret && pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
+    }
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeCRLInfo(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
     struct AsnDecodeSequenceItem items[] = {
      { ASN_INTEGER, offsetof(CRL_INFO, dwVersion),
-       CRYPT_AsnDecodeInt, sizeof(DWORD), TRUE, FALSE, 0, 0 },
+       CRYPT_AsnDecodeIntInternal, sizeof(DWORD), TRUE, FALSE, 0, 0 },
      { ASN_SEQUENCEOF, offsetof(CRL_INFO, SignatureAlgorithm),
        CRYPT_AsnDecodeAlgorithmId, sizeof(CRYPT_ALGORITHM_IDENTIFIER),
        FALSE, TRUE, offsetof(CRL_INFO, SignatureAlgorithm.pszObjId), 0 },
      { 0, offsetof(CRL_INFO, Issuer), CRYPT_AsnDecodeDerBlob,
        sizeof(CRYPT_DER_BLOB), FALSE, TRUE, offsetof(CRL_INFO,
        Issuer.pbData) },
-     { 0, offsetof(CRL_INFO, ThisUpdate), CRYPT_AsnDecodeChoiceOfTime,
+     { 0, offsetof(CRL_INFO, ThisUpdate), CRYPT_AsnDecodeChoiceOfTimeInternal,
        sizeof(FILETIME), FALSE, FALSE, 0 },
-     { 0, offsetof(CRL_INFO, NextUpdate), CRYPT_AsnDecodeChoiceOfTime,
+     { 0, offsetof(CRL_INFO, NextUpdate), CRYPT_AsnDecodeChoiceOfTimeInternal,
        sizeof(FILETIME), TRUE, FALSE, 0 },
      { ASN_SEQUENCEOF, offsetof(CRL_INFO, cCRLEntry),
-       CRYPT_AsnDecodeCRLEntries, sizeof(struct GenericArray), TRUE, TRUE,
-       offsetof(CRL_INFO, rgCRLEntry), 0 },
+       CRYPT_AsnDecodeCRLEntries, MEMBERSIZE(CRL_INFO, cCRLEntry, cExtension),
+       TRUE, TRUE, offsetof(CRL_INFO, rgCRLEntry), 0 },
      { ASN_CONTEXT | ASN_CONSTRUCTOR | 0, offsetof(CRL_INFO, cExtension),
-       CRYPT_AsnDecodeCertExtensions, sizeof(CERT_EXTENSIONS), TRUE, TRUE,
-       offsetof(CRL_INFO, rgExtension), 0 },
+       CRYPT_AsnDecodeCRLExtensions, FINALMEMBERSIZE(CRL_INFO, cExtension),
+       TRUE, TRUE, offsetof(CRL_INFO, rgExtension), 0 },
     };
     BOOL ret = TRUE;
 
     TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
      pDecodePara, pvStructInfo, *pcbStructInfo);
 
-    ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
-     sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo, pcbStructInfo,
+     NULL, NULL);
 
     TRACE("Returning %d (%08x)\n", ret, GetLastError());
     return ret;
@@ -1015,28 +1244,32 @@ static BOOL WINAPI CRYPT_AsnDecodeCRL(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
-    BOOL ret = TRUE;
+    BOOL ret = FALSE;
 
     TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
      pDecodePara, pvStructInfo, *pcbStructInfo);
 
     __TRY
     {
-        PCERT_SIGNED_CONTENT_INFO signedCrl = NULL;
         DWORD size = 0;
 
-        /* First try to decode it as a signed crl. */
-        ret = CRYPT_AsnDecodeCertSignedContent(dwCertEncodingType, X509_CERT,
-         pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL,
-         (BYTE *)&signedCrl, &size);
-        if (ret)
+        /* Unless told not to, first try to decode it as a signed crl. */
+        if (!(dwFlags & CRYPT_DECODE_TO_BE_SIGNED_FLAG))
         {
-            size = 0;
-            ret = CRYPT_AsnDecodeCRLInfo(dwCertEncodingType,
-             X509_CERT_CRL_TO_BE_SIGNED, signedCrl->ToBeSigned.pbData,
-             signedCrl->ToBeSigned.cbData, dwFlags, pDecodePara,
-             pvStructInfo, pcbStructInfo);
-            LocalFree(signedCrl);
+            PCERT_SIGNED_CONTENT_INFO signedCrl = NULL;
+
+            ret = CRYPT_AsnDecodeCertSignedContent(dwCertEncodingType,
+             X509_CERT, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL,
+             &signedCrl, &size);
+            if (ret)
+            {
+                size = 0;
+                ret = CRYPT_AsnDecodeCRLInfo(dwCertEncodingType,
+                 X509_CERT_CRL_TO_BE_SIGNED, signedCrl->ToBeSigned.pbData,
+                 signedCrl->ToBeSigned.cbData, dwFlags, pDecodePara,
+                 pvStructInfo, pcbStructInfo);
+                LocalFree(signedCrl);
+            }
         }
         /* Failing that, try it as an unsigned crl */
         if (!ret)
@@ -1050,7 +1283,6 @@ static BOOL WINAPI CRYPT_AsnDecodeCRL(DWORD dwCertEncodingType,
     __EXCEPT_PAGE_FAULT
     {
         SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
     }
     __ENDTRY
 
@@ -1058,42 +1290,87 @@ static BOOL WINAPI CRYPT_AsnDecodeCRL(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeOidInternal(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeOidIgnoreTag(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
     BOOL ret = TRUE;
+    DWORD dataLen;
 
-    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo);
 
-    if (pbEncoded[0] == ASN_OBJECTIDENTIFIER)
+    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
     {
-        DWORD dataLen;
+        BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+        DWORD bytesNeeded = sizeof(LPSTR);
 
-        if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+        if (dataLen)
         {
-            BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
-            DWORD bytesNeeded = sizeof(LPSTR);
+            /* The largest possible string for the first two components
+             * is 2.175 (= 2 * 40 + 175 = 255), so this is big enough.
+             */
+            char firstTwo[6];
+            const BYTE *ptr;
+
+            snprintf(firstTwo, sizeof(firstTwo), "%d.%d",
+             pbEncoded[1 + lenBytes] / 40,
+             pbEncoded[1 + lenBytes] - (pbEncoded[1 + lenBytes] / 40)
+             * 40);
+            bytesNeeded += strlen(firstTwo) + 1;
+            for (ptr = pbEncoded + 2 + lenBytes; ret &&
+             ptr - pbEncoded - 1 - lenBytes < dataLen; )
+            {
+                /* large enough for ".4000000" */
+                char str[9];
+                int val = 0;
 
+                while (ptr - pbEncoded - 1 - lenBytes < dataLen &&
+                 (*ptr & 0x80))
+                {
+                    val <<= 7;
+                    val |= *ptr & 0x7f;
+                    ptr++;
+                }
+                if (ptr - pbEncoded - 1 - lenBytes >= dataLen ||
+                 (*ptr & 0x80))
+                {
+                    SetLastError(CRYPT_E_ASN1_CORRUPT);
+                    ret = FALSE;
+                }
+                else
+                {
+                    val <<= 7;
+                    val |= *ptr++;
+                    snprintf(str, sizeof(str), ".%d", val);
+                    bytesNeeded += strlen(str);
+                }
+            }
+        }
+        if (pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
+        if (!pvStructInfo)
+            *pcbStructInfo = bytesNeeded;
+        else if (*pcbStructInfo < bytesNeeded)
+        {
+            *pcbStructInfo = bytesNeeded;
+            SetLastError(ERROR_MORE_DATA);
+            ret = FALSE;
+        }
+        else
+        {
             if (dataLen)
             {
-                /* The largest possible string for the first two components
-                 * is 2.175 (= 2 * 40 + 175 = 255), so this is big enough.
-                 */
-                char firstTwo[6];
                 const BYTE *ptr;
+                LPSTR pszObjId = *(LPSTR *)pvStructInfo;
 
-                snprintf(firstTwo, sizeof(firstTwo), "%d.%d",
-                 pbEncoded[1 + lenBytes] / 40,
-                 pbEncoded[1 + lenBytes] - (pbEncoded[1 + lenBytes] / 40)
-                 * 40);
-                bytesNeeded += strlen(firstTwo) + 1;
+                *pszObjId = 0;
+                sprintf(pszObjId, "%d.%d", pbEncoded[1 + lenBytes] / 40,
+                 pbEncoded[1 + lenBytes] - (pbEncoded[1 + lenBytes] /
+                 40) * 40);
+                pszObjId += strlen(pszObjId);
                 for (ptr = pbEncoded + 2 + lenBytes; ret &&
                  ptr - pbEncoded - 1 - lenBytes < dataLen; )
                 {
-                    /* large enough for ".4000000" */
-                    char str[9];
                     int val = 0;
 
                     while (ptr - pbEncoded - 1 - lenBytes < dataLen &&
@@ -1103,78 +1380,45 @@ static BOOL WINAPI CRYPT_AsnDecodeOidInternal(DWORD dwCertEncodingType,
                         val |= *ptr & 0x7f;
                         ptr++;
                     }
-                    if (ptr - pbEncoded - 1 - lenBytes >= dataLen ||
-                     (*ptr & 0x80))
-                    {
-                        SetLastError(CRYPT_E_ASN1_CORRUPT);
-                        ret = FALSE;
-                    }
-                    else
-                    {
-                        val <<= 7;
-                        val |= *ptr++;
-                        snprintf(str, sizeof(str), ".%d", val);
-                        bytesNeeded += strlen(str);
-                    }
+                    val <<= 7;
+                    val |= *ptr++;
+                    sprintf(pszObjId, ".%d", val);
+                    pszObjId += strlen(pszObjId);
                 }
             }
-            if (!pvStructInfo)
-                *pcbStructInfo = bytesNeeded;
-            else if (*pcbStructInfo < bytesNeeded)
-            {
-                *pcbStructInfo = bytesNeeded;
-                SetLastError(ERROR_MORE_DATA);
-                ret = FALSE;
-            }
             else
-            {
-                if (dataLen)
-                {
-                    const BYTE *ptr;
-                    LPSTR pszObjId = *(LPSTR *)pvStructInfo;
+                *(LPSTR *)pvStructInfo = NULL;
+            *pcbStructInfo = bytesNeeded;
+        }
+    }
+    return ret;
+}
 
-                    *pszObjId = 0;
-                    sprintf(pszObjId, "%d.%d", pbEncoded[1 + lenBytes] / 40,
-                     pbEncoded[1 + lenBytes] - (pbEncoded[1 + lenBytes] /
-                     40) * 40);
-                    pszObjId += strlen(pszObjId);
-                    for (ptr = pbEncoded + 2 + lenBytes; ret &&
-                     ptr - pbEncoded - 1 - lenBytes < dataLen; )
-                    {
-                        int val = 0;
+static BOOL CRYPT_AsnDecodeOidInternal(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
+{
+    BOOL ret;
 
-                        while (ptr - pbEncoded - 1 - lenBytes < dataLen &&
-                         (*ptr & 0x80))
-                        {
-                            val <<= 7;
-                            val |= *ptr & 0x7f;
-                            ptr++;
-                        }
-                        val <<= 7;
-                        val |= *ptr++;
-                        sprintf(pszObjId, ".%d", val);
-                        pszObjId += strlen(pszObjId);
-                    }
-                }
-                else
-                    *(LPSTR *)pvStructInfo = NULL;
-                *pcbStructInfo = bytesNeeded;
-            }
-        }
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo);
+
+    if (pbEncoded[0] == ASN_OBJECTIDENTIFIER)
+        ret = CRYPT_AsnDecodeOidIgnoreTag(pbEncoded, cbEncoded, dwFlags,
+         pvStructInfo, pcbStructInfo, pcbDecoded);
+    else
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        ret = FALSE;
     }
     return ret;
 }
 
-/* Warning:  assumes pvStructInfo is a CERT_EXTENSION whose pszObjId is set
- * ahead of time!
- */
-static BOOL WINAPI CRYPT_AsnDecodeExtension(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeExtension(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
     struct AsnDecodeSequenceItem items[] = {
      { ASN_OBJECTIDENTIFIER, offsetof(CERT_EXTENSION, pszObjId),
-       CRYPT_AsnDecodeOidInternal, sizeof(LPSTR), FALSE, TRUE,
+       CRYPT_AsnDecodeOidIgnoreTag, sizeof(LPSTR), FALSE, TRUE,
        offsetof(CERT_EXTENSION, pszObjId), 0 },
      { ASN_BOOL, offsetof(CERT_EXTENSION, fCritical), CRYPT_AsnDecodeBool,
        sizeof(BOOL), TRUE, FALSE, 0, 0 },
@@ -1183,16 +1427,16 @@ static BOOL WINAPI CRYPT_AsnDecodeExtension(DWORD dwCertEncodingType,
        offsetof(CERT_EXTENSION, Value.pbData) },
     };
     BOOL ret = TRUE;
-    PCERT_EXTENSION ext = (PCERT_EXTENSION)pvStructInfo;
+    PCERT_EXTENSION ext = pvStructInfo;
 
     TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags, ext,
      *pcbStructInfo);
 
     if (ext)
         TRACE("ext->pszObjId is %p\n", ext->pszObjId);
-    ret = CRYPT_AsnDecodeSequence(X509_ASN_ENCODING, items,
-     sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags, NULL,
-     ext, pcbStructInfo, ext ? ext->pszObjId : NULL);
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, ext, pcbStructInfo,
+     pcbDecoded, ext ? ext->pszObjId : NULL);
     if (ext)
         TRACE("ext->pszObjId is %p (%s)\n", ext->pszObjId,
          debugstr_a(ext->pszObjId));
@@ -1200,55 +1444,27 @@ static BOOL WINAPI CRYPT_AsnDecodeExtension(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeExtensionsInternal(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
-{
-    BOOL ret = TRUE;
-    struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
-     CRYPT_AsnDecodeExtension, sizeof(CERT_EXTENSION), TRUE,
-     offsetof(CERT_EXTENSION, pszObjId) };
-    PCERT_EXTENSIONS exts = (PCERT_EXTENSIONS)pvStructInfo;
-
-    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
-
-    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo, exts ? exts->rgExtension : NULL);
-    return ret;
-}
-
 static BOOL WINAPI CRYPT_AsnDecodeExtensions(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
     BOOL ret = TRUE;
 
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, pvStructInfo ? *pcbStructInfo : 0);
+
     __TRY
     {
-        ret = CRYPT_AsnDecodeExtensionsInternal(dwCertEncodingType,
-         lpszStructType, pbEncoded, cbEncoded,
-         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, pcbStructInfo);
-        if (ret && pvStructInfo)
-        {
-            ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
-             pcbStructInfo, *pcbStructInfo);
-            if (ret)
-            {
-                CERT_EXTENSIONS *exts;
+        struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+         offsetof(CERT_EXTENSIONS, cExtension),
+         offsetof(CERT_EXTENSIONS, rgExtension),
+         sizeof(CERT_EXTENSIONS),
+         CRYPT_AsnDecodeExtension, sizeof(CERT_EXTENSION), TRUE,
+         offsetof(CERT_EXTENSION, pszObjId) };
 
-                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
-                    pvStructInfo = *(BYTE **)pvStructInfo;
-                exts = (CERT_EXTENSIONS *)pvStructInfo;
-                exts->rgExtension = (CERT_EXTENSION *)((BYTE *)exts +
-                 sizeof(CERT_EXTENSIONS));
-                ret = CRYPT_AsnDecodeExtensionsInternal(dwCertEncodingType,
-                 lpszStructType, pbEncoded, cbEncoded,
-                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo,
-                 pcbStructInfo);
+        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+         dwFlags, pDecodePara, pvStructInfo, pcbStructInfo, NULL);
             }
-        }
-    }
     __EXCEPT_PAGE_FAULT
     {
         SetLastError(STATUS_ACCESS_VIOLATION);
@@ -1262,13 +1478,13 @@ static BOOL WINAPI CRYPT_AsnDecodeExtensions(DWORD dwCertEncodingType,
  * order to avoid overwriting memory.  (In some cases, it may change it, if it
  * doesn't copy anything to memory.)  Be sure to set it correctly!
  */
-static BOOL WINAPI CRYPT_AsnDecodeNameValueInternal(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeNameValueInternal(const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded)
 {
     BOOL ret = TRUE;
     DWORD dataLen;
-    CERT_NAME_VALUE *value = (CERT_NAME_VALUE *)pvStructInfo;
+    CERT_NAME_VALUE *value = pvStructInfo;
 
     if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
     {
@@ -1340,6 +1556,8 @@ static BOOL WINAPI CRYPT_AsnDecodeNameValueInternal(DWORD dwCertEncodingType,
             return FALSE;
         }
 
+        if (pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
         if (!value)
             *pcbStructInfo = bytesNeeded;
         else if (*pcbStructInfo < bytesNeeded)
@@ -1394,7 +1612,7 @@ static BOOL WINAPI CRYPT_AsnDecodeNameValueInternal(DWORD dwCertEncodingType,
                     LPWSTR str = (LPWSTR)value->Value.pbData;
 
                     value->Value.cbData = MultiByteToWideChar(CP_UTF8, 0,
-                     (LPCSTR)pbEncoded + 1 + lenBytes, dataLen,
+                     (LPCSTR)pbEncoded + 1 + lenBytes, dataLen, 
                      str, bytesNeeded - sizeof(CERT_NAME_VALUE)) * 2;
                     break;
                 }
@@ -1418,9 +1636,8 @@ static BOOL WINAPI CRYPT_AsnDecodeNameValue(DWORD dwCertEncodingType,
 
     __TRY
     {
-        ret = CRYPT_AsnDecodeNameValueInternal(dwCertEncodingType,
-         lpszStructType, pbEncoded, cbEncoded,
-         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, pcbStructInfo);
+        ret = CRYPT_AsnDecodeNameValueInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pcbStructInfo, NULL);
         if (ret && pvStructInfo)
         {
             ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
@@ -1431,12 +1648,11 @@ static BOOL WINAPI CRYPT_AsnDecodeNameValue(DWORD dwCertEncodingType,
 
                 if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
                     pvStructInfo = *(BYTE **)pvStructInfo;
-                value = (CERT_NAME_VALUE *)pvStructInfo;
+                value = pvStructInfo;
                 value->Value.pbData = ((BYTE *)value + sizeof(CERT_NAME_VALUE));
-                ret = CRYPT_AsnDecodeNameValueInternal(dwCertEncodingType,
-                 lpszStructType, pbEncoded, cbEncoded,
-                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo,
-                 pcbStructInfo);
+                ret = CRYPT_AsnDecodeNameValueInternal( pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo,
+                 pcbStructInfo, NULL);
             }
         }
     }
@@ -1449,14 +1665,13 @@ static BOOL WINAPI CRYPT_AsnDecodeNameValue(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeUnicodeNameValueInternal(
- DWORD dwCertEncodingType, LPCSTR lpszStructType, const BYTE *pbEncoded,
- DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
- void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeUnicodeNameValueInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
 {
     BOOL ret = TRUE;
     DWORD dataLen;
-    CERT_NAME_VALUE *value = (CERT_NAME_VALUE *)pvStructInfo;
+    CERT_NAME_VALUE *value = pvStructInfo;
 
     if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
     {
@@ -1467,54 +1682,67 @@ static BOOL WINAPI CRYPT_AsnDecodeUnicodeNameValueInternal(
         {
         case ASN_NUMERICSTRING:
             valueType = CERT_RDN_NUMERIC_STRING;
-            bytesNeeded += dataLen * 2;
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
             break;
         case ASN_PRINTABLESTRING:
             valueType = CERT_RDN_PRINTABLE_STRING;
-            bytesNeeded += dataLen * 2;
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
             break;
         case ASN_IA5STRING:
             valueType = CERT_RDN_IA5_STRING;
-            bytesNeeded += dataLen * 2;
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
             break;
         case ASN_T61STRING:
             valueType = CERT_RDN_T61_STRING;
-            bytesNeeded += dataLen * 2;
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
             break;
         case ASN_VIDEOTEXSTRING:
             valueType = CERT_RDN_VIDEOTEX_STRING;
-            bytesNeeded += dataLen * 2;
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
             break;
         case ASN_GRAPHICSTRING:
             valueType = CERT_RDN_GRAPHIC_STRING;
-            bytesNeeded += dataLen * 2;
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
             break;
         case ASN_VISIBLESTRING:
             valueType = CERT_RDN_VISIBLE_STRING;
-            bytesNeeded += dataLen * 2;
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
             break;
         case ASN_GENERALSTRING:
             valueType = CERT_RDN_GENERAL_STRING;
-            bytesNeeded += dataLen * 2;
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
             break;
         case ASN_UNIVERSALSTRING:
             valueType = CERT_RDN_UNIVERSAL_STRING;
-            bytesNeeded += dataLen / 2;
+            if (dataLen)
+                bytesNeeded += dataLen / 2 + sizeof(WCHAR);
             break;
         case ASN_BMPSTRING:
             valueType = CERT_RDN_BMP_STRING;
-            bytesNeeded += dataLen;
+            if (dataLen)
+                bytesNeeded += dataLen + sizeof(WCHAR);
             break;
         case ASN_UTF8STRING:
             valueType = CERT_RDN_UTF8_STRING;
-            bytesNeeded += MultiByteToWideChar(CP_UTF8, 0,
-             (LPCSTR)pbEncoded + 1 + lenBytes, dataLen, NULL, 0) * 2;
+            if (dataLen)
+                bytesNeeded += (MultiByteToWideChar(CP_UTF8, 0,
+                 (LPCSTR)pbEncoded + 1 + lenBytes, dataLen, NULL, 0) + 1) * 2;
             break;
         default:
             SetLastError(CRYPT_E_ASN1_BADTAG);
             return FALSE;
         }
 
+        if (pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
         if (!value)
             *pcbStructInfo = bytesNeeded;
         else if (*pcbStructInfo < bytesNeeded)
@@ -1546,23 +1774,28 @@ static BOOL WINAPI CRYPT_AsnDecodeUnicodeNameValueInternal(
                     value->Value.cbData = dataLen * 2;
                     for (i = 0; i < dataLen; i++)
                         str[i] = pbEncoded[1 + lenBytes + i];
+                    str[i] = 0;
                     break;
                 case ASN_UNIVERSALSTRING:
                     value->Value.cbData = dataLen / 2;
                     for (i = 0; i < dataLen / 4; i++)
                         str[i] = (pbEncoded[1 + lenBytes + 2 * i + 2] << 8)
                          | pbEncoded[1 + lenBytes + 2 * i + 3];
+                    str[i] = 0;
                     break;
                 case ASN_BMPSTRING:
                     value->Value.cbData = dataLen;
                     for (i = 0; i < dataLen / 2; i++)
                         str[i] = (pbEncoded[1 + lenBytes + 2 * i] << 8) |
                          pbEncoded[1 + lenBytes + 2 * i + 1];
+                    str[i] = 0;
                     break;
                 case ASN_UTF8STRING:
                     value->Value.cbData = MultiByteToWideChar(CP_UTF8, 0,
                      (LPCSTR)pbEncoded + 1 + lenBytes, dataLen,
-                     str, bytesNeeded - sizeof(CERT_NAME_VALUE)) * 2;
+                     str, bytesNeeded - sizeof(CERT_NAME_VALUE)) * sizeof(WCHAR);
+                    *(WCHAR *)(value->Value.pbData + value->Value.cbData) = 0;
+                    value->Value.cbData += sizeof(WCHAR);
                     break;
                 }
             }
@@ -1584,9 +1817,8 @@ static BOOL WINAPI CRYPT_AsnDecodeUnicodeNameValue(DWORD dwCertEncodingType,
 
     __TRY
     {
-        ret = CRYPT_AsnDecodeUnicodeNameValueInternal(dwCertEncodingType,
-         lpszStructType, pbEncoded, cbEncoded,
-         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, pcbStructInfo);
+        ret = CRYPT_AsnDecodeUnicodeNameValueInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pcbStructInfo, NULL);
         if (ret && pvStructInfo)
         {
             ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
@@ -1597,12 +1829,11 @@ static BOOL WINAPI CRYPT_AsnDecodeUnicodeNameValue(DWORD dwCertEncodingType,
 
                 if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
                     pvStructInfo = *(BYTE **)pvStructInfo;
-                value = (CERT_NAME_VALUE *)pvStructInfo;
+                value = pvStructInfo;
                 value->Value.pbData = ((BYTE *)value + sizeof(CERT_NAME_VALUE));
-                ret = CRYPT_AsnDecodeUnicodeNameValueInternal(
-                 dwCertEncodingType, lpszStructType, pbEncoded, cbEncoded,
-                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo,
-                 pcbStructInfo);
+                ret = CRYPT_AsnDecodeUnicodeNameValueInternal(pbEncoded,
+                 cbEncoded, dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo,
+                 pcbStructInfo, NULL);
             }
         }
     }
@@ -1615,29 +1846,28 @@ static BOOL WINAPI CRYPT_AsnDecodeUnicodeNameValue(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeRdnAttr(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeRdnAttr(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
     BOOL ret;
     struct AsnDecodeSequenceItem items[] = {
      { ASN_OBJECTIDENTIFIER, offsetof(CERT_RDN_ATTR, pszObjId),
-       CRYPT_AsnDecodeOidInternal, sizeof(LPSTR), FALSE, TRUE,
+       CRYPT_AsnDecodeOidIgnoreTag, sizeof(LPSTR), FALSE, TRUE,
        offsetof(CERT_RDN_ATTR, pszObjId), 0 },
      { 0, offsetof(CERT_RDN_ATTR, dwValueType),
        CRYPT_AsnDecodeNameValueInternal, sizeof(CERT_NAME_VALUE),
        FALSE, TRUE, offsetof(CERT_RDN_ATTR, Value.pbData), 0 },
     };
-    CERT_RDN_ATTR *attr = (CERT_RDN_ATTR *)pvStructInfo;
+    CERT_RDN_ATTR *attr = pvStructInfo;
 
     TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
      pvStructInfo, *pcbStructInfo);
 
     if (attr)
         TRACE("attr->pszObjId is %p\n", attr->pszObjId);
-    ret = CRYPT_AsnDecodeSequence(X509_ASN_ENCODING, items,
-     sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags, NULL,
-     attr, pcbStructInfo, attr ? attr->pszObjId : NULL);
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, attr, pcbStructInfo, pcbDecoded,
+     attr ? attr->pszObjId : NULL);
     if (attr)
     {
         TRACE("attr->pszObjId is %p (%s)\n", attr->pszObjId,
@@ -1648,18 +1878,18 @@ static BOOL WINAPI CRYPT_AsnDecodeRdnAttr(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeRdn(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeRdn(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags,  void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
     BOOL ret = TRUE;
     struct AsnArrayDescriptor arrayDesc = { ASN_CONSTRUCTOR | ASN_SETOF,
+     offsetof(CERT_RDN, cRDNAttr), offsetof(CERT_RDN, rgRDNAttr),
+     sizeof(CERT_RDN),
      CRYPT_AsnDecodeRdnAttr, sizeof(CERT_RDN_ATTR), TRUE,
      offsetof(CERT_RDN_ATTR, pszObjId) };
-    PCERT_RDN rdn = (PCERT_RDN)pvStructInfo;
 
     ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo, rdn ? rdn->rgRDNAttr : NULL);
+     NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
     return ret;
 }
 
@@ -1672,11 +1902,34 @@ static BOOL WINAPI CRYPT_AsnDecodeName(DWORD dwCertEncodingType,
     __TRY
     {
         struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+         offsetof(CERT_NAME_INFO, cRDN), offsetof(CERT_NAME_INFO, rgRDN),
+         sizeof(CERT_NAME_INFO),
          CRYPT_AsnDecodeRdn, sizeof(CERT_RDN), TRUE,
          offsetof(CERT_RDN, rgRDNAttr) };
+        DWORD bytesNeeded;
+
+        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, &bytesNeeded,
+         NULL);
+        if (ret)
+        {
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                CERT_NAME_INFO *info;
 
-        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                info = pvStructInfo;
+                info->rgRDN = (CERT_RDN *)((BYTE *)pvStructInfo +
+                 sizeof(CERT_NAME_INFO));
+                ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo,
+                 &bytesNeeded, NULL);
+    }
+        }
     }
     __EXCEPT_PAGE_FAULT
     {
@@ -1687,29 +1940,29 @@ static BOOL WINAPI CRYPT_AsnDecodeName(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeUnicodeRdnAttr(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeUnicodeRdnAttr(const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded)
 {
     BOOL ret;
     struct AsnDecodeSequenceItem items[] = {
      { ASN_OBJECTIDENTIFIER, offsetof(CERT_RDN_ATTR, pszObjId),
-       CRYPT_AsnDecodeOidInternal, sizeof(LPSTR), FALSE, TRUE,
+       CRYPT_AsnDecodeOidIgnoreTag, sizeof(LPSTR), FALSE, TRUE,
        offsetof(CERT_RDN_ATTR, pszObjId), 0 },
      { 0, offsetof(CERT_RDN_ATTR, dwValueType),
        CRYPT_AsnDecodeUnicodeNameValueInternal, sizeof(CERT_NAME_VALUE),
        FALSE, TRUE, offsetof(CERT_RDN_ATTR, Value.pbData), 0 },
     };
-    CERT_RDN_ATTR *attr = (CERT_RDN_ATTR *)pvStructInfo;
+    CERT_RDN_ATTR *attr = pvStructInfo;
 
     TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
      pvStructInfo, *pcbStructInfo);
 
     if (attr)
         TRACE("attr->pszObjId is %p\n", attr->pszObjId);
-    ret = CRYPT_AsnDecodeSequence(X509_ASN_ENCODING, items,
-     sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags, NULL,
-     attr, pcbStructInfo, attr ? attr->pszObjId : NULL);
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, attr, pcbStructInfo, pcbDecoded,
+     attr ? attr->pszObjId : NULL);
     if (attr)
     {
         TRACE("attr->pszObjId is %p (%s)\n", attr->pszObjId,
@@ -1720,18 +1973,18 @@ static BOOL WINAPI CRYPT_AsnDecodeUnicodeRdnAttr(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeUnicodeRdn(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeUnicodeRdn(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
     BOOL ret = TRUE;
     struct AsnArrayDescriptor arrayDesc = { ASN_CONSTRUCTOR | ASN_SETOF,
+     offsetof(CERT_RDN, cRDNAttr), offsetof(CERT_RDN, rgRDNAttr),
+     sizeof(CERT_RDN),
      CRYPT_AsnDecodeUnicodeRdnAttr, sizeof(CERT_RDN_ATTR), TRUE,
      offsetof(CERT_RDN_ATTR, pszObjId) };
-    PCERT_RDN rdn = (PCERT_RDN)pvStructInfo;
 
     ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo, rdn ? rdn->rgRDNAttr : NULL);
+     NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
     return ret;
 }
 
@@ -1744,11 +1997,34 @@ static BOOL WINAPI CRYPT_AsnDecodeUnicodeName(DWORD dwCertEncodingType,
     __TRY
     {
         struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+         offsetof(CERT_NAME_INFO, cRDN), offsetof(CERT_NAME_INFO, rgRDN),
+         sizeof(CERT_NAME_INFO),
          CRYPT_AsnDecodeUnicodeRdn, sizeof(CERT_RDN), TRUE,
          offsetof(CERT_RDN, rgRDNAttr) };
+        DWORD bytesNeeded;
+
+        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, &bytesNeeded,
+         NULL);
+        if (ret)
+        {
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                CERT_NAME_INFO *info;
 
-        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                info = pvStructInfo;
+                info->rgRDN = (CERT_RDN *)((BYTE *)pvStructInfo +
+                 sizeof(CERT_NAME_INFO));
+                ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo,
+                 &bytesNeeded, NULL);
+    }
+        }
     }
     __EXCEPT_PAGE_FAULT
     {
@@ -1759,238 +2035,352 @@ static BOOL WINAPI CRYPT_AsnDecodeUnicodeName(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeCopyBytes(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_FindEncodedLen(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD *pcbDecoded)
 {
-    BOOL ret = TRUE;
-    DWORD bytesNeeded = sizeof(CRYPT_OBJID_BLOB);
+    BOOL ret = TRUE, done = FALSE;
+    DWORD indefiniteNestingLevels = 0, decoded = 0;
 
-    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
+    TRACE("(%p, %d)\n", pbEncoded, cbEncoded);
 
-    if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG))
-        bytesNeeded += cbEncoded;
-    if (!pvStructInfo)
-        *pcbStructInfo = bytesNeeded;
-    else if (*pcbStructInfo < bytesNeeded)
-    {
-        SetLastError(ERROR_MORE_DATA);
-        *pcbStructInfo = bytesNeeded;
-        ret = FALSE;
-    }
-    else
-    {
-        PCRYPT_OBJID_BLOB blob = (PCRYPT_OBJID_BLOB)pvStructInfo;
+    do {
+        DWORD dataLen;
 
-        *pcbStructInfo = bytesNeeded;
-        blob->cbData = cbEncoded;
-        if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
-            blob->pbData = (LPBYTE)pbEncoded;
-        else
+        if (!cbEncoded)
+            done = TRUE;
+        else if ((ret = CRYPT_GetLengthIndefinite(pbEncoded, cbEncoded,
+         &dataLen)))
         {
-            assert(blob->pbData);
-            memcpy(blob->pbData, pbEncoded, blob->cbData);
+            BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+
+            if (dataLen == CMSG_INDEFINITE_LENGTH)
+            {
+                indefiniteNestingLevels++;
+                pbEncoded += 1 + lenBytes;
+                cbEncoded -= 1 + lenBytes;
+                decoded += 1 + lenBytes;
+                TRACE("indefiniteNestingLevels = %d\n",
+                 indefiniteNestingLevels);
+            }
+            else
+            {
+                if (pbEncoded[0] == 0 && pbEncoded[1] == 0 &&
+                 indefiniteNestingLevels)
+                {
+                    indefiniteNestingLevels--;
+                    TRACE("indefiniteNestingLevels = %d\n",
+                     indefiniteNestingLevels);
+                }
+                pbEncoded += 1 + lenBytes + dataLen;
+                cbEncoded -= 1 + lenBytes + dataLen;
+                decoded += 1 + lenBytes + dataLen;
+                if (!indefiniteNestingLevels)
+                    done = TRUE;
+            }
         }
+    } while (ret && !done);
+    /* If we haven't found all 0 TLVs, we haven't found the end */
+    if (ret && indefiniteNestingLevels)
+    {
+        SetLastError(CRYPT_E_ASN1_EOD);
+        ret = FALSE;
     }
+    if (ret)
+        *pcbDecoded = decoded;
+    TRACE("returning %d (%d)\n", ret, ret ? *pcbDecoded : 0);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeAlgorithmId(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeCopyBytes(const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded)
 {
-    CRYPT_ALGORITHM_IDENTIFIER *algo =
-     (CRYPT_ALGORITHM_IDENTIFIER *)pvStructInfo;
     BOOL ret = TRUE;
-    struct AsnDecodeSequenceItem items[] = {
-     { ASN_OBJECTIDENTIFIER, offsetof(CRYPT_ALGORITHM_IDENTIFIER, pszObjId),
-       CRYPT_AsnDecodeOidInternal, sizeof(LPSTR), FALSE, TRUE,
-       offsetof(CRYPT_ALGORITHM_IDENTIFIER, pszObjId), 0 },
-     { 0, offsetof(CRYPT_ALGORITHM_IDENTIFIER, Parameters),
-       CRYPT_AsnDecodeCopyBytes, sizeof(CRYPT_OBJID_BLOB), TRUE, TRUE,
-       offsetof(CRYPT_ALGORITHM_IDENTIFIER, Parameters.pbData), 0 },
-    };
+    DWORD bytesNeeded = sizeof(CRYPT_OBJID_BLOB), encodedLen = 0;
 
-    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo);
 
-    ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
-     sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo, algo ? algo->pszObjId : NULL);
-    if (ret && pvStructInfo)
+    if ((ret = CRYPT_FindEncodedLen(pbEncoded, cbEncoded, &encodedLen)))
     {
-        TRACE("pszObjId is %p (%s)\n", algo->pszObjId,
-         debugstr_a(algo->pszObjId));
+        if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG))
+            bytesNeeded += encodedLen;
+        if (!pvStructInfo)
+            *pcbStructInfo = bytesNeeded;
+        else if (*pcbStructInfo < bytesNeeded)
+        {
+            SetLastError(ERROR_MORE_DATA);
+            *pcbStructInfo = bytesNeeded;
+            ret = FALSE;
+        }
+        else
+        {
+            PCRYPT_OBJID_BLOB blob = pvStructInfo;
+
+            *pcbStructInfo = bytesNeeded;
+            blob->cbData = encodedLen;
+            if (encodedLen)
+            {
+                if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+                    blob->pbData = (LPBYTE)pbEncoded;
+                else
+                {
+                    assert(blob->pbData);
+                    memcpy(blob->pbData, pbEncoded, blob->cbData);
+                }
+            }
+            else
+                blob->pbData = NULL;
+        }
+        if (pcbDecoded)
+            *pcbDecoded = encodedLen;
     }
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodePubKeyInfoInternal(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeCTLUsage(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
+{
+    BOOL ret;
+    struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+     offsetof(CTL_USAGE, cUsageIdentifier),
+     offsetof(CTL_USAGE, rgpszUsageIdentifier),
+     sizeof(CTL_USAGE),
+     CRYPT_AsnDecodeOidInternal, sizeof(LPSTR), TRUE, 0 };
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
+     NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeCTLEntryAttributes(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    struct AsnArrayDescriptor arrayDesc = { 0,
+     offsetof(CTL_ENTRY, cAttribute), offsetof(CTL_ENTRY, rgAttribute),
+     FINALMEMBERSIZE(CTL_ENTRY, cAttribute),
+     CRYPT_AsnDecodePKCSAttributeInternal, sizeof(CRYPT_ATTRIBUTE), TRUE,
+     offsetof(CRYPT_ATTRIBUTE, pszObjId) };
+    BOOL ret;
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeCTLEntry(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
-    BOOL ret = TRUE;
     struct AsnDecodeSequenceItem items[] = {
-     { ASN_SEQUENCEOF, offsetof(CERT_PUBLIC_KEY_INFO, Algorithm),
-       CRYPT_AsnDecodeAlgorithmId, sizeof(CRYPT_ALGORITHM_IDENTIFIER),
-       FALSE, TRUE, offsetof(CERT_PUBLIC_KEY_INFO,
-       Algorithm.pszObjId) },
-     { ASN_BITSTRING, offsetof(CERT_PUBLIC_KEY_INFO, PublicKey),
-       CRYPT_AsnDecodeBitsInternal, sizeof(CRYPT_BIT_BLOB), FALSE, TRUE,
-       offsetof(CERT_PUBLIC_KEY_INFO, PublicKey.pbData) },
+     { ASN_OCTETSTRING, offsetof(CTL_ENTRY, SubjectIdentifier),
+       CRYPT_AsnDecodeOctetsInternal, sizeof(CRYPT_DATA_BLOB), FALSE, TRUE,
+       offsetof(CTL_ENTRY, SubjectIdentifier.pbData), 0 },
+     { ASN_CONSTRUCTOR | ASN_SETOF, offsetof(CTL_ENTRY, cAttribute),
+       CRYPT_AsnDecodeCTLEntryAttributes,
+       FINALMEMBERSIZE(CTL_ENTRY, cAttribute), FALSE, TRUE,
+       offsetof(CTL_ENTRY, rgAttribute), 0 },
     };
-    PCERT_PUBLIC_KEY_INFO info = (PCERT_PUBLIC_KEY_INFO)pvStructInfo;
+    BOOL ret = TRUE;
+    CTL_ENTRY *entry = pvStructInfo;
+
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags, entry,
+     *pcbStructInfo);
 
-    ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
-     sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo, info ?
-     info->Algorithm.Parameters.pbData : NULL);
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, entry, pcbStructInfo,
+     pcbDecoded, entry ? entry->SubjectIdentifier.pbData : NULL);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodePubKeyInfo(DWORD dwCertEncodingType,
+static BOOL CRYPT_AsnDecodeCTLEntries(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
+{
+    BOOL ret;
+    struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+     offsetof(CTL_INFO, cCTLEntry), offsetof(CTL_INFO, rgCTLEntry),
+     FINALMEMBERSIZE(CTL_INFO, cExtension),
+     CRYPT_AsnDecodeCTLEntry, sizeof(CTL_ENTRY), TRUE,
+     offsetof(CTL_ENTRY, SubjectIdentifier.pbData) };
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeCTLExtensionsInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret = TRUE;
+    struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+     offsetof(CTL_INFO, cExtension), offsetof(CTL_INFO, rgExtension),
+     FINALMEMBERSIZE(CTL_INFO, cExtension),
+     CRYPT_AsnDecodeExtension, sizeof(CERT_EXTENSION), TRUE,
+     offsetof(CERT_EXTENSION, pszObjId) };
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeCTLExtensions(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    DWORD dataLen;
+
+    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+    {
+        BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+
+        ret = CRYPT_AsnDecodeCTLExtensionsInternal(pbEncoded + 1 + lenBytes,
+         dataLen, dwFlags, pvStructInfo, pcbStructInfo, NULL);
+        if (ret && pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeCTL(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
-    BOOL ret = TRUE;
+    BOOL ret = FALSE;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
 
     __TRY
     {
-        DWORD bytesNeeded;
-
-        if ((ret = CRYPT_AsnDecodePubKeyInfoInternal(dwCertEncodingType,
-         lpszStructType, pbEncoded, cbEncoded,
-         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, &bytesNeeded)))
-        {
-            if (!pvStructInfo)
-                *pcbStructInfo = bytesNeeded;
-            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
-             pvStructInfo, pcbStructInfo, bytesNeeded)))
-            {
-                PCERT_PUBLIC_KEY_INFO info;
+        struct AsnDecodeSequenceItem items[] = {
+         { ASN_INTEGER, offsetof(CTL_INFO, dwVersion),
+           CRYPT_AsnDecodeIntInternal, sizeof(DWORD), TRUE, FALSE, 0, 0 },
+         { ASN_SEQUENCEOF, offsetof(CTL_INFO, SubjectUsage),
+           CRYPT_AsnDecodeCTLUsage, sizeof(CTL_USAGE), FALSE, TRUE,
+           offsetof(CTL_INFO, SubjectUsage.rgpszUsageIdentifier), 0 },
+         { ASN_OCTETSTRING, offsetof(CTL_INFO, ListIdentifier),
+           CRYPT_AsnDecodeOctetsInternal, sizeof(CRYPT_DATA_BLOB), TRUE,
+           TRUE, offsetof(CTL_INFO, ListIdentifier.pbData), 0 },
+         { ASN_INTEGER, offsetof(CTL_INFO, SequenceNumber),
+           CRYPT_AsnDecodeIntegerInternal, sizeof(CRYPT_INTEGER_BLOB),
+           TRUE, TRUE, offsetof(CTL_INFO, SequenceNumber.pbData), 0 },
+         { 0, offsetof(CTL_INFO, ThisUpdate),
+           CRYPT_AsnDecodeChoiceOfTimeInternal, sizeof(FILETIME), FALSE, FALSE,
+           0 },
+         { 0, offsetof(CTL_INFO, NextUpdate),
+           CRYPT_AsnDecodeChoiceOfTimeInternal, sizeof(FILETIME), TRUE, FALSE,
+           0 },
+         { ASN_SEQUENCEOF, offsetof(CTL_INFO, SubjectAlgorithm),
+           CRYPT_AsnDecodeAlgorithmId, sizeof(CRYPT_ALGORITHM_IDENTIFIER),
+           FALSE, TRUE, offsetof(CTL_INFO, SubjectAlgorithm.pszObjId), 0 },
+         { ASN_SEQUENCEOF, offsetof(CTL_INFO, cCTLEntry),
+           CRYPT_AsnDecodeCTLEntries,
+           MEMBERSIZE(CTL_INFO, cCTLEntry, cExtension),
+           TRUE, TRUE, offsetof(CTL_INFO, rgCTLEntry), 0 },
+         { ASN_CONTEXT | ASN_CONSTRUCTOR | 0, offsetof(CTL_INFO, cExtension),
+           CRYPT_AsnDecodeCTLExtensions, FINALMEMBERSIZE(CTL_INFO, cExtension),
+           TRUE, TRUE, offsetof(CTL_INFO, rgExtension), 0 },
+        };
 
-                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
-                    pvStructInfo = *(BYTE **)pvStructInfo;
-                info = (PCERT_PUBLIC_KEY_INFO)pvStructInfo;
-                info->Algorithm.Parameters.pbData = (BYTE *)pvStructInfo +
-                 sizeof(CERT_PUBLIC_KEY_INFO);
-                ret = CRYPT_AsnDecodePubKeyInfoInternal(dwCertEncodingType,
-                 lpszStructType, pbEncoded, cbEncoded,
-                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo,
-                 &bytesNeeded);
-            }
-        }
+        ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+         pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo,
+         pcbStructInfo, NULL, NULL);
     }
     __EXCEPT_PAGE_FAULT
     {
         SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
     }
     __ENDTRY
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeBool(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeSMIMECapability(const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded)
 {
     BOOL ret;
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_OBJECTIDENTIFIER, offsetof(CRYPT_SMIME_CAPABILITY, pszObjId),
+       CRYPT_AsnDecodeOidIgnoreTag, sizeof(LPSTR), FALSE, TRUE,
+       offsetof(CRYPT_SMIME_CAPABILITY, pszObjId), 0 },
+     { 0, offsetof(CRYPT_SMIME_CAPABILITY, Parameters),
+       CRYPT_AsnDecodeCopyBytes, sizeof(CRYPT_OBJID_BLOB), TRUE, TRUE,
+       offsetof(CRYPT_SMIME_CAPABILITY, Parameters.pbData), 0 },
+    };
+    PCRYPT_SMIME_CAPABILITY capability = pvStructInfo;
 
-    if (cbEncoded < 3)
-    {
-        SetLastError(CRYPT_E_ASN1_CORRUPT);
-        return FALSE;
-    }
-    if (GET_LEN_BYTES(pbEncoded[1]) > 1)
-    {
-        SetLastError(CRYPT_E_ASN1_CORRUPT);
-        return FALSE;
-    }
-    if (pbEncoded[1] > 1)
-    {
-        SetLastError(CRYPT_E_ASN1_CORRUPT);
-        return FALSE;
-    }
-    if (!pvStructInfo)
-    {
-        *pcbStructInfo = sizeof(BOOL);
-        ret = TRUE;
-    }
-    else if (*pcbStructInfo < sizeof(BOOL))
-    {
-        *pcbStructInfo = sizeof(BOOL);
-        SetLastError(ERROR_MORE_DATA);
-        ret = FALSE;
-    }
-    else
-    {
-        *(BOOL *)pvStructInfo = pbEncoded[2] ? TRUE : FALSE;
-        ret = TRUE;
-    }
-    TRACE("returning %d (%08x)\n", ret, GetLastError());
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo);
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, capability ? capability->pszObjId : NULL);
+    TRACE("returning %d\n", ret);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeAltNameEntry(DWORD dwCertEncodingType,
+static BOOL WINAPI CRYPT_AsnDecodeSMIMECapabilities(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
-    PCERT_ALT_NAME_ENTRY entry = (PCERT_ALT_NAME_ENTRY)pvStructInfo;
-    DWORD dataLen, lenBytes, bytesNeeded = sizeof(CERT_ALT_NAME_ENTRY);
-    BOOL ret;
+    BOOL ret = FALSE;
 
     TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
      pDecodePara, pvStructInfo, *pcbStructInfo);
 
-    if (cbEncoded < 2)
-    {
-        SetLastError(CRYPT_E_ASN1_CORRUPT);
-        return FALSE;
-    }
-    if ((pbEncoded[0] & ASN_FLAGS_MASK) != ASN_CONTEXT)
+    __TRY
     {
-        SetLastError(CRYPT_E_ASN1_BADTAG);
-        return FALSE;
-    }
-    lenBytes = GET_LEN_BYTES(pbEncoded[1]);
-    if (1 + lenBytes > cbEncoded)
+        struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+         offsetof(CRYPT_SMIME_CAPABILITIES, cCapability),
+         offsetof(CRYPT_SMIME_CAPABILITIES, rgCapability),
+         sizeof(CRYPT_SMIME_CAPABILITIES),
+         CRYPT_AsnDecodeSMIMECapability, sizeof(CRYPT_SMIME_CAPABILITY), TRUE,
+         offsetof(CRYPT_SMIME_CAPABILITY, pszObjId) };
+
+        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+         dwFlags, pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+            }
+    __EXCEPT_PAGE_FAULT
     {
-        SetLastError(CRYPT_E_ASN1_CORRUPT);
-        return FALSE;
+        SetLastError(STATUS_ACCESS_VIOLATION);
     }
+    __ENDTRY
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeIA5String(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret = TRUE;
+    DWORD dataLen;
+    LPSTR *pStr = pvStructInfo;
+
     if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
     {
-        switch (pbEncoded[0] & ASN_TYPE_MASK)
+        BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+        DWORD bytesNeeded = sizeof(LPSTR) + sizeof(char);
+
+        if (pbEncoded[0] != ASN_IA5STRING)
         {
-        case 1: /* rfc822Name */
-        case 2: /* dNSName */
-        case 6: /* uniformResourceIdentifier */
-            bytesNeeded += (dataLen + 1) * sizeof(WCHAR);
-            break;
-        case 7: /* iPAddress */
-            bytesNeeded += dataLen;
-            break;
-        case 8: /* registeredID */
-            /* FIXME: decode as OID */
-        case 0: /* otherName */
-        case 4: /* directoryName */
-            FIXME("stub\n");
-            SetLastError(CRYPT_E_ASN1_BADTAG);
-            ret = FALSE;
-            break;
-        case 3: /* x400Address, unimplemented */
-        case 5: /* ediPartyName, unimplemented */
-            SetLastError(CRYPT_E_ASN1_BADTAG);
-            ret = FALSE;
-            break;
-        default:
             SetLastError(CRYPT_E_ASN1_CORRUPT);
             ret = FALSE;
         }
-        if (ret)
+        else
         {
-            if (!entry)
+            bytesNeeded += dataLen;
+            if (pcbDecoded)
+                *pcbDecoded = 1 + lenBytes + dataLen;
+            if (!pvStructInfo)
                 *pcbStructInfo = bytesNeeded;
             else if (*pcbStructInfo < bytesNeeded)
             {
@@ -2001,322 +2391,523 @@ static BOOL WINAPI CRYPT_AsnDecodeAltNameEntry(DWORD dwCertEncodingType,
             else
             {
                 *pcbStructInfo = bytesNeeded;
-                /* MS used values one greater than the asn1 ones.. sigh */
-                entry->dwAltNameChoice = (pbEncoded[0] & 0x7f) + 1;
-                switch (pbEncoded[0] & ASN_TYPE_MASK)
-                {
-                case 1: /* rfc822Name */
-                case 2: /* dNSName */
-                case 6: /* uniformResourceIdentifier */
+                if (dataLen)
                 {
-                    DWORD i;
+                    LPSTR str = *pStr;
 
-                    for (i = 0; i < dataLen; i++)
-                        entry->u.pwszURL[i] =
-                         (WCHAR)pbEncoded[1 + lenBytes + i];
-                    entry->u.pwszURL[i] = 0;
-                    TRACE("URL is %p (%s)\n", entry->u.pwszURL,
-                     debugstr_w(entry->u.pwszURL));
-                    break;
-                }
-                case 7: /* iPAddress */
-                    /* The next data pointer is in the pwszURL spot, that is,
-                     * the first 4 bytes.  Need to move it to the next spot.
-                     */
-                    entry->u.IPAddress.pbData = (LPBYTE)entry->u.pwszURL;
-                    entry->u.IPAddress.cbData = dataLen;
-                    memcpy(entry->u.IPAddress.pbData, pbEncoded + 1 + lenBytes,
-                     dataLen);
-                    break;
+                    assert(str);
+                    memcpy(str, pbEncoded + 1 + lenBytes, dataLen);
+                    str[dataLen] = 0;
                 }
+                else
+                    *pStr = NULL;
             }
         }
     }
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeAltNameInternal(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeNoticeNumbers(const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded)
 {
-    BOOL ret = TRUE;
     struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
-     CRYPT_AsnDecodeAltNameEntry, sizeof(CERT_ALT_NAME_ENTRY), TRUE,
-     offsetof(CERT_ALT_NAME_ENTRY, u.pwszURL) };
-    PCERT_ALT_NAME_INFO info = (PCERT_ALT_NAME_INFO)pvStructInfo;
+     offsetof(CERT_POLICY_QUALIFIER_NOTICE_REFERENCE, cNoticeNumbers),
+     offsetof(CERT_POLICY_QUALIFIER_NOTICE_REFERENCE, rgNoticeNumbers),
+     FINALMEMBERSIZE(CERT_POLICY_QUALIFIER_NOTICE_REFERENCE, cNoticeNumbers),
+     CRYPT_AsnDecodeIntInternal, sizeof(int), FALSE, 0 };
+    BOOL ret;
 
-    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
+    TRACE("(%p, %d, %08x, %p, %d)\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, pvStructInfo ? *pcbDecoded : 0);
 
-    if (info)
-        TRACE("info->rgAltEntry is %p\n", info->rgAltEntry);
-    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo, info ? info->rgAltEntry : NULL);
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    TRACE("returning %d\n", ret);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeAuthorityKeyId(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeNoticeReference(const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded)
 {
     BOOL ret;
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_IA5STRING, offsetof(CERT_POLICY_QUALIFIER_NOTICE_REFERENCE,
+       pszOrganization), CRYPT_AsnDecodeIA5String, sizeof(LPSTR), FALSE, TRUE,
+       offsetof(CERT_POLICY_QUALIFIER_NOTICE_REFERENCE, pszOrganization), 0 },
+     { ASN_SEQUENCEOF, offsetof(CERT_POLICY_QUALIFIER_NOTICE_REFERENCE,
+       cNoticeNumbers), CRYPT_AsnDecodeNoticeNumbers,
+       FINALMEMBERSIZE(CERT_POLICY_QUALIFIER_NOTICE_REFERENCE, cNoticeNumbers),
+       FALSE, TRUE, offsetof(CERT_POLICY_QUALIFIER_NOTICE_REFERENCE,
+       rgNoticeNumbers), 0 },
+    };
+    DWORD bytesNeeded;
 
-    __TRY
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, pvStructInfo ? *pcbStructInfo : 0);
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, NULL, &bytesNeeded, pcbDecoded,
+     NULL);
+    if (ret)
     {
-        struct AsnDecodeSequenceItem items[] = {
-         { ASN_CONTEXT | 0, offsetof(CERT_AUTHORITY_KEY_ID_INFO, KeyId),
-           CRYPT_AsnDecodeIntegerInternal, sizeof(CRYPT_DATA_BLOB),
-           TRUE, TRUE, offsetof(CERT_AUTHORITY_KEY_ID_INFO, KeyId.pbData), 0 },
-         { ASN_CONTEXT | ASN_CONSTRUCTOR| 1,
-           offsetof(CERT_AUTHORITY_KEY_ID_INFO, CertIssuer),
-           CRYPT_AsnDecodeOctetsInternal, sizeof(CERT_NAME_BLOB), TRUE, TRUE,
-           offsetof(CERT_AUTHORITY_KEY_ID_INFO, CertIssuer.pbData), 0 },
-         { ASN_CONTEXT | 2, offsetof(CERT_AUTHORITY_KEY_ID_INFO,
-           CertSerialNumber), CRYPT_AsnDecodeIntegerInternal,
-           sizeof(CRYPT_INTEGER_BLOB), TRUE, TRUE,
-           offsetof(CERT_AUTHORITY_KEY_ID_INFO, CertSerialNumber.pbData), 0 },
-        };
+        /* The caller is expecting a pointer to a
+         * CERT_POLICY_QUALIFIER_NOTICE_REFERENCE to be decoded, whereas
+         * CRYPT_AsnDecodeSequence is decoding a
+         * CERT_POLICY_QUALIFIER_NOTICE_REFERENCE.  Increment the bytes
+         * needed, and decode again if the requisite space is available.
+         */
+        bytesNeeded += sizeof(PCERT_POLICY_QUALIFIER_NOTICE_REFERENCE);
+        if (!pvStructInfo)
+            *pcbStructInfo = bytesNeeded;
+        else if (*pcbStructInfo < bytesNeeded)
+        {
+            *pcbStructInfo = bytesNeeded;
+            SetLastError(ERROR_MORE_DATA);
+            ret = FALSE;
+        }
+        else
+        {
+            PCERT_POLICY_QUALIFIER_NOTICE_REFERENCE noticeRef;
 
-        ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
-         sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+            *pcbStructInfo = bytesNeeded;
+            /* The pointer (pvStructInfo) passed in points to the first dynamic
+             * pointer, so use it as the pointer to the
+             * CERT_POLICY_QUALIFIER_NOTICE_REFERENCE, and create the
+             * appropriate offset for the first dynamic pointer within the
+             * notice reference by pointing to the first memory location past
+             * the CERT_POLICY_QUALIFIER_NOTICE_REFERENCE.
+             */
+            noticeRef =
+             *(PCERT_POLICY_QUALIFIER_NOTICE_REFERENCE *)pvStructInfo;
+            noticeRef->pszOrganization = (LPSTR)((LPBYTE)noticeRef +
+             sizeof(CERT_POLICY_QUALIFIER_NOTICE_REFERENCE));
+            ret = CRYPT_AsnDecodeSequence(items,
+             sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
+             NULL, noticeRef, &bytesNeeded, pcbDecoded,
+             noticeRef->pszOrganization);
+        }
     }
-    __EXCEPT_PAGE_FAULT
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeUnicodeString(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret = TRUE;
+    DWORD dataLen;
+
+    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
     {
-        SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
+        BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+        DWORD bytesNeeded = sizeof(LPWSTR);
+
+        switch (pbEncoded[0])
+        {
+        case ASN_NUMERICSTRING:
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
+            break;
+        case ASN_PRINTABLESTRING:
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
+            break;
+        case ASN_IA5STRING:
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
+            break;
+        case ASN_T61STRING:
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
+            break;
+        case ASN_VIDEOTEXSTRING:
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
+            break;
+        case ASN_GRAPHICSTRING:
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
+            break;
+        case ASN_VISIBLESTRING:
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
+            break;
+        case ASN_GENERALSTRING:
+            if (dataLen)
+                bytesNeeded += (dataLen + 1) * 2;
+            break;
+        case ASN_UNIVERSALSTRING:
+            if (dataLen)
+                bytesNeeded += dataLen / 2 + sizeof(WCHAR);
+            break;
+        case ASN_BMPSTRING:
+            if (dataLen)
+                bytesNeeded += dataLen + sizeof(WCHAR);
+            break;
+        case ASN_UTF8STRING:
+            if (dataLen)
+                bytesNeeded += (MultiByteToWideChar(CP_UTF8, 0,
+                 (LPCSTR)pbEncoded + 1 + lenBytes, dataLen, NULL, 0) + 1) * 2;
+            break;
+        default:
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+            return FALSE;
+        }
+
+        if (pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
+        if (!pvStructInfo)
+            *pcbStructInfo = bytesNeeded;
+        else if (*pcbStructInfo < bytesNeeded)
+        {
+            *pcbStructInfo = bytesNeeded;
+            SetLastError(ERROR_MORE_DATA);
+            ret = FALSE;
+        }
+        else
+        {
+            LPWSTR *pStr = pvStructInfo;
+
+            *pcbStructInfo = bytesNeeded;
+            if (dataLen)
+            {
+                DWORD i;
+                LPWSTR str = *(LPWSTR *)pStr;
+
+                assert(str);
+                switch (pbEncoded[0])
+                {
+                case ASN_NUMERICSTRING:
+                case ASN_PRINTABLESTRING:
+                case ASN_IA5STRING:
+                case ASN_T61STRING:
+                case ASN_VIDEOTEXSTRING:
+                case ASN_GRAPHICSTRING:
+                case ASN_VISIBLESTRING:
+                case ASN_GENERALSTRING:
+                    for (i = 0; i < dataLen; i++)
+                        str[i] = pbEncoded[1 + lenBytes + i];
+                    str[i] = 0;
+                    break;
+                case ASN_UNIVERSALSTRING:
+                    for (i = 0; i < dataLen / 4; i++)
+                        str[i] = (pbEncoded[1 + lenBytes + 2 * i + 2] << 8)
+                         | pbEncoded[1 + lenBytes + 2 * i + 3];
+                    str[i] = 0;
+                    break;
+                case ASN_BMPSTRING:
+                    for (i = 0; i < dataLen / 2; i++)
+                        str[i] = (pbEncoded[1 + lenBytes + 2 * i] << 8) |
+                         pbEncoded[1 + lenBytes + 2 * i + 1];
+                    str[i] = 0;
+                    break;
+                case ASN_UTF8STRING:
+                {
+                    int len = MultiByteToWideChar(CP_UTF8, 0,
+                     (LPCSTR)pbEncoded + 1 + lenBytes, dataLen,
+                     str, bytesNeeded - sizeof(CERT_NAME_VALUE)) * 2;
+                    str[len] = 0;
+                    break;
+                }
+                }
+            }
+            else
+                *pStr = NULL;
+        }
     }
-    __ENDTRY
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeAltName(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodePolicyQualifierUserNoticeInternal(
const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo,
DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
-    BOOL ret = TRUE;
+    BOOL ret;
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_SEQUENCE, offsetof(CERT_POLICY_QUALIFIER_USER_NOTICE,
+       pNoticeReference), CRYPT_AsnDecodeNoticeReference,
+       sizeof(PCERT_POLICY_QUALIFIER_NOTICE_REFERENCE), TRUE, TRUE,
+       offsetof(CERT_POLICY_QUALIFIER_USER_NOTICE, pNoticeReference), 0 },
+     { 0, offsetof(CERT_POLICY_QUALIFIER_USER_NOTICE, pszDisplayText),
+       CRYPT_AsnDecodeUnicodeString, sizeof(LPWSTR), TRUE, TRUE,
+       offsetof(CERT_POLICY_QUALIFIER_USER_NOTICE, pszDisplayText), 0 },
+    };
+    PCERT_POLICY_QUALIFIER_USER_NOTICE notice = pvStructInfo;
+
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo);
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, notice ? notice->pNoticeReference : NULL);
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodePolicyQualifierUserNotice(
+ DWORD dwCertEncodingType, LPCSTR lpszStructType, const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
+ void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = FALSE;
 
     TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
      pDecodePara, pvStructInfo, *pcbStructInfo);
 
     __TRY
     {
-        struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
-         CRYPT_AsnDecodeAltNameEntry, sizeof(CERT_ALT_NAME_ENTRY), TRUE,
-         offsetof(CERT_ALT_NAME_ENTRY, u.pwszURL) };
+        DWORD bytesNeeded;
+
+        ret = CRYPT_AsnDecodePolicyQualifierUserNoticeInternal(pbEncoded,
+         cbEncoded, dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, &bytesNeeded,
+         NULL);
+        if (ret)
+        {
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                PCERT_POLICY_QUALIFIER_USER_NOTICE notice;
 
-        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                notice = pvStructInfo;
+                notice->pNoticeReference =
+                 (PCERT_POLICY_QUALIFIER_NOTICE_REFERENCE)
+                 ((BYTE *)pvStructInfo +
+                 sizeof(CERT_POLICY_QUALIFIER_USER_NOTICE));
+                ret = CRYPT_AsnDecodePolicyQualifierUserNoticeInternal(
+                 pbEncoded, cbEncoded, dwFlags & ~CRYPT_DECODE_ALLOC_FLAG,
+                 pvStructInfo, &bytesNeeded, NULL);
+            }
+        }
     }
     __EXCEPT_PAGE_FAULT
     {
         SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
     }
     __ENDTRY
+    TRACE("returning %d\n", ret);
     return ret;
 }
 
-struct PATH_LEN_CONSTRAINT
+static BOOL CRYPT_AsnDecodePKCSAttributeValue(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
 {
-    BOOL  fPathLenConstraint;
-    DWORD dwPathLenConstraint;
-};
+    BOOL ret;
+    struct AsnArrayDescriptor arrayDesc = { 0,
+     offsetof(CRYPT_ATTRIBUTE, cValue), offsetof(CRYPT_ATTRIBUTE, rgValue),
+     FINALMEMBERSIZE(CRYPT_ATTRIBUTE, cValue),
+     CRYPT_AsnDecodeCopyBytes,
+     sizeof(CRYPT_DER_BLOB), TRUE, offsetof(CRYPT_DER_BLOB, pbData) };
 
-static BOOL WINAPI CRYPT_AsnDecodePathLenConstraint(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, pvStructInfo ? *pcbStructInfo : 0, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodePKCSAttributeInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
 {
-    BOOL ret = TRUE;
+    BOOL ret;
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_OBJECTIDENTIFIER, offsetof(CRYPT_ATTRIBUTE, pszObjId),
+       CRYPT_AsnDecodeOidIgnoreTag, sizeof(LPSTR), FALSE, TRUE,
+       offsetof(CRYPT_ATTRIBUTE, pszObjId), 0 },
+     { ASN_CONSTRUCTOR | ASN_SETOF, offsetof(CRYPT_ATTRIBUTE, cValue),
+       CRYPT_AsnDecodePKCSAttributeValue,
+       FINALMEMBERSIZE(CRYPT_ATTRIBUTE, cValue), FALSE,
+       TRUE, offsetof(CRYPT_ATTRIBUTE, rgValue), 0 },
+    };
+    PCRYPT_ATTRIBUTE attr = pvStructInfo;
 
     TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
      pvStructInfo, *pcbStructInfo);
 
-    if (cbEncoded)
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, attr ? attr->pszObjId : NULL);
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodePKCSAttribute(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = FALSE;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
+
+    __TRY
     {
-        if (pbEncoded[0] == ASN_INTEGER)
-        {
-            DWORD bytesNeeded = sizeof(struct PATH_LEN_CONSTRAINT);
+        DWORD bytesNeeded;
 
+        ret = CRYPT_AsnDecodePKCSAttributeInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, &bytesNeeded, NULL);
+        if (ret)
+        {
             if (!pvStructInfo)
                 *pcbStructInfo = bytesNeeded;
-            else if (*pcbStructInfo < bytesNeeded)
-            {
-                SetLastError(ERROR_MORE_DATA);
-                *pcbStructInfo = bytesNeeded;
-                ret = FALSE;
-            }
-            else
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
             {
-                struct PATH_LEN_CONSTRAINT *constraint =
-                 (struct PATH_LEN_CONSTRAINT *)pvStructInfo;
-                DWORD size = sizeof(constraint->dwPathLenConstraint);
+                PCRYPT_ATTRIBUTE attr;
 
-                ret = CRYPT_AsnDecodeInt(dwCertEncodingType, X509_INTEGER,
-                 pbEncoded, cbEncoded, 0, NULL,
-                 &constraint->dwPathLenConstraint, &size);
-                if (ret)
-                    constraint->fPathLenConstraint = TRUE;
-                TRACE("got an int, dwPathLenConstraint is %d\n",
-                 constraint->dwPathLenConstraint);
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                attr = pvStructInfo;
+                attr->pszObjId = (LPSTR)((BYTE *)pvStructInfo +
+                 sizeof(CRYPT_ATTRIBUTE));
+                ret = CRYPT_AsnDecodePKCSAttributeInternal(pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo, &bytesNeeded,
+                 NULL);
             }
         }
-        else
-        {
-            SetLastError(CRYPT_E_ASN1_CORRUPT);
-            ret = FALSE;
-        }
     }
-    TRACE("returning %d (%08x)\n", ret, GetLastError());
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+    }
+    __ENDTRY
+    TRACE("returning %d\n", ret);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeSubtreeConstraints(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodePKCSAttributesInternal(const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded)
 {
+    struct AsnArrayDescriptor arrayDesc = { 0,
+     offsetof(CRYPT_ATTRIBUTES, cAttr), offsetof(CRYPT_ATTRIBUTES, rgAttr),
+     sizeof(CRYPT_ATTRIBUTES),
+     CRYPT_AsnDecodePKCSAttributeInternal, sizeof(CRYPT_ATTRIBUTE), TRUE,
+     offsetof(CRYPT_ATTRIBUTE, pszObjId) };
     BOOL ret;
-    struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
-     CRYPT_AsnDecodeCopyBytes, sizeof(CERT_NAME_BLOB), TRUE,
-     offsetof(CERT_NAME_BLOB, pbData) };
-    struct GenericArray *entries = (struct GenericArray *)pvStructInfo;
-
-    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
 
     ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo,
-     entries ? entries->rgItems : NULL);
-    TRACE("Returning %d (%08x)\n", ret, GetLastError());
+     NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeBasicConstraints(DWORD dwCertEncodingType,
+static BOOL WINAPI CRYPT_AsnDecodePKCSAttributes(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
-    BOOL ret;
+    BOOL ret = FALSE;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
 
     __TRY
     {
-        struct AsnDecodeSequenceItem items[] = {
-         { ASN_BITSTRING, offsetof(CERT_BASIC_CONSTRAINTS_INFO, SubjectType),
-           CRYPT_AsnDecodeBitsInternal, sizeof(CRYPT_BIT_BLOB), FALSE, TRUE,
-           offsetof(CERT_BASIC_CONSTRAINTS_INFO, SubjectType.pbData), 0 },
-         { ASN_INTEGER, offsetof(CERT_BASIC_CONSTRAINTS_INFO,
-           fPathLenConstraint), CRYPT_AsnDecodePathLenConstraint,
-           sizeof(struct PATH_LEN_CONSTRAINT), TRUE, FALSE, 0, 0 },
-         { ASN_SEQUENCEOF, offsetof(CERT_BASIC_CONSTRAINTS_INFO,
-           cSubtreesConstraint), CRYPT_AsnDecodeSubtreeConstraints,
-           sizeof(struct GenericArray), TRUE, TRUE,
-           offsetof(CERT_BASIC_CONSTRAINTS_INFO, rgSubtreesConstraint), 0 },
-        };
+        struct AsnArrayDescriptor arrayDesc = { ASN_CONSTRUCTOR | ASN_SETOF,
+         offsetof(CRYPT_ATTRIBUTES, cAttr), offsetof(CRYPT_ATTRIBUTES, rgAttr),
+         sizeof(CRYPT_ATTRIBUTES),
+         CRYPT_AsnDecodePKCSAttributeInternal, sizeof(CRYPT_ATTRIBUTE),
+         TRUE, offsetof(CRYPT_ATTRIBUTE, pszObjId) };
 
-        ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
-         sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
-    }
+        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+         dwFlags, pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+            }
     __EXCEPT_PAGE_FAULT
     {
         SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
     }
     __ENDTRY
+    TRACE("returning %d\n", ret);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeBasicConstraints2(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeAlgorithmId(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
-    BOOL ret;
-
-    __TRY
-    {
-        struct AsnDecodeSequenceItem items[] = {
-         { ASN_BOOL, offsetof(CERT_BASIC_CONSTRAINTS2_INFO, fCA),
-           CRYPT_AsnDecodeBool, sizeof(BOOL), TRUE, FALSE, 0, 0 },
-         { ASN_INTEGER, offsetof(CERT_BASIC_CONSTRAINTS2_INFO,
-           fPathLenConstraint), CRYPT_AsnDecodePathLenConstraint,
-           sizeof(struct PATH_LEN_CONSTRAINT), TRUE, FALSE, 0, 0 },
-        };
+    CRYPT_ALGORITHM_IDENTIFIER *algo = pvStructInfo;
+    BOOL ret = TRUE;
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_OBJECTIDENTIFIER, offsetof(CRYPT_ALGORITHM_IDENTIFIER, pszObjId),
+       CRYPT_AsnDecodeOidIgnoreTag, sizeof(LPSTR), FALSE, TRUE,
+       offsetof(CRYPT_ALGORITHM_IDENTIFIER, pszObjId), 0 },
+     { 0, offsetof(CRYPT_ALGORITHM_IDENTIFIER, Parameters),
+       CRYPT_AsnDecodeCopyBytes, sizeof(CRYPT_OBJID_BLOB), TRUE, TRUE, 
+       offsetof(CRYPT_ALGORITHM_IDENTIFIER, Parameters.pbData), 0 },
+    };
 
-        ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
-         sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
-    }
-    __EXCEPT_PAGE_FAULT
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, algo ? algo->pszObjId : NULL);
+    if (ret && pvStructInfo)
     {
-        SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
+        TRACE("pszObjId is %p (%s)\n", algo->pszObjId,
+         debugstr_a(algo->pszObjId));
     }
-    __ENDTRY
     return ret;
 }
 
-#define RSA1_MAGIC 0x31415352
-
-struct DECODED_RSA_PUB_KEY
+static BOOL CRYPT_AsnDecodePubKeyInfoInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
 {
-    DWORD              pubexp;
-    CRYPT_INTEGER_BLOB modulus;
-};
+    BOOL ret = TRUE;
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_SEQUENCEOF, offsetof(CERT_PUBLIC_KEY_INFO, Algorithm),
+       CRYPT_AsnDecodeAlgorithmId, sizeof(CRYPT_ALGORITHM_IDENTIFIER),
+       FALSE, TRUE, offsetof(CERT_PUBLIC_KEY_INFO,
+       Algorithm.pszObjId) },
+     { ASN_BITSTRING, offsetof(CERT_PUBLIC_KEY_INFO, PublicKey),
+       CRYPT_AsnDecodeBitsInternal, sizeof(CRYPT_BIT_BLOB), FALSE, TRUE,
+       offsetof(CERT_PUBLIC_KEY_INFO, PublicKey.pbData) },
+    };
+    PCERT_PUBLIC_KEY_INFO info = pvStructInfo;
 
-static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey(DWORD dwCertEncodingType,
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, info ? info->Algorithm.Parameters.pbData : NULL);
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodePubKeyInfo(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
-    BOOL ret;
+    BOOL ret = TRUE;
 
     __TRY
     {
-        struct AsnDecodeSequenceItem items[] = {
-         { ASN_INTEGER, offsetof(struct DECODED_RSA_PUB_KEY, modulus),
-           CRYPT_AsnDecodeUnsignedIntegerInternal, sizeof(CRYPT_INTEGER_BLOB),
-           FALSE, TRUE, offsetof(struct DECODED_RSA_PUB_KEY, modulus.pbData),
-           0 },
-         { ASN_INTEGER, offsetof(struct DECODED_RSA_PUB_KEY, pubexp),
-           CRYPT_AsnDecodeInt, sizeof(DWORD), FALSE, FALSE, 0, 0 },
-        };
-        struct DECODED_RSA_PUB_KEY *decodedKey = NULL;
-        DWORD size = 0;
+        DWORD bytesNeeded;
 
-        ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
-         sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded,
-         CRYPT_DECODE_ALLOC_FLAG, NULL, &decodedKey, &size, NULL);
-        if (ret)
+        if ((ret = CRYPT_AsnDecodePubKeyInfoInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, &bytesNeeded, NULL)))
         {
-            DWORD bytesNeeded = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) +
-             decodedKey->modulus.cbData;
-
             if (!pvStructInfo)
-            {
                 *pcbStructInfo = bytesNeeded;
-                ret = TRUE;
-            }
             else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
              pvStructInfo, pcbStructInfo, bytesNeeded)))
             {
-                BLOBHEADER *hdr;
-                RSAPUBKEY *rsaPubKey;
+                PCERT_PUBLIC_KEY_INFO info;
 
                 if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
                     pvStructInfo = *(BYTE **)pvStructInfo;
-                hdr = (BLOBHEADER *)pvStructInfo;
-                hdr->bType = PUBLICKEYBLOB;
-                hdr->bVersion = CUR_BLOB_VERSION;
-                hdr->reserved = 0;
-                hdr->aiKeyAlg = CALG_RSA_KEYX;
-                rsaPubKey = (RSAPUBKEY *)((BYTE *)pvStructInfo +
-                 sizeof(BLOBHEADER));
-                rsaPubKey->magic = RSA1_MAGIC;
-                rsaPubKey->pubexp = decodedKey->pubexp;
-                rsaPubKey->bitlen = decodedKey->modulus.cbData * 8;
-                memcpy((BYTE *)pvStructInfo + sizeof(BLOBHEADER) +
-                 sizeof(RSAPUBKEY), decodedKey->modulus.pbData,
-                 decodedKey->modulus.cbData);
+                info = pvStructInfo;
+                info->Algorithm.Parameters.pbData = (BYTE *)pvStructInfo +
+                 sizeof(CERT_PUBLIC_KEY_INFO);
+                ret = CRYPT_AsnDecodePubKeyInfoInternal(pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo,
+                 &bytesNeeded, NULL);
             }
-            LocalFree(decodedKey);
         }
     }
     __EXCEPT_PAGE_FAULT
@@ -2328,125 +2919,121 @@ static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeOctetsInternal(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeBool(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
     BOOL ret;
-    DWORD bytesNeeded, dataLen;
-
-    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
 
-    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+    if (cbEncoded < 3)
     {
-        if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
-            bytesNeeded = sizeof(CRYPT_DATA_BLOB);
-        else
-            bytesNeeded = dataLen + sizeof(CRYPT_DATA_BLOB);
-        if (!pvStructInfo)
-            *pcbStructInfo = bytesNeeded;
-        else if (*pcbStructInfo < bytesNeeded)
-        {
-            SetLastError(ERROR_MORE_DATA);
-            *pcbStructInfo = bytesNeeded;
-            ret = FALSE;
-        }
-        else
-        {
-            CRYPT_DATA_BLOB *blob;
-            BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
-
-            blob = (CRYPT_DATA_BLOB *)pvStructInfo;
-            blob->cbData = dataLen;
-            if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
-                blob->pbData = (BYTE *)pbEncoded + 1 + lenBytes;
-            else
-            {
-                assert(blob->pbData);
-                if (blob->cbData)
-                    memcpy(blob->pbData, pbEncoded + 1 + lenBytes,
-                     blob->cbData);
-            }
-        }
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        return FALSE;
     }
-    return ret;
-}
-
-static BOOL WINAPI CRYPT_AsnDecodeOctets(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
-{
-    BOOL ret;
-
-    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
-
-    __TRY
+    if (GET_LEN_BYTES(pbEncoded[1]) > 1)
     {
-        DWORD bytesNeeded;
-
-        if (!cbEncoded)
-        {
-            SetLastError(CRYPT_E_ASN1_CORRUPT);
-            ret = FALSE;
-        }
-        else if (pbEncoded[0] != ASN_OCTETSTRING)
-        {
-            SetLastError(CRYPT_E_ASN1_BADTAG);
-            ret = FALSE;
-        }
-        else if ((ret = CRYPT_AsnDecodeOctetsInternal(dwCertEncodingType,
-         lpszStructType, pbEncoded, cbEncoded,
-         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, &bytesNeeded)))
-        {
-            if (!pvStructInfo)
-                *pcbStructInfo = bytesNeeded;
-            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
-             pvStructInfo, pcbStructInfo, bytesNeeded)))
-            {
-                CRYPT_DATA_BLOB *blob;
-
-                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
-                    pvStructInfo = *(BYTE **)pvStructInfo;
-                blob = (CRYPT_DATA_BLOB *)pvStructInfo;
-                blob->pbData = (BYTE *)pvStructInfo + sizeof(CRYPT_DATA_BLOB);
-                ret = CRYPT_AsnDecodeOctetsInternal(dwCertEncodingType,
-                 lpszStructType, pbEncoded, cbEncoded,
-                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo,
-                 &bytesNeeded);
-            }
-        }
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        return FALSE;
     }
-    __EXCEPT_PAGE_FAULT
+    if (pbEncoded[1] > 1)
     {
-        SetLastError(STATUS_ACCESS_VIOLATION);
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        return FALSE;
+    }
+    if (pcbDecoded)
+        *pcbDecoded = 3;
+    if (!pvStructInfo)
+    {
+        *pcbStructInfo = sizeof(BOOL);
+        ret = TRUE;
+    }
+    else if (*pcbStructInfo < sizeof(BOOL))
+    {
+        *pcbStructInfo = sizeof(BOOL);
+        SetLastError(ERROR_MORE_DATA);
         ret = FALSE;
     }
-    __ENDTRY
+    else
+    {
+        *pcbStructInfo = sizeof(BOOL);
+        *(BOOL *)pvStructInfo = pbEncoded[2] ? TRUE : FALSE;
+        ret = TRUE;
+    }
+    TRACE("returning %d (%08x)\n", ret, GetLastError());
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeBitsInternal(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeAltNameEntry(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
+    PCERT_ALT_NAME_ENTRY entry = pvStructInfo;
+    DWORD dataLen, lenBytes, bytesNeeded = sizeof(CERT_ALT_NAME_ENTRY);
     BOOL ret;
 
-    TRACE("(%p, %d, 0x%08x, %p, %p, %d)\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo);
 
-    if (pbEncoded[0] == ASN_BITSTRING)
+    if (cbEncoded < 2)
     {
-        DWORD bytesNeeded, dataLen;
-
-        if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        return FALSE;
+    }
+    lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+    if (1 + lenBytes > cbEncoded)
+    {
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        return FALSE;
+    }
+    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+    {
+        switch (pbEncoded[0] & ASN_TYPE_MASK)
         {
-            if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
-                bytesNeeded = sizeof(CRYPT_BIT_BLOB);
+        case 1: /* rfc822Name */
+        case 2: /* dNSName */
+        case 6: /* uniformResourceIdentifier */
+            if (memchr(pbEncoded + 1 + lenBytes, 0, dataLen))
+            {
+                SetLastError(CRYPT_E_ASN1_RULE);
+                ret = FALSE;
+            }
             else
-                bytesNeeded = dataLen - 1 + sizeof(CRYPT_BIT_BLOB);
-            if (!pvStructInfo)
+            bytesNeeded += (dataLen + 1) * sizeof(WCHAR);
+            break;
+        case 4: /* directoryName */
+        case 7: /* iPAddress */
+            bytesNeeded += dataLen;
+            break;
+        case 8: /* registeredID */
+            ret = CRYPT_AsnDecodeOidIgnoreTag(pbEncoded, cbEncoded, 0, NULL,
+             &dataLen, NULL);
+            if (ret)
+            {
+                /* FIXME: ugly, shouldn't need to know internals of OID decode
+                 * function to use it.
+                 */
+                bytesNeeded += dataLen - sizeof(LPSTR);
+            }
+            break;
+        case 0: /* otherName */
+            FIXME("%d: stub\n", pbEncoded[0] & ASN_TYPE_MASK);
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+            ret = FALSE;
+            break;
+        case 3: /* x400Address, unimplemented */
+        case 5: /* ediPartyName, unimplemented */
+            TRACE("type %d unimplemented\n", pbEncoded[0] & ASN_TYPE_MASK);
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+            ret = FALSE;
+            break;
+        default:
+            TRACE("type %d bad\n", pbEncoded[0] & ASN_TYPE_MASK);
+            SetLastError(CRYPT_E_ASN1_CORRUPT);
+            ret = FALSE;
+        }
+        if (ret)
+        {
+            if (pcbDecoded)
+                *pcbDecoded = 1 + lenBytes + dataLen;
+            if (!entry)
                 *pcbStructInfo = bytesNeeded;
             else if (*pcbStructInfo < bytesNeeded)
             {
@@ -2456,75 +3043,131 @@ static BOOL WINAPI CRYPT_AsnDecodeBitsInternal(DWORD dwCertEncodingType,
             }
             else
             {
-                CRYPT_BIT_BLOB *blob;
-
-                blob = (CRYPT_BIT_BLOB *)pvStructInfo;
-                blob->cbData = dataLen - 1;
-                blob->cUnusedBits = *(pbEncoded + 1 +
-                 GET_LEN_BYTES(pbEncoded[1]));
-                if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+                *pcbStructInfo = bytesNeeded;
+                /* MS used values one greater than the asn1 ones.. sigh */
+                entry->dwAltNameChoice = (pbEncoded[0] & ASN_TYPE_MASK) + 1;
+                switch (pbEncoded[0] & ASN_TYPE_MASK)
                 {
-                    blob->pbData = (BYTE *)pbEncoded + 2 +
-                     GET_LEN_BYTES(pbEncoded[1]);
-                }
-                else
+                case 1: /* rfc822Name */
+                case 2: /* dNSName */
+                case 6: /* uniformResourceIdentifier */
                 {
-                    assert(blob->pbData);
-                    if (blob->cbData)
-                    {
-                        BYTE mask = 0xff << blob->cUnusedBits;
+                    DWORD i;
 
-                        memcpy(blob->pbData, pbEncoded + 2 +
-                         GET_LEN_BYTES(pbEncoded[1]), blob->cbData);
-                        blob->pbData[blob->cbData - 1] &= mask;
-                    }
-                }
-            }
+                    for (i = 0; i < dataLen; i++)
+                        entry->u.pwszURL[i] =
+                         (WCHAR)pbEncoded[1 + lenBytes + i];
+                    entry->u.pwszURL[i] = 0;
+                    TRACE("URL is %p (%s)\n", entry->u.pwszURL,
+                     debugstr_w(entry->u.pwszURL));
+                    break;
+                }
+                case 4: /* directoryName */
+                    /* The data are memory-equivalent with the IPAddress case,
+                     * fall-through
+                     */
+                case 7: /* iPAddress */
+                    /* The next data pointer is in the pwszURL spot, that is,
+                     * the first 4 bytes.  Need to move it to the next spot.
+                     */
+                    entry->u.IPAddress.pbData = (LPBYTE)entry->u.pwszURL;
+                    entry->u.IPAddress.cbData = dataLen;
+                    memcpy(entry->u.IPAddress.pbData, pbEncoded + 1 + lenBytes,
+                     dataLen);
+                    break;
+                case 8: /* registeredID */
+                    ret = CRYPT_AsnDecodeOidIgnoreTag(pbEncoded, cbEncoded, 0,
+                     &entry->u.pszRegisteredID, &dataLen, NULL);
+                    break;
+                }
+            }
         }
     }
-    else
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeAltNameInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    struct AsnArrayDescriptor arrayDesc = { 0,
+     offsetof(CERT_ALT_NAME_INFO, cAltEntry),
+     offsetof(CERT_ALT_NAME_INFO, rgAltEntry),
+     sizeof(CERT_ALT_NAME_INFO),
+     CRYPT_AsnDecodeAltNameEntry, sizeof(CERT_ALT_NAME_ENTRY), TRUE,
+     offsetof(CERT_ALT_NAME_ENTRY, u.pwszURL) };
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
+     NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    return ret;
+}
+
+/* Like CRYPT_AsnDecodeIntegerInternal, but swaps the bytes */
+static BOOL CRYPT_AsnDecodeIntegerSwapBytes(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+
+    TRACE("(%p, %d, 0x%08x, %p, %d, %p)\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    /* Can't use the CRYPT_DECODE_NOCOPY_FLAG, because we modify the bytes in-
+     * place.
+     */
+    ret = CRYPT_AsnDecodeIntegerInternal(pbEncoded, cbEncoded,
+     dwFlags & ~CRYPT_DECODE_NOCOPY_FLAG, pvStructInfo, pcbStructInfo,
+     pcbDecoded);
+    if (ret && pvStructInfo)
     {
-        SetLastError(CRYPT_E_ASN1_BADTAG);
-        ret = FALSE;
+        CRYPT_DATA_BLOB *blob = pvStructInfo;
+
+        if (blob->cbData)
+        {
+            DWORD i;
+            BYTE temp;
+
+            for (i = 0; i < blob->cbData / 2; i++)
+            {
+                temp = blob->pbData[i];
+                blob->pbData[i] = blob->pbData[blob->cbData - i - 1];
+                blob->pbData[blob->cbData - i - 1] = temp;
+            }
+        }
     }
     TRACE("returning %d (%08x)\n", ret, GetLastError());
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeBits(DWORD dwCertEncodingType,
+static BOOL WINAPI CRYPT_AsnDecodeAuthorityKeyId(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
     BOOL ret;
 
-    TRACE("(%p, %d, 0x%08x, %p, %p, %p)\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo);
-
     __TRY
     {
-        DWORD bytesNeeded;
-
-        if ((ret = CRYPT_AsnDecodeBitsInternal(dwCertEncodingType,
-         lpszStructType, pbEncoded, cbEncoded,
-         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, &bytesNeeded)))
-        {
-            if (!pvStructInfo)
-                *pcbStructInfo = bytesNeeded;
-            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
-             pvStructInfo, pcbStructInfo, bytesNeeded)))
-            {
-                CRYPT_BIT_BLOB *blob;
+        struct AsnDecodeSequenceItem items[] = {
+         { ASN_CONTEXT | 0, offsetof(CERT_AUTHORITY_KEY_ID_INFO, KeyId),
+           CRYPT_AsnDecodeIntegerSwapBytes, sizeof(CRYPT_DATA_BLOB),
+           TRUE, TRUE, offsetof(CERT_AUTHORITY_KEY_ID_INFO, KeyId.pbData), 0 },
+         { ASN_CONTEXT | ASN_CONSTRUCTOR| 1,
+           offsetof(CERT_AUTHORITY_KEY_ID_INFO, CertIssuer),
+           CRYPT_AsnDecodeOctetsInternal, sizeof(CERT_NAME_BLOB), TRUE, TRUE,
+           offsetof(CERT_AUTHORITY_KEY_ID_INFO, CertIssuer.pbData), 0 },
+         { ASN_CONTEXT | 2, offsetof(CERT_AUTHORITY_KEY_ID_INFO,
+           CertSerialNumber), CRYPT_AsnDecodeIntegerInternal,
+           sizeof(CRYPT_INTEGER_BLOB), TRUE, TRUE,
+           offsetof(CERT_AUTHORITY_KEY_ID_INFO, CertSerialNumber.pbData), 0 },
+        };
 
-                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
-                    pvStructInfo = *(BYTE **)pvStructInfo;
-                blob = (CRYPT_BIT_BLOB *)pvStructInfo;
-                blob->pbData = (BYTE *)pvStructInfo + sizeof(CRYPT_BIT_BLOB);
-                ret = CRYPT_AsnDecodeBitsInternal(dwCertEncodingType,
-                 lpszStructType, pbEncoded, cbEncoded,
-                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo,
-                 &bytesNeeded);
-            }
-        }
+        ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+         pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo,
+         pcbStructInfo, NULL, NULL);
     }
     __EXCEPT_PAGE_FAULT
     {
@@ -2532,63 +3175,36 @@ static BOOL WINAPI CRYPT_AsnDecodeBits(DWORD dwCertEncodingType,
         ret = FALSE;
     }
     __ENDTRY
-    TRACE("returning %d (%08x)\n", ret, GetLastError());
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
+static BOOL WINAPI CRYPT_AsnDecodeAuthorityKeyId2(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
     BOOL ret;
 
-    if (!pvStructInfo)
-    {
-        *pcbStructInfo = sizeof(int);
-        return TRUE;
-    }
     __TRY
     {
-        BYTE buf[sizeof(CRYPT_INTEGER_BLOB) + sizeof(int)];
-        CRYPT_INTEGER_BLOB *blob = (CRYPT_INTEGER_BLOB *)buf;
-        DWORD size = sizeof(buf);
-
-        blob->pbData = buf + sizeof(CRYPT_INTEGER_BLOB);
-        if (pbEncoded[0] != ASN_INTEGER)
-        {
-            SetLastError(CRYPT_E_ASN1_BADTAG);
-            ret = FALSE;
-        }
-        else
-            ret = CRYPT_AsnDecodeIntegerInternal(dwCertEncodingType,
-             X509_MULTI_BYTE_INTEGER, pbEncoded, cbEncoded, 0, NULL, &buf,
-             &size);
-        if (ret)
-        {
-            if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
-             pvStructInfo, pcbStructInfo, sizeof(int))))
-            {
-                int val, i;
+        struct AsnDecodeSequenceItem items[] = {
+         { ASN_CONTEXT | 0, offsetof(CERT_AUTHORITY_KEY_ID2_INFO, KeyId),
+           CRYPT_AsnDecodeIntegerSwapBytes, sizeof(CRYPT_DATA_BLOB),
+           TRUE, TRUE, offsetof(CERT_AUTHORITY_KEY_ID2_INFO, KeyId.pbData), 0 },
+         { ASN_CONTEXT | ASN_CONSTRUCTOR| 1,
+           offsetof(CERT_AUTHORITY_KEY_ID2_INFO, AuthorityCertIssuer),
+           CRYPT_AsnDecodeAltNameInternal, sizeof(CERT_ALT_NAME_INFO), TRUE,
+           TRUE, offsetof(CERT_AUTHORITY_KEY_ID2_INFO,
+           AuthorityCertIssuer.rgAltEntry), 0 },
+         { ASN_CONTEXT | 2, offsetof(CERT_AUTHORITY_KEY_ID2_INFO,
+           AuthorityCertSerialNumber), CRYPT_AsnDecodeIntegerInternal,
+           sizeof(CRYPT_INTEGER_BLOB), TRUE, TRUE,
+           offsetof(CERT_AUTHORITY_KEY_ID2_INFO,
+           AuthorityCertSerialNumber.pbData), 0 },
+        };
 
-                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
-                    pvStructInfo = *(BYTE **)pvStructInfo;
-                if (blob->pbData[blob->cbData - 1] & 0x80)
-                {
-                    /* initialize to a negative value to sign-extend */
-                    val = -1;
-                }
-                else
-                    val = 0;
-                for (i = 0; i < blob->cbData; i++)
-                {
-                    val <<= 8;
-                    val |= blob->pbData[blob->cbData - i - 1];
-                }
-                memcpy(pvStructInfo, &val, sizeof(int));
-            }
-        }
-        else if (GetLastError() == ERROR_MORE_DATA)
-            SetLastError(CRYPT_E_ASN1_LARGE);
+        ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+         pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo,
+         pcbStructInfo, NULL, NULL);
     }
     __EXCEPT_PAGE_FAULT
     {
@@ -2599,84 +3215,230 @@ static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeIntegerInternal(DWORD dwCertEncodingType,
+static BOOL CRYPT_AsnDecodeAccessDescription(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    struct AsnDecodeSequenceItem items[] = {
+     { 0, offsetof(CERT_ACCESS_DESCRIPTION, pszAccessMethod),
+       CRYPT_AsnDecodeOidInternal, sizeof(LPSTR), FALSE, TRUE,
+       offsetof(CERT_ACCESS_DESCRIPTION, pszAccessMethod), 0 },
+     { 0, offsetof(CERT_ACCESS_DESCRIPTION, AccessLocation),
+       CRYPT_AsnDecodeAltNameEntry, sizeof(CERT_ALT_NAME_ENTRY), FALSE,
+       TRUE, offsetof(CERT_ACCESS_DESCRIPTION, AccessLocation.u.pwszURL), 0 },
+    };
+    CERT_ACCESS_DESCRIPTION *descr = pvStructInfo;
+
+    return CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, descr ? descr->pszAccessMethod : NULL);
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeAuthorityInfoAccess(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
     BOOL ret;
-    DWORD bytesNeeded, dataLen;
 
-    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
+
+    __TRY
     {
-        BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+        struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+         offsetof(CERT_AUTHORITY_INFO_ACCESS, cAccDescr),
+         offsetof(CERT_AUTHORITY_INFO_ACCESS, rgAccDescr),
+         sizeof(CERT_AUTHORITY_INFO_ACCESS),
+         CRYPT_AsnDecodeAccessDescription, sizeof(CERT_ACCESS_DESCRIPTION),
+         TRUE, offsetof(CERT_ACCESS_DESCRIPTION, pszAccessMethod) };
 
-        bytesNeeded = dataLen + sizeof(CRYPT_INTEGER_BLOB);
-        if (!pvStructInfo)
-            *pcbStructInfo = bytesNeeded;
-        else if (*pcbStructInfo < bytesNeeded)
-        {
-            *pcbStructInfo = bytesNeeded;
-            SetLastError(ERROR_MORE_DATA);
-            ret = FALSE;
-        }
-        else
+        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+         dwFlags, pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodePKCSContent(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
+{
+    BOOL ret;
+    DWORD dataLen;
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    /* The caller has already checked the tag, no need to check it again.
+     * Check the outer length is valid:
+     */
+    if ((ret = CRYPT_GetLengthIndefinite(pbEncoded, cbEncoded, &dataLen)))
+    {
+        BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+        DWORD innerLen;
+
+        pbEncoded += 1 + lenBytes;
+        cbEncoded -= 1 + lenBytes;
+        if (dataLen == CMSG_INDEFINITE_LENGTH)
+            cbEncoded -= 2; /* space for 0 TLV */
+        /* Check the inner length is valid: */
+        if ((ret = CRYPT_GetLengthIndefinite(pbEncoded, cbEncoded, &innerLen)))
         {
-            CRYPT_INTEGER_BLOB *blob = (CRYPT_INTEGER_BLOB *)pvStructInfo;
+            DWORD decodedLen;
 
-            blob->cbData = dataLen;
-            assert(blob->pbData);
-            if (blob->cbData)
+            ret = CRYPT_AsnDecodeCopyBytes(pbEncoded, cbEncoded, dwFlags,
+             pvStructInfo, pcbStructInfo, &decodedLen);
+            if (dataLen == CMSG_INDEFINITE_LENGTH)
             {
-                DWORD i;
-
-                for (i = 0; i < blob->cbData; i++)
+                if (*(pbEncoded + decodedLen) != 0 ||
+                 *(pbEncoded + decodedLen + 1) != 0)
                 {
-                    blob->pbData[i] = *(pbEncoded + 1 + lenBytes +
-                     dataLen - i - 1);
+                    TRACE("expected 0 TLV, got {%02x,%02x}\n",
+                     *(pbEncoded + decodedLen),
+                     *(pbEncoded + decodedLen + 1));
+                    SetLastError(CRYPT_E_ASN1_CORRUPT);
+                    ret = FALSE;
                 }
+                else
+                    decodedLen += 2;
+            }
+            if (ret && pcbDecoded)
+            {
+                *pcbDecoded = 1 + lenBytes + decodedLen;
+                TRACE("decoded %d bytes\n", *pcbDecoded);
             }
         }
     }
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeInteger(DWORD dwCertEncodingType,
+static BOOL CRYPT_AsnDecodePKCSContentInfoInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    CRYPT_CONTENT_INFO *info = pvStructInfo;
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_OBJECTIDENTIFIER, offsetof(CRYPT_CONTENT_INFO, pszObjId),
+       CRYPT_AsnDecodeOidIgnoreTag, sizeof(LPSTR), FALSE, TRUE,
+       offsetof(CRYPT_CONTENT_INFO, pszObjId), 0 },
+     { ASN_CONTEXT | ASN_CONSTRUCTOR | 0,
+       offsetof(CRYPT_CONTENT_INFO, Content), CRYPT_AsnDecodePKCSContent,
+       sizeof(CRYPT_DER_BLOB), TRUE, TRUE,
+       offsetof(CRYPT_CONTENT_INFO, Content.pbData), 0 },
+    };
+    BOOL ret;
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, info ? info->pszObjId : NULL);
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodePKCSContentInfo(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = FALSE;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
+
+    __TRY
+    {
+        ret = CRYPT_AsnDecodePKCSContentInfoInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pcbStructInfo, NULL);
+        if (ret && pvStructInfo)
+        {
+            ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
+             pcbStructInfo, *pcbStructInfo);
+            if (ret)
+            {
+                CRYPT_CONTENT_INFO *info;
+
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                info = pvStructInfo;
+                info->pszObjId = (LPSTR)((BYTE *)info +
+                 sizeof(CRYPT_CONTENT_INFO));
+                ret = CRYPT_AsnDecodePKCSContentInfoInternal(pbEncoded,
+                 cbEncoded, dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo,
+                 pcbStructInfo, NULL);
+            }
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+    }
+    __ENDTRY
+    return ret;
+}
+
+BOOL CRYPT_AsnDecodePKCSDigestedData(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
+ CRYPT_DIGESTED_DATA *digestedData, DWORD *pcbDigestedData)
 {
     BOOL ret;
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_INTEGER, offsetof(CRYPT_DIGESTED_DATA, version),
+       CRYPT_AsnDecodeIntInternal, sizeof(DWORD), FALSE, FALSE, 0, 0 },
+     { ASN_SEQUENCEOF, offsetof(CRYPT_DIGESTED_DATA, DigestAlgorithm),
+       CRYPT_AsnDecodeAlgorithmId, sizeof(CRYPT_ALGORITHM_IDENTIFIER),
+       FALSE, TRUE, offsetof(CRYPT_DIGESTED_DATA, DigestAlgorithm.pszObjId),
+       0 },
+     { ASN_SEQUENCEOF, offsetof(CRYPT_DIGESTED_DATA, ContentInfo),
+       CRYPT_AsnDecodePKCSContentInfoInternal,
+       sizeof(CRYPT_CONTENT_INFO), FALSE, TRUE, offsetof(CRYPT_DIGESTED_DATA,
+       ContentInfo.pszObjId), 0 },
+     { ASN_OCTETSTRING, offsetof(CRYPT_DIGESTED_DATA, hash),
+       CRYPT_AsnDecodeOctetsInternal, sizeof(CRYPT_HASH_BLOB), FALSE, TRUE,
+       offsetof(CRYPT_DIGESTED_DATA, hash.pbData), 0 },
+    };
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, pDecodePara, digestedData, pcbDigestedData,
+     NULL, NULL);
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeAltName(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = TRUE;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
 
     __TRY
     {
         DWORD bytesNeeded;
 
-        if (pbEncoded[0] != ASN_INTEGER)
-        {
-            SetLastError(CRYPT_E_ASN1_BADTAG);
-            ret = FALSE;
-        }
-        else
-            ret = CRYPT_AsnDecodeIntegerInternal(dwCertEncodingType,
-             lpszStructType, pbEncoded, cbEncoded,
-             dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, NULL, &bytesNeeded);
-        if (ret)
+        if ((ret = CRYPT_AsnDecodeAltNameInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, &bytesNeeded, NULL)))
         {
             if (!pvStructInfo)
                 *pcbStructInfo = bytesNeeded;
             else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
              pvStructInfo, pcbStructInfo, bytesNeeded)))
             {
-                CRYPT_INTEGER_BLOB *blob;
+                CERT_ALT_NAME_INFO *name;
 
                 if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
                     pvStructInfo = *(BYTE **)pvStructInfo;
-                blob = (CRYPT_INTEGER_BLOB *)pvStructInfo;
-                blob->pbData = (BYTE *)pvStructInfo +
-                 sizeof(CRYPT_INTEGER_BLOB);
-                ret = CRYPT_AsnDecodeIntegerInternal(dwCertEncodingType,
-                 lpszStructType, pbEncoded, cbEncoded,
-                 dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, pvStructInfo,
-                 &bytesNeeded);
+                name = pvStructInfo;
+                name->rgAltEntry = (PCERT_ALT_NAME_ENTRY)
+                 ((BYTE *)pvStructInfo + sizeof(CERT_ALT_NAME_INFO));
+                ret = CRYPT_AsnDecodeAltNameInternal(pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo,
+                 &bytesNeeded, NULL);
             }
         }
     }
@@ -2689,64 +3451,73 @@ static BOOL WINAPI CRYPT_AsnDecodeInteger(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeUnsignedIntegerInternal(
- DWORD dwCertEncodingType, LPCSTR lpszStructType, const BYTE *pbEncoded,
- DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
- void *pvStructInfo, DWORD *pcbStructInfo)
+struct PATH_LEN_CONSTRAINT
 {
-    BOOL ret;
+    BOOL  fPathLenConstraint;
+    DWORD dwPathLenConstraint;
+};
 
-    if (pbEncoded[0] == ASN_INTEGER)
-    {
-        DWORD bytesNeeded, dataLen;
+static BOOL CRYPT_AsnDecodePathLenConstraint(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret = TRUE;
+    DWORD bytesNeeded = sizeof(struct PATH_LEN_CONSTRAINT), size;
 
-        if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
-        {
-            BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
-
-            bytesNeeded = dataLen + sizeof(CRYPT_INTEGER_BLOB);
-            if (!pvStructInfo)
-                *pcbStructInfo = bytesNeeded;
-            else if (*pcbStructInfo < bytesNeeded)
-            {
-                *pcbStructInfo = bytesNeeded;
-                SetLastError(ERROR_MORE_DATA);
-                ret = FALSE;
-            }
-            else
-            {
-                CRYPT_INTEGER_BLOB *blob = (CRYPT_INTEGER_BLOB *)pvStructInfo;
-
-                blob->cbData = dataLen;
-                assert(blob->pbData);
-                /* remove leading zero byte if it exists */
-                if (blob->cbData && *(pbEncoded + 1 + lenBytes) == 0)
-                {
-                    blob->cbData--;
-                    blob->pbData++;
-                }
-                if (blob->cbData)
-                {
-                    DWORD i;
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
 
-                    for (i = 0; i < blob->cbData; i++)
-                    {
-                        blob->pbData[i] = *(pbEncoded + 1 + lenBytes +
-                         dataLen - i - 1);
-                    }
-                }
-            }
-        }
+    if (!pvStructInfo)
+    {
+        ret = CRYPT_AsnDecodeIntInternal(pbEncoded, cbEncoded, dwFlags, NULL,
+         &size, pcbDecoded);
+        *pcbStructInfo = bytesNeeded;
     }
-    else
+    else if (*pcbStructInfo < bytesNeeded)
     {
-        SetLastError(CRYPT_E_ASN1_BADTAG);
+        SetLastError(ERROR_MORE_DATA);
+        *pcbStructInfo = bytesNeeded;
         ret = FALSE;
     }
+    else
+    {
+        struct PATH_LEN_CONSTRAINT *constraint = pvStructInfo;
+
+        *pcbStructInfo = bytesNeeded;
+        size = sizeof(constraint->dwPathLenConstraint);
+        ret = CRYPT_AsnDecodeIntInternal(pbEncoded, cbEncoded, dwFlags,
+         &constraint->dwPathLenConstraint, &size, pcbDecoded);
+        if (ret)
+            constraint->fPathLenConstraint = TRUE;
+        TRACE("got an int, dwPathLenConstraint is %d\n",
+         constraint->dwPathLenConstraint);
+    }
+    TRACE("returning %d (%08x)\n", ret, GetLastError());
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeUnsignedInteger(DWORD dwCertEncodingType,
+static BOOL CRYPT_AsnDecodeSubtreeConstraints(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+     offsetof(CERT_BASIC_CONSTRAINTS_INFO, cSubtreesConstraint),
+     offsetof(CERT_BASIC_CONSTRAINTS_INFO, rgSubtreesConstraint),
+     FINALMEMBERSIZE(CERT_BASIC_CONSTRAINTS_INFO, cSubtreesConstraint),
+     CRYPT_AsnDecodeCopyBytes, sizeof(CERT_NAME_BLOB), TRUE,
+     offsetof(CERT_NAME_BLOB, pbData) };
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    TRACE("Returning %d (%08x)\n", ret, GetLastError());
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeBasicConstraints(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
@@ -2754,30 +3525,23 @@ static BOOL WINAPI CRYPT_AsnDecodeUnsignedInteger(DWORD dwCertEncodingType,
 
     __TRY
     {
-        DWORD bytesNeeded;
-
-        if ((ret = CRYPT_AsnDecodeUnsignedIntegerInternal(dwCertEncodingType,
-         lpszStructType, pbEncoded, cbEncoded,
-         dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, NULL, &bytesNeeded)))
-        {
-            if (!pvStructInfo)
-                *pcbStructInfo = bytesNeeded;
-            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
-             pvStructInfo, pcbStructInfo, bytesNeeded)))
-            {
-                CRYPT_INTEGER_BLOB *blob;
+        struct AsnDecodeSequenceItem items[] = {
+         { ASN_BITSTRING, offsetof(CERT_BASIC_CONSTRAINTS_INFO, SubjectType),
+           CRYPT_AsnDecodeBitsInternal, sizeof(CRYPT_BIT_BLOB), FALSE, TRUE, 
+           offsetof(CERT_BASIC_CONSTRAINTS_INFO, SubjectType.pbData), 0 },
+         { ASN_INTEGER, offsetof(CERT_BASIC_CONSTRAINTS_INFO,
+           fPathLenConstraint), CRYPT_AsnDecodePathLenConstraint,
+           sizeof(struct PATH_LEN_CONSTRAINT), TRUE, FALSE, 0, 0 },
+         { ASN_SEQUENCEOF, offsetof(CERT_BASIC_CONSTRAINTS_INFO,
+           cSubtreesConstraint), CRYPT_AsnDecodeSubtreeConstraints,
+           FINALMEMBERSIZE(CERT_BASIC_CONSTRAINTS_INFO, cSubtreesConstraint),
+           TRUE, TRUE,
+           offsetof(CERT_BASIC_CONSTRAINTS_INFO, rgSubtreesConstraint), 0 },
+        };
 
-                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
-                    pvStructInfo = *(BYTE **)pvStructInfo;
-                blob = (CRYPT_INTEGER_BLOB *)pvStructInfo;
-                blob->pbData = (BYTE *)pvStructInfo +
-                 sizeof(CRYPT_INTEGER_BLOB);
-                ret = CRYPT_AsnDecodeUnsignedIntegerInternal(dwCertEncodingType,
-                 lpszStructType, pbEncoded, cbEncoded,
-                 dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, pvStructInfo,
-                 &bytesNeeded);
-            }
-        }
+        ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+         pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo,
+         pcbStructInfo, NULL, NULL);
     }
     __EXCEPT_PAGE_FAULT
     {
@@ -2788,64 +3552,25 @@ static BOOL WINAPI CRYPT_AsnDecodeUnsignedInteger(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeEnumerated(DWORD dwCertEncodingType,
+static BOOL WINAPI CRYPT_AsnDecodeBasicConstraints2(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
     BOOL ret;
 
-    if (!pvStructInfo)
-    {
-        *pcbStructInfo = sizeof(int);
-        return TRUE;
-    }
     __TRY
     {
-        if (pbEncoded[0] == ASN_ENUMERATED)
-        {
-            unsigned int val = 0, i;
+        struct AsnDecodeSequenceItem items[] = {
+         { ASN_BOOL, offsetof(CERT_BASIC_CONSTRAINTS2_INFO, fCA),
+           CRYPT_AsnDecodeBool, sizeof(BOOL), TRUE, FALSE, 0, 0 },
+         { ASN_INTEGER, offsetof(CERT_BASIC_CONSTRAINTS2_INFO,
+           fPathLenConstraint), CRYPT_AsnDecodePathLenConstraint,
+           sizeof(struct PATH_LEN_CONSTRAINT), TRUE, FALSE, 0, 0 },
+        };
 
-            if (cbEncoded <= 1)
-            {
-                SetLastError(CRYPT_E_ASN1_EOD);
-                ret = FALSE;
-            }
-            else if (pbEncoded[1] == 0)
-            {
-                SetLastError(CRYPT_E_ASN1_CORRUPT);
-                ret = FALSE;
-            }
-            else
-            {
-                /* A little strange looking, but we have to accept a sign byte:
-                 * 0xffffffff gets encoded as 0a 05 00 ff ff ff ff.  Also,
-                 * assuming a small length is okay here, it has to be in short
-                 * form.
-                 */
-                if (pbEncoded[1] > sizeof(unsigned int) + 1)
-                {
-                    SetLastError(CRYPT_E_ASN1_LARGE);
-                    return FALSE;
-                }
-                for (i = 0; i < pbEncoded[1]; i++)
-                {
-                    val <<= 8;
-                    val |= pbEncoded[2 + i];
-                }
-                if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
-                 pvStructInfo, pcbStructInfo, sizeof(unsigned int))))
-                {
-                    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
-                        pvStructInfo = *(BYTE **)pvStructInfo;
-                    memcpy(pvStructInfo, &val, sizeof(unsigned int));
-                }
-            }
-        }
-        else
-        {
-            SetLastError(CRYPT_E_ASN1_BADTAG);
-            ret = FALSE;
-        }
+        ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+         pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo,
+         pcbStructInfo, NULL, NULL);
     }
     __EXCEPT_PAGE_FAULT
     {
@@ -2856,728 +3581,2179 @@ static BOOL WINAPI CRYPT_AsnDecodeEnumerated(DWORD dwCertEncodingType,
     return ret;
 }
 
-/* Modifies word, pbEncoded, and len, and magically sets a value ret to FALSE
- * if it fails.
- */
-#define CRYPT_TIME_GET_DIGITS(pbEncoded, len, numDigits, word) \
- do { \
-    BYTE i; \
- \
-    (word) = 0; \
-    for (i = 0; (len) > 0 && i < (numDigits); i++, (len)--) \
-    { \
-        if (!isdigit(*(pbEncoded))) \
-        { \
-            SetLastError(CRYPT_E_ASN1_CORRUPT); \
-            ret = FALSE; \
-        } \
-        else \
-        { \
-            (word) *= 10; \
-            (word) += *(pbEncoded)++ - '0'; \
-        } \
-    } \
- } while (0)
+static BOOL CRYPT_AsnDecodePolicyQualifier(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_OBJECTIDENTIFIER, offsetof(CERT_POLICY_QUALIFIER_INFO,
+       pszPolicyQualifierId), CRYPT_AsnDecodeOidInternal, sizeof(LPSTR),
+       FALSE, TRUE, offsetof(CERT_POLICY_QUALIFIER_INFO, pszPolicyQualifierId),
+       0 },
+     { 0, offsetof(CERT_POLICY_QUALIFIER_INFO, Qualifier),
+       CRYPT_AsnDecodeDerBlob, sizeof(CRYPT_OBJID_BLOB), TRUE, TRUE,
+       offsetof(CERT_POLICY_QUALIFIER_INFO, Qualifier.pbData), 0 },
+    };
+    BOOL ret;
+    CERT_POLICY_QUALIFIER_INFO *qualifier = pvStructInfo;
 
-static BOOL CRYPT_AsnDecodeTimeZone(const BYTE *pbEncoded, DWORD len,
- SYSTEMTIME *sysTime)
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, pvStructInfo ? *pcbStructInfo : 0);
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, qualifier ? qualifier->pszPolicyQualifierId : NULL);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodePolicyQualifiers(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+     offsetof(CERT_POLICY_INFO, cPolicyQualifier),
+     offsetof(CERT_POLICY_INFO, rgPolicyQualifier),
+     FINALMEMBERSIZE(CERT_POLICY_INFO, cPolicyQualifier),
+     CRYPT_AsnDecodePolicyQualifier, sizeof(CERT_POLICY_QUALIFIER_INFO), TRUE,
+     offsetof(CERT_POLICY_QUALIFIER_INFO, pszPolicyQualifierId) };
+
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, pvStructInfo ? *pcbStructInfo : 0);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    TRACE("Returning %d (%08x)\n", ret, GetLastError());
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeCertPolicy(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_OBJECTIDENTIFIER, offsetof(CERT_POLICY_INFO, pszPolicyIdentifier),
+       CRYPT_AsnDecodeOidInternal, sizeof(LPSTR), FALSE, TRUE,
+       offsetof(CERT_POLICY_INFO, pszPolicyIdentifier), 0 },
+     { ASN_SEQUENCEOF, offsetof(CERT_POLICY_INFO, cPolicyQualifier),
+       CRYPT_AsnDecodePolicyQualifiers,
+       FINALMEMBERSIZE(CERT_POLICY_INFO, cPolicyQualifier), TRUE,
+       TRUE, offsetof(CERT_POLICY_INFO, rgPolicyQualifier), 0 },
+    };
+    CERT_POLICY_INFO *info = pvStructInfo;
     BOOL ret;
 
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, pvStructInfo ? *pcbStructInfo : 0);
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, info ? info->pszPolicyIdentifier : NULL);
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeCertPolicies(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = FALSE;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, pvStructInfo ? *pcbStructInfo : 0);
+
     __TRY
     {
-        ret = TRUE;
-        if (len >= 3 && (*pbEncoded == '+' || *pbEncoded == '-'))
-        {
-            WORD hours, minutes = 0;
-            BYTE sign = *pbEncoded++;
+        struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+         offsetof(CERT_POLICIES_INFO, cPolicyInfo),
+         offsetof(CERT_POLICIES_INFO, rgPolicyInfo),
+         sizeof(CERT_POLICIES_INFO),
+         CRYPT_AsnDecodeCertPolicy, sizeof(CERT_POLICY_INFO), TRUE,
+         offsetof(CERT_POLICY_INFO, pszPolicyIdentifier) };
 
-            len--;
-            CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, hours);
-            if (ret && hours >= 24)
-            {
-                SetLastError(CRYPT_E_ASN1_CORRUPT);
-                ret = FALSE;
-            }
-            else if (len >= 2)
-            {
-                CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, minutes);
-                if (ret && minutes >= 60)
-                {
-                    SetLastError(CRYPT_E_ASN1_CORRUPT);
-                    ret = FALSE;
-                }
-            }
-            if (ret)
-            {
-                if (sign == '+')
-                {
-                    sysTime->wHour += hours;
-                    sysTime->wMinute += minutes;
-                }
-                else
-                {
-                    if (hours > sysTime->wHour)
-                    {
-                        sysTime->wDay--;
-                        sysTime->wHour = 24 - (hours - sysTime->wHour);
-                    }
-                    else
-                        sysTime->wHour -= hours;
-                    if (minutes > sysTime->wMinute)
-                    {
-                        sysTime->wHour--;
-                        sysTime->wMinute = 60 - (minutes - sysTime->wMinute);
-                    }
-                    else
-                        sysTime->wMinute -= minutes;
-                }
-            }
-        }
+        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+         dwFlags, pDecodePara, pvStructInfo, pcbStructInfo, NULL);
     }
     __EXCEPT_PAGE_FAULT
     {
         SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
     }
     __ENDTRY
     return ret;
 }
 
-#define MIN_ENCODED_TIME_LENGTH 10
+static BOOL CRYPT_AsnDecodeCertPolicyMapping(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_OBJECTIDENTIFIER, offsetof(CERT_POLICY_MAPPING,
+       pszIssuerDomainPolicy), CRYPT_AsnDecodeOidInternal, sizeof(LPSTR),
+       FALSE, TRUE, offsetof(CERT_POLICY_MAPPING, pszIssuerDomainPolicy), 0 },
+     { ASN_OBJECTIDENTIFIER, offsetof(CERT_POLICY_MAPPING,
+       pszSubjectDomainPolicy), CRYPT_AsnDecodeOidInternal, sizeof(LPSTR),
+       FALSE, TRUE, offsetof(CERT_POLICY_MAPPING, pszSubjectDomainPolicy), 0 },
+    };
+    CERT_POLICY_MAPPING *mapping = pvStructInfo;
+    BOOL ret;
 
-static BOOL WINAPI CRYPT_AsnDecodeUtcTime(DWORD dwCertEncodingType,
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, pvStructInfo ? *pcbStructInfo : 0);
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, mapping ? mapping->pszIssuerDomainPolicy : NULL);
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeCertPolicyMappings(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
-    BOOL ret;
+    BOOL ret = FALSE;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, pvStructInfo ? *pcbStructInfo : 0);
 
-    if (!pvStructInfo)
-    {
-        *pcbStructInfo = sizeof(FILETIME);
-        return TRUE;
-    }
     __TRY
     {
-        ret = TRUE;
-        if (pbEncoded[0] == ASN_UTCTIME)
-        {
-            if (cbEncoded <= 1)
+        struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+         offsetof(CERT_POLICY_MAPPINGS_INFO, cPolicyMapping),
+         offsetof(CERT_POLICY_MAPPINGS_INFO, rgPolicyMapping),
+         sizeof(CERT_POLICY_MAPPING),
+         CRYPT_AsnDecodeCertPolicyMapping, sizeof(CERT_POLICY_MAPPING), TRUE,
+         offsetof(CERT_POLICY_MAPPING, pszIssuerDomainPolicy) };
+
+        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+         dwFlags, pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeRequireExplicit(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    DWORD skip, size = sizeof(skip);
+
+    if (!cbEncoded)
+    {
+        SetLastError(CRYPT_E_ASN1_EOD);
+        return FALSE;
+    }
+    if (pbEncoded[0] != (ASN_CONTEXT | 0))
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        return FALSE;
+    }
+    if ((ret = CRYPT_AsnDecodeIntInternal(pbEncoded, cbEncoded, dwFlags,
+     &skip, &size, pcbDecoded)))
+    {
+        DWORD bytesNeeded = MEMBERSIZE(CERT_POLICY_CONSTRAINTS_INFO,
+         fRequireExplicitPolicy, fInhibitPolicyMapping);
+
+        if (!pvStructInfo)
+            *pcbStructInfo = bytesNeeded;
+        else if (*pcbStructInfo < bytesNeeded)
+        {
+            *pcbStructInfo = bytesNeeded;
+            SetLastError(ERROR_MORE_DATA);
+            ret = FALSE;
+        }
+        else
+        {
+            CERT_POLICY_CONSTRAINTS_INFO *info =
+             (CERT_POLICY_CONSTRAINTS_INFO *)((BYTE *)pvStructInfo -
+             offsetof(CERT_POLICY_CONSTRAINTS_INFO, fRequireExplicitPolicy));
+
+            *pcbStructInfo = bytesNeeded;
+            /* The BOOL is implicit:  if the integer is present, then it's
+             * TRUE.
+             */
+            info->fRequireExplicitPolicy = TRUE;
+            info->dwRequireExplicitPolicySkipCerts = skip;
+        }
+    }
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeInhibitMapping(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    DWORD skip, size = sizeof(skip);
+
+    if (!cbEncoded)
+    {
+        SetLastError(CRYPT_E_ASN1_EOD);
+        return FALSE;
+    }
+    if (pbEncoded[0] != (ASN_CONTEXT | 1))
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        return FALSE;
+    }
+    if ((ret = CRYPT_AsnDecodeIntInternal(pbEncoded, cbEncoded, dwFlags,
+     &skip, &size, pcbDecoded)))
+    {
+        DWORD bytesNeeded = FINALMEMBERSIZE(CERT_POLICY_CONSTRAINTS_INFO,
+         fInhibitPolicyMapping);
+
+        if (!pvStructInfo)
+            *pcbStructInfo = bytesNeeded;
+        else if (*pcbStructInfo < bytesNeeded)
+        {
+            *pcbStructInfo = bytesNeeded;
+            SetLastError(ERROR_MORE_DATA);
+            ret = FALSE;
+        }
+        else
+        {
+            CERT_POLICY_CONSTRAINTS_INFO *info =
+             (CERT_POLICY_CONSTRAINTS_INFO *)((BYTE *)pvStructInfo -
+             offsetof(CERT_POLICY_CONSTRAINTS_INFO, fInhibitPolicyMapping));
+
+            *pcbStructInfo = bytesNeeded;
+            /* The BOOL is implicit:  if the integer is present, then it's
+             * TRUE.
+             */
+            info->fInhibitPolicyMapping = TRUE;
+            info->dwInhibitPolicyMappingSkipCerts = skip;
+        }
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeCertPolicyConstraints(
+ DWORD dwCertEncodingType, LPCSTR lpszStructType, const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
+ void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = FALSE;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, pvStructInfo ? *pcbStructInfo : 0);
+
+    __TRY
+    {
+        struct AsnDecodeSequenceItem items[] = {
+         { ASN_CONTEXT | 0,
+           offsetof(CERT_POLICY_CONSTRAINTS_INFO, fRequireExplicitPolicy),
+           CRYPT_AsnDecodeRequireExplicit,
+           MEMBERSIZE(CERT_POLICY_CONSTRAINTS_INFO, fRequireExplicitPolicy,
+           fInhibitPolicyMapping), TRUE, FALSE, 0, 0 },
+         { ASN_CONTEXT | 1,
+           offsetof(CERT_POLICY_CONSTRAINTS_INFO, fInhibitPolicyMapping),
+           CRYPT_AsnDecodeInhibitMapping,
+           FINALMEMBERSIZE(CERT_POLICY_CONSTRAINTS_INFO, fInhibitPolicyMapping),
+           TRUE, FALSE, 0, 0 },
+        };
+
+        ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+         pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo,
+         pcbStructInfo, NULL, NULL);
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+    }
+    __ENDTRY
+    return ret;
+}
+
+#define RSA1_MAGIC 0x31415352
+
+struct DECODED_RSA_PUB_KEY
+{
+    DWORD              pubexp;
+    CRYPT_INTEGER_BLOB modulus;
+};
+
+static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    __TRY
+    {
+        struct AsnDecodeSequenceItem items[] = {
+         { ASN_INTEGER, offsetof(struct DECODED_RSA_PUB_KEY, modulus),
+           CRYPT_AsnDecodeUnsignedIntegerInternal, sizeof(CRYPT_INTEGER_BLOB),
+           FALSE, TRUE, offsetof(struct DECODED_RSA_PUB_KEY, modulus.pbData),
+           0 },
+         { ASN_INTEGER, offsetof(struct DECODED_RSA_PUB_KEY, pubexp),
+           CRYPT_AsnDecodeIntInternal, sizeof(DWORD), FALSE, FALSE, 0, 0 },
+        };
+        struct DECODED_RSA_PUB_KEY *decodedKey = NULL;
+        DWORD size = 0;
+
+        ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+         pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &decodedKey,
+         &size, NULL, NULL);
+        if (ret)
+        {
+            DWORD bytesNeeded = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) +
+             decodedKey->modulus.cbData;
+
+            if (!pvStructInfo)
+            {
+                *pcbStructInfo = bytesNeeded;
+                ret = TRUE;
+            }
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                BLOBHEADER *hdr;
+                RSAPUBKEY *rsaPubKey;
+
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                hdr = pvStructInfo;
+                hdr->bType = PUBLICKEYBLOB;
+                hdr->bVersion = CUR_BLOB_VERSION;
+                hdr->reserved = 0;
+                hdr->aiKeyAlg = CALG_RSA_KEYX;
+                rsaPubKey = (RSAPUBKEY *)((BYTE *)pvStructInfo +
+                 sizeof(BLOBHEADER));
+                rsaPubKey->magic = RSA1_MAGIC;
+                rsaPubKey->pubexp = decodedKey->pubexp;
+                rsaPubKey->bitlen = decodedKey->modulus.cbData * 8;
+                memcpy((BYTE *)pvStructInfo + sizeof(BLOBHEADER) +
+                 sizeof(RSAPUBKEY), decodedKey->modulus.pbData,
+                 decodedKey->modulus.cbData);
+            }
+            LocalFree(decodedKey);
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeOctetsInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    DWORD bytesNeeded, dataLen;
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+    {
+        BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+
+        if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+            bytesNeeded = sizeof(CRYPT_DATA_BLOB);
+        else
+            bytesNeeded = dataLen + sizeof(CRYPT_DATA_BLOB);
+        if (pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
+        if (!pvStructInfo)
+            *pcbStructInfo = bytesNeeded;
+        else if (*pcbStructInfo < bytesNeeded)
+        {
+            SetLastError(ERROR_MORE_DATA);
+            *pcbStructInfo = bytesNeeded;
+            ret = FALSE;
+        }
+        else
+        {
+            CRYPT_DATA_BLOB *blob;
+
+            *pcbStructInfo = bytesNeeded;
+            blob = pvStructInfo;
+            blob->cbData = dataLen;
+            if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+                blob->pbData = (BYTE *)pbEncoded + 1 + lenBytes;
+            else
+            {
+                assert(blob->pbData);
+                if (blob->cbData)
+                    memcpy(blob->pbData, pbEncoded + 1 + lenBytes,
+                     blob->cbData);
+            }
+        }
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeOctets(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
+
+    __TRY
+    {
+        DWORD bytesNeeded;
+
+        if (!cbEncoded)
+        {
+            SetLastError(CRYPT_E_ASN1_CORRUPT);
+            ret = FALSE;
+        }
+        else if (pbEncoded[0] != ASN_OCTETSTRING)
+        {
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+            ret = FALSE;
+        }
+        else if ((ret = CRYPT_AsnDecodeOctetsInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, &bytesNeeded, NULL)))
+        {
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                CRYPT_DATA_BLOB *blob;
+
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                blob = pvStructInfo;
+                blob->pbData = (BYTE *)pvStructInfo + sizeof(CRYPT_DATA_BLOB);
+                ret = CRYPT_AsnDecodeOctetsInternal(pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo,
+                 &bytesNeeded, NULL);
+            }
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeBitsInternal(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
+{
+    BOOL ret;
+    DWORD bytesNeeded, dataLen;
+    BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+
+    TRACE("(%p, %d, 0x%08x, %p, %d, %p)\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+    {
+        if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+            bytesNeeded = sizeof(CRYPT_BIT_BLOB);
+        else
+            bytesNeeded = dataLen - 1 + sizeof(CRYPT_BIT_BLOB);
+        if (pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
+        if (!pvStructInfo)
+            *pcbStructInfo = bytesNeeded;
+        else if (*pcbStructInfo < bytesNeeded)
+        {
+            *pcbStructInfo = bytesNeeded;
+            SetLastError(ERROR_MORE_DATA);
+            ret = FALSE;
+        }
+        else
+        {
+            CRYPT_BIT_BLOB *blob;
+
+            *pcbStructInfo = bytesNeeded;
+            blob = pvStructInfo;
+            blob->cbData = dataLen - 1;
+            blob->cUnusedBits = *(pbEncoded + 1 + lenBytes);
+            if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+            {
+                blob->pbData = (BYTE *)pbEncoded + 2 + lenBytes;
+            }
+            else
+            {
+                assert(blob->pbData);
+                if (blob->cbData)
+                {
+                    BYTE mask = 0xff << blob->cUnusedBits;
+
+                    memcpy(blob->pbData, pbEncoded + 2 + lenBytes,
+                     blob->cbData);
+                    blob->pbData[blob->cbData - 1] &= mask;
+                }
+            }
+        }
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeBits(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    TRACE("(%p, %d, 0x%08x, %p, %p, %p)\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, pcbStructInfo);
+
+    __TRY
+    {
+        DWORD bytesNeeded;
+
+        if (!cbEncoded)
+        {
+            SetLastError(CRYPT_E_ASN1_CORRUPT);
+            ret = FALSE;
+        }
+        else if (pbEncoded[0] != ASN_BITSTRING)
+        {
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+            ret = FALSE;
+        }
+        else if ((ret = CRYPT_AsnDecodeBitsInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, &bytesNeeded, NULL)))
+        {
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                CRYPT_BIT_BLOB *blob;
+
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                blob = pvStructInfo;
+                blob->pbData = (BYTE *)pvStructInfo + sizeof(CRYPT_BIT_BLOB);
+                ret = CRYPT_AsnDecodeBitsInternal(pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo,
+                 &bytesNeeded, NULL);
+            }
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    TRACE("returning %d (%08x)\n", ret, GetLastError());
+    return ret;
+}
+
+/* Ignores tag.  Only allows integers 4 bytes or smaller in size. */
+static BOOL CRYPT_AsnDecodeIntInternal(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
+{
+    BOOL ret;
+    DWORD dataLen;
+
+    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+    {
+        BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+
+        if (pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
+        if (dataLen > sizeof(int))
+        {
+            SetLastError(CRYPT_E_ASN1_LARGE);
+            ret = FALSE;
+        }
+        else if (!pvStructInfo)
+            *pcbStructInfo = sizeof(int);
+        else if ((ret = CRYPT_DecodeCheckSpace(pcbStructInfo, sizeof(int))))
+        {
+            int val, i;
+
+            if (dataLen && pbEncoded[1 + lenBytes] & 0x80)
+            {
+                /* initialize to a negative value to sign-extend */
+                val = -1;
+            }
+            else
+                val = 0;
+            for (i = 0; i < dataLen; i++)
+            {
+                val <<= 8;
+                val |= pbEncoded[1 + lenBytes + i];
+            }
+            memcpy(pvStructInfo, &val, sizeof(int));
+        }
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    __TRY
+    {
+        DWORD bytesNeeded;
+
+        if (!cbEncoded)
+        {
+            SetLastError(CRYPT_E_ASN1_EOD);
+            ret = FALSE;
+        }
+        else if (pbEncoded[0] != ASN_INTEGER)
+        {
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+            ret = FALSE;
+        }
+        else
+            ret = CRYPT_AsnDecodeIntInternal(pbEncoded, cbEncoded,
+             dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, &bytesNeeded, NULL);
+        if (ret)
+        {
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                ret = CRYPT_AsnDecodeIntInternal(pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo,
+                 &bytesNeeded, NULL);
+            }
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeIntegerInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    DWORD bytesNeeded, dataLen;
+
+    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+    {
+        BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+
+        bytesNeeded = dataLen + sizeof(CRYPT_INTEGER_BLOB);
+        if (pcbDecoded)
+            *pcbDecoded = 1 + lenBytes + dataLen;
+        if (!pvStructInfo)
+            *pcbStructInfo = bytesNeeded;
+        else if (*pcbStructInfo < bytesNeeded)
+        {
+            *pcbStructInfo = bytesNeeded;
+            SetLastError(ERROR_MORE_DATA);
+            ret = FALSE;
+        }
+        else
+        {
+            CRYPT_INTEGER_BLOB *blob = pvStructInfo;
+
+            *pcbStructInfo = bytesNeeded;
+            blob->cbData = dataLen;
+            assert(blob->pbData);
+            if (blob->cbData)
+            {
+                DWORD i;
+
+                for (i = 0; i < blob->cbData; i++)
+                {
+                    blob->pbData[i] = *(pbEncoded + 1 + lenBytes +
+                     dataLen - i - 1);
+                }
+            }
+        }
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeInteger(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    __TRY
+    {
+        DWORD bytesNeeded;
+
+        if (pbEncoded[0] != ASN_INTEGER)
+        {
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+            ret = FALSE;
+        }
+        else
+            ret = CRYPT_AsnDecodeIntegerInternal(pbEncoded, cbEncoded,
+             dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, &bytesNeeded, NULL);
+        if (ret)
+        {
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                CRYPT_INTEGER_BLOB *blob;
+
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                blob = pvStructInfo;
+                blob->pbData = (BYTE *)pvStructInfo +
+                 sizeof(CRYPT_INTEGER_BLOB);
+                ret = CRYPT_AsnDecodeIntegerInternal(pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, pvStructInfo,
+                 &bytesNeeded, NULL);
+            }
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeUnsignedIntegerInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+
+    if (pbEncoded[0] == ASN_INTEGER)
+    {
+        DWORD bytesNeeded, dataLen;
+
+        if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+        {
+            BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+
+            if (pcbDecoded)
+                *pcbDecoded = 1 + lenBytes + dataLen;
+            bytesNeeded = dataLen + sizeof(CRYPT_INTEGER_BLOB);
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if (*pcbStructInfo < bytesNeeded)
+            {
+                *pcbStructInfo = bytesNeeded;
+                SetLastError(ERROR_MORE_DATA);
+                ret = FALSE;
+            }
+            else
+            {
+                CRYPT_INTEGER_BLOB *blob = pvStructInfo;
+
+                *pcbStructInfo = bytesNeeded;
+                blob->cbData = dataLen;
+                assert(blob->pbData);
+                /* remove leading zero byte if it exists */
+                if (blob->cbData && *(pbEncoded + 1 + lenBytes) == 0)
+                {
+                    blob->cbData--;
+                    blob->pbData++;
+                }
+                if (blob->cbData)
+                {
+                    DWORD i;
+
+                    for (i = 0; i < blob->cbData; i++)
+                    {
+                        blob->pbData[i] = *(pbEncoded + 1 + lenBytes +
+                         dataLen - i - 1);
+                    }
+                }
+            }
+        }
+    }
+    else
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        ret = FALSE;
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeUnsignedInteger(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    __TRY
+    {
+        DWORD bytesNeeded;
+
+        if ((ret = CRYPT_AsnDecodeUnsignedIntegerInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, NULL, &bytesNeeded, NULL)))
+        {
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                CRYPT_INTEGER_BLOB *blob;
+
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                blob = pvStructInfo;
+                blob->pbData = (BYTE *)pvStructInfo +
+                 sizeof(CRYPT_INTEGER_BLOB);
+                ret = CRYPT_AsnDecodeUnsignedIntegerInternal(pbEncoded,
+                 cbEncoded, dwFlags & ~CRYPT_ENCODE_ALLOC_FLAG, pvStructInfo,
+                 &bytesNeeded, NULL);
+            }
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeEnumerated(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    if (!pvStructInfo)
+    {
+        *pcbStructInfo = sizeof(int);
+        return TRUE;
+    }
+    __TRY
+    {
+        if (pbEncoded[0] == ASN_ENUMERATED)
+        {
+            unsigned int val = 0, i;
+
+            if (cbEncoded <= 1)
+            {
+                SetLastError(CRYPT_E_ASN1_EOD);
+                ret = FALSE;
+            }
+            else if (pbEncoded[1] == 0)
+            {
+                SetLastError(CRYPT_E_ASN1_CORRUPT);
+                ret = FALSE;
+            }
+            else
+            {
+                /* A little strange looking, but we have to accept a sign byte:
+                 * 0xffffffff gets encoded as 0a 05 00 ff ff ff ff.  Also,
+                 * assuming a small length is okay here, it has to be in short
+                 * form.
+                 */
+                if (pbEncoded[1] > sizeof(unsigned int) + 1)
+                {
+                    SetLastError(CRYPT_E_ASN1_LARGE);
+                    return FALSE;
+                }
+                for (i = 0; i < pbEncoded[1]; i++)
+                {
+                    val <<= 8;
+                    val |= pbEncoded[2 + i];
+                }
+                if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+                 pvStructInfo, pcbStructInfo, sizeof(unsigned int))))
+                {
+                    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                        pvStructInfo = *(BYTE **)pvStructInfo;
+                    memcpy(pvStructInfo, &val, sizeof(unsigned int));
+                }
+            }
+        }
+        else
+        {
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+            ret = FALSE;
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+/* Modifies word, pbEncoded, and len, and magically sets a value ret to FALSE
+ * if it fails.
+ */
+#define CRYPT_TIME_GET_DIGITS(pbEncoded, len, numDigits, word) \
+ do { \
+    BYTE i; \
+ \
+    (word) = 0; \
+    for (i = 0; (len) > 0 && i < (numDigits); i++, (len)--) \
+    { \
+        if (!isdigit(*(pbEncoded))) \
+        { \
+            SetLastError(CRYPT_E_ASN1_CORRUPT); \
+            ret = FALSE; \
+        } \
+        else \
+        { \
+            (word) *= 10; \
+            (word) += *(pbEncoded)++ - '0'; \
+        } \
+    } \
+ } while (0)
+
+static BOOL CRYPT_AsnDecodeTimeZone(const BYTE *pbEncoded, DWORD len,
+ SYSTEMTIME *sysTime)
+{
+    BOOL ret = TRUE;
+
+    if (len >= 3 && (*pbEncoded == '+' || *pbEncoded == '-'))
+    {
+        WORD hours, minutes = 0;
+        BYTE sign = *pbEncoded++;
+
+        len--;
+        CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, hours);
+        if (ret && hours >= 24)
+        {
+            SetLastError(CRYPT_E_ASN1_CORRUPT);
+            ret = FALSE;
+        }
+        else if (len >= 2)
+        {
+            CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, minutes);
+            if (ret && minutes >= 60)
+            {
+                SetLastError(CRYPT_E_ASN1_CORRUPT);
+                ret = FALSE;
+            }
+        }
+        if (ret)
+        {
+            if (sign == '+')
+            {
+                sysTime->wHour += hours;
+                sysTime->wMinute += minutes;
+            }
+            else
+            {
+                if (hours > sysTime->wHour)
+                {
+                    sysTime->wDay--;
+                    sysTime->wHour = 24 - (hours - sysTime->wHour);
+                }
+                else
+                    sysTime->wHour -= hours;
+                if (minutes > sysTime->wMinute)
+                {
+                    sysTime->wHour--;
+                    sysTime->wMinute = 60 - (minutes - sysTime->wMinute);
+                }
+                else
+                    sysTime->wMinute -= minutes;
+            }
+        }
+    }
+    return ret;
+}
+
+#define MIN_ENCODED_TIME_LENGTH 10
+
+static BOOL CRYPT_AsnDecodeUtcTimeInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret = FALSE;
+
+    if (pbEncoded[0] == ASN_UTCTIME)
+    {
+        if (cbEncoded <= 1)
+            SetLastError(CRYPT_E_ASN1_EOD);
+        else if (pbEncoded[1] > 0x7f)
+        {
+            /* long-form date strings really can't be valid */
+            SetLastError(CRYPT_E_ASN1_CORRUPT);
+        }
+        else
+        {
+            SYSTEMTIME sysTime = { 0 };
+            BYTE len = pbEncoded[1];
+
+            if (len < MIN_ENCODED_TIME_LENGTH)
+                SetLastError(CRYPT_E_ASN1_CORRUPT);
+            else
+            {
+                ret = TRUE;
+                if (pcbDecoded)
+                    *pcbDecoded = 2 + len;
+                pbEncoded += 2;
+                CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wYear);
+                if (sysTime.wYear >= 50)
+                    sysTime.wYear += 1900;
+                else
+                    sysTime.wYear += 2000;
+                CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMonth);
+                CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wDay);
+                CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wHour);
+                CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMinute);
+                if (ret && len > 0)
+                {
+                    if (len >= 2 && isdigit(*pbEncoded) &&
+                     isdigit(*(pbEncoded + 1)))
+                        CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2,
+                         sysTime.wSecond);
+                    else if (isdigit(*pbEncoded))
+                        CRYPT_TIME_GET_DIGITS(pbEncoded, len, 1,
+                         sysTime.wSecond);
+                    if (ret)
+                        ret = CRYPT_AsnDecodeTimeZone(pbEncoded, len,
+                         &sysTime);
+                }
+                if (ret)
+                {
+                    if (!pvStructInfo)
+                        *pcbStructInfo = sizeof(FILETIME);
+                    else if ((ret = CRYPT_DecodeCheckSpace(pcbStructInfo,
+                     sizeof(FILETIME))))
+                        ret = SystemTimeToFileTime(&sysTime, pvStructInfo);
+                }
+            }
+        }
+    }
+    else
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeUtcTime(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = FALSE;
+
+    __TRY
+    {
+        DWORD bytesNeeded;
+
+        ret = CRYPT_AsnDecodeUtcTimeInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, &bytesNeeded, NULL);
+        if (ret)
+        {
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags,
+             pDecodePara, pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                ret = CRYPT_AsnDecodeUtcTimeInternal(pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo,
+                 &bytesNeeded, NULL);
+            }
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeGeneralizedTime(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret = FALSE;
+
+    if (pbEncoded[0] == ASN_GENERALTIME)
+    {
+        if (cbEncoded <= 1)
+            SetLastError(CRYPT_E_ASN1_EOD);
+        else if (pbEncoded[1] > 0x7f)
+        {
+            /* long-form date strings really can't be valid */
+            SetLastError(CRYPT_E_ASN1_CORRUPT);
+        }
+        else
+        {
+            BYTE len = pbEncoded[1];
+
+            if (len < MIN_ENCODED_TIME_LENGTH)
+                SetLastError(CRYPT_E_ASN1_CORRUPT);
+            else
+            {
+                SYSTEMTIME sysTime = { 0 };
+
+                ret = TRUE;
+                if (pcbDecoded)
+                    *pcbDecoded = 2 + len;
+                pbEncoded += 2;
+                CRYPT_TIME_GET_DIGITS(pbEncoded, len, 4, sysTime.wYear);
+                CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMonth);
+                CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wDay);
+                CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wHour);
+                if (ret && len > 0)
+                {
+                    CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2,
+                     sysTime.wMinute);
+                    if (ret && len > 0)
+                        CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2,
+                         sysTime.wSecond);
+                    if (ret && len > 0 && (*pbEncoded == '.' ||
+                     *pbEncoded == ','))
+                    {
+                        BYTE digits;
+
+                        pbEncoded++;
+                        len--;
+                        /* workaround macro weirdness */
+                        digits = min(len, 3);
+                        CRYPT_TIME_GET_DIGITS(pbEncoded, len, digits,
+                         sysTime.wMilliseconds);
+                    }
+                    if (ret)
+                        ret = CRYPT_AsnDecodeTimeZone(pbEncoded, len,
+                         &sysTime);
+                }
+                if (ret)
+                {
+                    if (!pvStructInfo)
+                        *pcbStructInfo = sizeof(FILETIME);
+                    else if ((ret = CRYPT_DecodeCheckSpace(pcbStructInfo,
+                     sizeof(FILETIME))))
+                        ret = SystemTimeToFileTime(&sysTime, pvStructInfo);
+                }
+            }
+        }
+    }
+    else
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeChoiceOfTimeInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    InternalDecodeFunc decode = NULL;
+
+    if (pbEncoded[0] == ASN_UTCTIME)
+        decode = CRYPT_AsnDecodeUtcTimeInternal;
+    else if (pbEncoded[0] == ASN_GENERALTIME)
+        decode = CRYPT_AsnDecodeGeneralizedTime;
+    if (decode)
+        ret = decode(pbEncoded, cbEncoded, dwFlags, pvStructInfo,
+         pcbStructInfo, pcbDecoded);
+    else
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        ret = FALSE;
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeChoiceOfTime(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    __TRY
+    {
+        DWORD bytesNeeded;
+
+        ret = CRYPT_AsnDecodeChoiceOfTimeInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, &bytesNeeded, NULL);
+        if (ret)
+        {
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                ret = CRYPT_AsnDecodeChoiceOfTimeInternal(pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo,
+                 &bytesNeeded, NULL);
+            }
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeSequenceOfAny(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = TRUE;
+
+    __TRY
+    {
+        if (pbEncoded[0] == ASN_SEQUENCEOF)
+        {
+            DWORD bytesNeeded, dataLen, remainingLen, cValue;
+
+            if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+            {
+                BYTE lenBytes;
+                const BYTE *ptr;
+
+                lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+                bytesNeeded = sizeof(CRYPT_SEQUENCE_OF_ANY);
+                cValue = 0;
+                ptr = pbEncoded + 1 + lenBytes;
+                remainingLen = dataLen;
+                while (ret && remainingLen)
+                {
+                    DWORD nextLen;
+
+                    ret = CRYPT_GetLen(ptr, remainingLen, &nextLen);
+                    if (ret)
+                    {
+                        DWORD nextLenBytes = GET_LEN_BYTES(ptr[1]);
+
+                        remainingLen -= 1 + nextLenBytes + nextLen;
+                        ptr += 1 + nextLenBytes + nextLen;
+                        bytesNeeded += sizeof(CRYPT_DER_BLOB);
+                        if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG))
+                            bytesNeeded += 1 + nextLenBytes + nextLen;
+                        cValue++;
+                    }
+                }
+                if (ret)
+                {
+                    CRYPT_SEQUENCE_OF_ANY *seq;
+                    BYTE *nextPtr;
+                    DWORD i;
+
+                    if (!pvStructInfo)
+                        *pcbStructInfo = bytesNeeded;
+                    else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+                     pvStructInfo, pcbStructInfo, bytesNeeded)))
+                    {
+                        if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                            pvStructInfo = *(BYTE **)pvStructInfo;
+                        seq = pvStructInfo;
+                        seq->cValue = cValue;
+                        seq->rgValue = (CRYPT_DER_BLOB *)((BYTE *)seq +
+                         sizeof(*seq));
+                        nextPtr = (BYTE *)seq->rgValue +
+                         cValue * sizeof(CRYPT_DER_BLOB);
+                        ptr = pbEncoded + 1 + lenBytes;
+                        remainingLen = dataLen;
+                        i = 0;
+                        while (ret && remainingLen)
+                        {
+                            DWORD nextLen;
+
+                            ret = CRYPT_GetLen(ptr, remainingLen, &nextLen);
+                            if (ret)
+                            {
+                                DWORD nextLenBytes = GET_LEN_BYTES(ptr[1]);
+
+                                seq->rgValue[i].cbData = 1 + nextLenBytes +
+                                 nextLen;
+                                if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+                                    seq->rgValue[i].pbData = (BYTE *)ptr;
+                                else
+                                {
+                                    seq->rgValue[i].pbData = nextPtr;
+                                    memcpy(nextPtr, ptr, 1 + nextLenBytes +
+                                     nextLen);
+                                    nextPtr += 1 + nextLenBytes + nextLen;
+                                }
+                                remainingLen -= 1 + nextLenBytes + nextLen;
+                                ptr += 1 + nextLenBytes + nextLen;
+                                i++;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+            ret = FALSE;
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeDistPointName(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+
+    if (pbEncoded[0] == (ASN_CONTEXT | ASN_CONSTRUCTOR | 0))
+    {
+        DWORD bytesNeeded, dataLen;
+
+        if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+        {
+            struct AsnArrayDescriptor arrayDesc = {
+             ASN_CONTEXT | ASN_CONSTRUCTOR | 0,
+             offsetof(CRL_DIST_POINT_NAME, u.FullName.cAltEntry),
+             offsetof(CRL_DIST_POINT_NAME, u.FullName.rgAltEntry),
+             FINALMEMBERSIZE(CRL_DIST_POINT_NAME, u),
+             CRYPT_AsnDecodeAltNameEntry, sizeof(CERT_ALT_NAME_ENTRY), TRUE,
+             offsetof(CERT_ALT_NAME_ENTRY, u.pwszURL) };
+            BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+            DWORD nameLen;
+
+            if (dataLen)
             {
-                SetLastError(CRYPT_E_ASN1_EOD);
-                ret = FALSE;
+                ret = CRYPT_AsnDecodeArray(&arrayDesc,
+                 pbEncoded + 1 + lenBytes, cbEncoded - 1 - lenBytes,
+                 dwFlags, NULL, NULL, &nameLen, NULL);
+                bytesNeeded = sizeof(CRL_DIST_POINT_NAME) + nameLen -
+                 FINALMEMBERSIZE(CRL_DIST_POINT_NAME, u);
             }
-            else if (pbEncoded[1] > 0x7f)
+            else
+                bytesNeeded = sizeof(CRL_DIST_POINT_NAME);
+            if (pcbDecoded)
+                *pcbDecoded = 1 + lenBytes + dataLen;
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if (*pcbStructInfo < bytesNeeded)
             {
-                /* long-form date strings really can't be valid */
-                SetLastError(CRYPT_E_ASN1_CORRUPT);
+                *pcbStructInfo = bytesNeeded;
+                SetLastError(ERROR_MORE_DATA);
                 ret = FALSE;
             }
             else
             {
-                SYSTEMTIME sysTime = { 0 };
-                BYTE len = pbEncoded[1];
+                CRL_DIST_POINT_NAME *name = pvStructInfo;
 
-                if (len < MIN_ENCODED_TIME_LENGTH)
+                *pcbStructInfo = bytesNeeded;
+                if (dataLen)
                 {
-                    SetLastError(CRYPT_E_ASN1_CORRUPT);
-                    ret = FALSE;
+                    name->dwDistPointNameChoice = CRL_DIST_POINT_FULL_NAME;
+                    ret = CRYPT_AsnDecodeArray(&arrayDesc,
+                     pbEncoded + 1 + lenBytes, cbEncoded - 1 - lenBytes,
+                     dwFlags, NULL, &name->u.FullName.cAltEntry, &nameLen,
+                     NULL);
                 }
                 else
-                {
-                    pbEncoded += 2;
-                    CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wYear);
-                    if (sysTime.wYear >= 50)
-                        sysTime.wYear += 1900;
-                    else
-                        sysTime.wYear += 2000;
-                    CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMonth);
-                    CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wDay);
-                    CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wHour);
-                    CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMinute);
-                    if (ret && len > 0)
-                    {
-                        if (len >= 2 && isdigit(*pbEncoded) &&
-                         isdigit(*(pbEncoded + 1)))
-                            CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2,
-                             sysTime.wSecond);
-                        else if (isdigit(*pbEncoded))
-                            CRYPT_TIME_GET_DIGITS(pbEncoded, len, 1,
-                             sysTime.wSecond);
-                        if (ret)
-                            ret = CRYPT_AsnDecodeTimeZone(pbEncoded, len,
-                             &sysTime);
-                    }
-                    if (ret && (ret = CRYPT_DecodeEnsureSpace(dwFlags,
-                     pDecodePara, pvStructInfo, pcbStructInfo,
-                     sizeof(FILETIME))))
-                    {
-                        if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
-                            pvStructInfo = *(BYTE **)pvStructInfo;
-                        ret = SystemTimeToFileTime(&sysTime,
-                         (FILETIME *)pvStructInfo);
-                    }
-                }
+                    name->dwDistPointNameChoice = CRL_DIST_POINT_NO_NAME;
             }
         }
+    }
+    else
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        ret = FALSE;
+    }
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeDistPoint(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
+{
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_CONTEXT | ASN_CONSTRUCTOR | 0, offsetof(CRL_DIST_POINT,
+       DistPointName), CRYPT_AsnDecodeDistPointName,
+       sizeof(CRL_DIST_POINT_NAME), TRUE, TRUE, offsetof(CRL_DIST_POINT,
+       DistPointName.u.FullName.rgAltEntry), 0 },
+     { ASN_CONTEXT | 1, offsetof(CRL_DIST_POINT, ReasonFlags),
+       CRYPT_AsnDecodeBitsInternal, sizeof(CRYPT_BIT_BLOB), TRUE, TRUE,
+       offsetof(CRL_DIST_POINT, ReasonFlags.pbData), 0 },
+     { ASN_CONTEXT | ASN_CONSTRUCTOR | 2, offsetof(CRL_DIST_POINT, CRLIssuer),
+       CRYPT_AsnDecodeAltNameInternal, sizeof(CERT_ALT_NAME_INFO), TRUE, TRUE,
+       offsetof(CRL_DIST_POINT, CRLIssuer.rgAltEntry), 0 },
+    };
+    CRL_DIST_POINT *point = pvStructInfo;
+    BOOL ret;
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, point ? point->DistPointName.u.FullName.rgAltEntry : NULL);
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeCRLDistPoints(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
+
+    __TRY
+    {
+        struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+         offsetof(CRL_DIST_POINTS_INFO, cDistPoint),
+         offsetof(CRL_DIST_POINTS_INFO, rgDistPoint),
+         sizeof(CRL_DIST_POINTS_INFO),
+         CRYPT_AsnDecodeDistPoint, sizeof(CRL_DIST_POINT), TRUE,
+         offsetof(CRL_DIST_POINT, DistPointName.u.FullName.rgAltEntry) };
+
+        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+         dwFlags, pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeEnhancedKeyUsage(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
+
+    __TRY
+    {
+        struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
+         offsetof(CERT_ENHKEY_USAGE, cUsageIdentifier),
+         offsetof(CERT_ENHKEY_USAGE, rgpszUsageIdentifier),
+         sizeof(CERT_ENHKEY_USAGE),
+         CRYPT_AsnDecodeOidInternal, sizeof(LPSTR), TRUE, 0 };
+
+        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+         dwFlags, pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeIssuingDistPoint(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
+
+    __TRY
+    {
+        struct AsnDecodeSequenceItem items[] = {
+         { ASN_CONTEXT | ASN_CONSTRUCTOR | 0, offsetof(CRL_ISSUING_DIST_POINT,
+           DistPointName), CRYPT_AsnDecodeDistPointName,
+           sizeof(CRL_DIST_POINT_NAME), TRUE, TRUE,
+           offsetof(CRL_ISSUING_DIST_POINT,
+           DistPointName.u.FullName.rgAltEntry), 0 },
+         { ASN_CONTEXT | 1, offsetof(CRL_ISSUING_DIST_POINT,
+           fOnlyContainsUserCerts), CRYPT_AsnDecodeBool, sizeof(BOOL), TRUE,
+           FALSE, 0 },
+         { ASN_CONTEXT | 2, offsetof(CRL_ISSUING_DIST_POINT,
+           fOnlyContainsCACerts), CRYPT_AsnDecodeBool, sizeof(BOOL), TRUE,
+           FALSE, 0 },
+         { ASN_CONTEXT | 3, offsetof(CRL_ISSUING_DIST_POINT,
+           OnlySomeReasonFlags), CRYPT_AsnDecodeBitsInternal,
+           sizeof(CRYPT_BIT_BLOB), TRUE, TRUE, offsetof(CRL_ISSUING_DIST_POINT,
+           OnlySomeReasonFlags.pbData), 0 },
+         { ASN_CONTEXT | 4, offsetof(CRL_ISSUING_DIST_POINT,
+           fIndirectCRL), CRYPT_AsnDecodeBool, sizeof(BOOL), TRUE, FALSE, 0 },
+        };
+
+        ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+         pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo,
+         pcbStructInfo, NULL, NULL);
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeMaximum(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    DWORD max, size = sizeof(max);
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    if (!cbEncoded)
+    {
+        SetLastError(CRYPT_E_ASN1_EOD);
+        return FALSE;
+    }
+    if (pbEncoded[0] != (ASN_CONTEXT | 1))
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        return FALSE;
+    }
+    if ((ret = CRYPT_AsnDecodeIntInternal(pbEncoded, cbEncoded, dwFlags,
+     &max, &size, pcbDecoded)))
+    {
+        DWORD bytesNeeded = FINALMEMBERSIZE(CERT_GENERAL_SUBTREE, fMaximum);
+
+        if (!pvStructInfo)
+            *pcbStructInfo = bytesNeeded;
+        else if (*pcbStructInfo < bytesNeeded)
+        {
+            *pcbStructInfo = bytesNeeded;
+            SetLastError(ERROR_MORE_DATA);
+            ret = FALSE;
+        }
         else
         {
-            SetLastError(CRYPT_E_ASN1_BADTAG);
-            ret = FALSE;
+            CERT_GENERAL_SUBTREE *subtree = (CERT_GENERAL_SUBTREE *)
+             ((BYTE *)pvStructInfo - offsetof(CERT_GENERAL_SUBTREE, fMaximum));
+
+            *pcbStructInfo = bytesNeeded;
+            /* The BOOL is implicit:  if the integer is present, then it's
+             * TRUE.
+             */
+            subtree->fMaximum = TRUE;
+            subtree->dwMaximum = max;
         }
     }
-    __EXCEPT_PAGE_FAULT
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeSubtree(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    struct AsnDecodeSequenceItem items[] = {
+     { 0, offsetof(CERT_GENERAL_SUBTREE, Base),
+       CRYPT_AsnDecodeAltNameEntry, sizeof(CERT_ALT_NAME_ENTRY), TRUE, TRUE,
+       offsetof(CERT_ALT_NAME_ENTRY, u.pwszURL), 0 },
+     { ASN_CONTEXT | 0, offsetof(CERT_GENERAL_SUBTREE, dwMinimum),
+       CRYPT_AsnDecodeIntInternal, sizeof(DWORD), TRUE, FALSE, 0, 0 },
+     { ASN_CONTEXT | 1, offsetof(CERT_GENERAL_SUBTREE, fMaximum),
+       CRYPT_AsnDecodeMaximum, FINALMEMBERSIZE(CERT_GENERAL_SUBTREE, fMaximum),
+       TRUE, FALSE, 0, 0 },
+    };
+    CERT_GENERAL_SUBTREE *subtree = pvStructInfo;
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, subtree ? subtree->Base.u.pwszURL : NULL);
+    if (pcbDecoded)
     {
-        SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
+        TRACE("%d\n", *pcbDecoded);
+        if (*pcbDecoded < cbEncoded)
+            TRACE("%02x %02x\n", *(pbEncoded + *pcbDecoded),
+             *(pbEncoded + *pcbDecoded + 1));
     }
-    __ENDTRY
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodePermittedSubtree(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret = TRUE;
+    struct AsnArrayDescriptor arrayDesc = { 0,
+     offsetof(CERT_NAME_CONSTRAINTS_INFO, cPermittedSubtree),
+     offsetof(CERT_NAME_CONSTRAINTS_INFO, rgPermittedSubtree),
+     MEMBERSIZE(CERT_NAME_CONSTRAINTS_INFO, cPermittedSubtree,
+                cExcludedSubtree),
+     CRYPT_AsnDecodeSubtree, sizeof(CERT_GENERAL_SUBTREE), TRUE,
+     offsetof(CERT_GENERAL_SUBTREE, Base.u.pwszURL) };
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    return ret;
+}
+
+static BOOL CRYPT_AsnDecodeExcludedSubtree(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret = TRUE;
+    struct AsnArrayDescriptor arrayDesc = { 0,
+     offsetof(CERT_NAME_CONSTRAINTS_INFO, cExcludedSubtree),
+     offsetof(CERT_NAME_CONSTRAINTS_INFO, rgExcludedSubtree),
+     FINALMEMBERSIZE(CERT_NAME_CONSTRAINTS_INFO, cExcludedSubtree),
+     CRYPT_AsnDecodeSubtree, sizeof(CERT_GENERAL_SUBTREE), TRUE,
+     offsetof(CERT_GENERAL_SUBTREE, Base.u.pwszURL) };
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeGeneralizedTime(DWORD dwCertEncodingType,
+static BOOL WINAPI CRYPT_AsnDecodeNameConstraints(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
-    BOOL ret;
+    BOOL ret = FALSE;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
 
-    if (!pvStructInfo)
-    {
-        *pcbStructInfo = sizeof(FILETIME);
-        return TRUE;
-    }
     __TRY
     {
-        ret = TRUE;
-        if (pbEncoded[0] == ASN_GENERALTIME)
-        {
-            if (cbEncoded <= 1)
-            {
-                SetLastError(CRYPT_E_ASN1_EOD);
-                ret = FALSE;
-            }
-            else if (pbEncoded[1] > 0x7f)
-            {
-                /* long-form date strings really can't be valid */
-                SetLastError(CRYPT_E_ASN1_CORRUPT);
-                ret = FALSE;
-            }
-            else
-            {
-                BYTE len = pbEncoded[1];
-
-                if (len < MIN_ENCODED_TIME_LENGTH)
-                {
-                    SetLastError(CRYPT_E_ASN1_CORRUPT);
-                    ret = FALSE;
-                }
-                else
-                {
-                    SYSTEMTIME sysTime = { 0 };
+        struct AsnDecodeSequenceItem items[] = {
+         { ASN_CONTEXT | ASN_CONSTRUCTOR | 0,
+           offsetof(CERT_NAME_CONSTRAINTS_INFO, cPermittedSubtree),
+           CRYPT_AsnDecodePermittedSubtree,
+           MEMBERSIZE(CERT_NAME_CONSTRAINTS_INFO, cPermittedSubtree,
+           cExcludedSubtree), TRUE, TRUE,
+           offsetof(CERT_NAME_CONSTRAINTS_INFO, rgPermittedSubtree), 0 },
+         { ASN_CONTEXT | ASN_CONSTRUCTOR | 1,
+           offsetof(CERT_NAME_CONSTRAINTS_INFO, cExcludedSubtree),
+           CRYPT_AsnDecodeExcludedSubtree,
+           FINALMEMBERSIZE(CERT_NAME_CONSTRAINTS_INFO, cExcludedSubtree),
+           TRUE, TRUE,
+           offsetof(CERT_NAME_CONSTRAINTS_INFO, rgExcludedSubtree), 0 },
+        };
 
-                    pbEncoded += 2;
-                    CRYPT_TIME_GET_DIGITS(pbEncoded, len, 4, sysTime.wYear);
-                    CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMonth);
-                    CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wDay);
-                    CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wHour);
-                    if (ret && len > 0)
-                    {
-                        CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2,
-                         sysTime.wMinute);
-                        if (ret && len > 0)
-                            CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2,
-                             sysTime.wSecond);
-                        if (ret && len > 0 && (*pbEncoded == '.' ||
-                         *pbEncoded == ','))
-                        {
-                            BYTE digits;
-
-                            pbEncoded++;
-                            len--;
-                            /* workaround macro weirdness */
-                            digits = min(len, 3);
-                            CRYPT_TIME_GET_DIGITS(pbEncoded, len, digits,
-                             sysTime.wMilliseconds);
-                        }
-                        if (ret)
-                            ret = CRYPT_AsnDecodeTimeZone(pbEncoded, len,
-                             &sysTime);
-                    }
-                    if (ret && (ret = CRYPT_DecodeEnsureSpace(dwFlags,
-                     pDecodePara, pvStructInfo, pcbStructInfo,
-                     sizeof(FILETIME))))
-                    {
-                        if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
-                            pvStructInfo = *(BYTE **)pvStructInfo;
-                        ret = SystemTimeToFileTime(&sysTime,
-                         (FILETIME *)pvStructInfo);
-                    }
-                }
-            }
-        }
-        else
-        {
-            SetLastError(CRYPT_E_ASN1_BADTAG);
-            ret = FALSE;
-        }
+        ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+         pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo,
+         pcbStructInfo, NULL, NULL);
     }
     __EXCEPT_PAGE_FAULT
     {
         SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
     }
     __ENDTRY
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeChoiceOfTime(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeIssuerSerialNumber(const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded)
 {
     BOOL ret;
+    struct AsnDecodeSequenceItem items[] = {
+     { 0, offsetof(CERT_ISSUER_SERIAL_NUMBER, Issuer), CRYPT_AsnDecodeDerBlob,
+       sizeof(CRYPT_DER_BLOB), FALSE, TRUE, offsetof(CERT_ISSUER_SERIAL_NUMBER,
+       Issuer.pbData) },
+     { ASN_INTEGER, offsetof(CERT_ISSUER_SERIAL_NUMBER, SerialNumber),
+       CRYPT_AsnDecodeIntegerInternal, sizeof(CRYPT_INTEGER_BLOB), FALSE,
+       TRUE, offsetof(CERT_ISSUER_SERIAL_NUMBER, SerialNumber.pbData), 0 },
+    };
+    CERT_ISSUER_SERIAL_NUMBER *issuerSerial = pvStructInfo;
 
-    __TRY
-    {
-        if (pbEncoded[0] == ASN_UTCTIME)
-            ret = CRYPT_AsnDecodeUtcTime(dwCertEncodingType, lpszStructType,
-             pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo,
-             pcbStructInfo);
-        else if (pbEncoded[0] == ASN_GENERALTIME)
-            ret = CRYPT_AsnDecodeGeneralizedTime(dwCertEncodingType,
-             lpszStructType, pbEncoded, cbEncoded, dwFlags, pDecodePara,
-             pvStructInfo, pcbStructInfo);
-        else
-        {
-            SetLastError(CRYPT_E_ASN1_BADTAG);
-            ret = FALSE;
-        }
-    }
-    __EXCEPT_PAGE_FAULT
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, issuerSerial ? issuerSerial->Issuer.pbData : NULL);
+    if (ret && issuerSerial && !issuerSerial->SerialNumber.cbData)
     {
-        SetLastError(STATUS_ACCESS_VIOLATION);
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
         ret = FALSE;
     }
-    __ENDTRY
+    TRACE("returning %d\n", ret);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeSequenceOfAny(DWORD dwCertEncodingType,
+static BOOL CRYPT_AsnDecodePKCSSignerInfoInternal(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    CMSG_SIGNER_INFO *info = pvStructInfo;
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_INTEGER, offsetof(CMSG_SIGNER_INFO, dwVersion),
+       CRYPT_AsnDecodeIntInternal, sizeof(DWORD), FALSE, FALSE, 0, 0 },
+     { ASN_SEQUENCEOF, offsetof(CMSG_SIGNER_INFO, Issuer),
+       CRYPT_AsnDecodeIssuerSerialNumber, sizeof(CERT_ISSUER_SERIAL_NUMBER),
+       FALSE, TRUE, offsetof(CMSG_SIGNER_INFO, Issuer.pbData), 0 },
+     { ASN_SEQUENCEOF, offsetof(CMSG_SIGNER_INFO, HashAlgorithm),
+       CRYPT_AsnDecodeAlgorithmId, sizeof(CRYPT_ALGORITHM_IDENTIFIER),
+       FALSE, TRUE, offsetof(CMSG_SIGNER_INFO, HashAlgorithm.pszObjId), 0 },
+     { ASN_CONSTRUCTOR | ASN_CONTEXT | 0,
+       offsetof(CMSG_SIGNER_INFO, AuthAttrs),
+       CRYPT_AsnDecodePKCSAttributesInternal, sizeof(CRYPT_ATTRIBUTES),
+       TRUE, TRUE, offsetof(CMSG_SIGNER_INFO, AuthAttrs.rgAttr), 0 },
+     { ASN_SEQUENCEOF, offsetof(CMSG_SIGNER_INFO, HashEncryptionAlgorithm),
+       CRYPT_AsnDecodeAlgorithmId, sizeof(CRYPT_ALGORITHM_IDENTIFIER),
+       FALSE, TRUE, offsetof(CMSG_SIGNER_INFO,
+       HashEncryptionAlgorithm.pszObjId), 0 },
+     { ASN_OCTETSTRING, offsetof(CMSG_SIGNER_INFO, EncryptedHash),
+       CRYPT_AsnDecodeOctetsInternal, sizeof(CRYPT_DER_BLOB),
+       FALSE, TRUE, offsetof(CMSG_SIGNER_INFO, EncryptedHash.pbData), 0 },
+     { ASN_CONSTRUCTOR | ASN_CONTEXT | 1,
+       offsetof(CMSG_SIGNER_INFO, UnauthAttrs),
+       CRYPT_AsnDecodePKCSAttributesInternal, sizeof(CRYPT_ATTRIBUTES),
+       TRUE, TRUE, offsetof(CMSG_SIGNER_INFO, UnauthAttrs.rgAttr), 0 },
+    };
+    BOOL ret;
+
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo);
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, info ? info->Issuer.pbData : NULL);
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodePKCSSignerInfo(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
-    BOOL ret = TRUE;
+    BOOL ret = FALSE;
+
+    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
 
     __TRY
     {
-        if (pbEncoded[0] == ASN_SEQUENCEOF)
+        ret = CRYPT_AsnDecodePKCSSignerInfoInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pcbStructInfo, NULL);
+        if (ret && pvStructInfo)
         {
-            DWORD bytesNeeded, dataLen, remainingLen, cValue;
-
-            if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+            ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
+             pcbStructInfo, *pcbStructInfo);
+            if (ret)
             {
-                BYTE lenBytes;
-                const BYTE *ptr;
-
-                lenBytes = GET_LEN_BYTES(pbEncoded[1]);
-                bytesNeeded = sizeof(CRYPT_SEQUENCE_OF_ANY);
-                cValue = 0;
-                ptr = pbEncoded + 1 + lenBytes;
-                remainingLen = dataLen;
-                while (ret && remainingLen)
-                {
-                    DWORD nextLen;
-
-                    ret = CRYPT_GetLen(ptr, remainingLen, &nextLen);
-                    if (ret)
-                    {
-                        DWORD nextLenBytes = GET_LEN_BYTES(ptr[1]);
-
-                        remainingLen -= 1 + nextLenBytes + nextLen;
-                        ptr += 1 + nextLenBytes + nextLen;
-                        bytesNeeded += sizeof(CRYPT_DER_BLOB);
-                        if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG))
-                            bytesNeeded += 1 + nextLenBytes + nextLen;
-                        cValue++;
-                    }
-                }
-                if (ret)
-                {
-                    CRYPT_SEQUENCE_OF_ANY *seq;
-                    BYTE *nextPtr;
-                    DWORD i;
-
-                    if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
-                     pvStructInfo, pcbStructInfo, bytesNeeded)))
-                    {
-                        if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
-                            pvStructInfo = *(BYTE **)pvStructInfo;
-                        seq = (CRYPT_SEQUENCE_OF_ANY *)pvStructInfo;
-                        seq->cValue = cValue;
-                        seq->rgValue = (CRYPT_DER_BLOB *)((BYTE *)seq +
-                         sizeof(*seq));
-                        nextPtr = (BYTE *)seq->rgValue +
-                         cValue * sizeof(CRYPT_DER_BLOB);
-                        ptr = pbEncoded + 1 + lenBytes;
-                        remainingLen = dataLen;
-                        i = 0;
-                        while (ret && remainingLen)
-                        {
-                            DWORD nextLen;
-
-                            ret = CRYPT_GetLen(ptr, remainingLen, &nextLen);
-                            if (ret)
-                            {
-                                DWORD nextLenBytes = GET_LEN_BYTES(ptr[1]);
+                CMSG_SIGNER_INFO *info;
 
-                                seq->rgValue[i].cbData = 1 + nextLenBytes +
-                                 nextLen;
-                                if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
-                                    seq->rgValue[i].pbData = (BYTE *)ptr;
-                                else
-                                {
-                                    seq->rgValue[i].pbData = nextPtr;
-                                    memcpy(nextPtr, ptr, 1 + nextLenBytes +
-                                     nextLen);
-                                    nextPtr += 1 + nextLenBytes + nextLen;
-                                }
-                                remainingLen -= 1 + nextLenBytes + nextLen;
-                                ptr += 1 + nextLenBytes + nextLen;
-                                i++;
-                            }
-                        }
-                    }
-                }
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                info = pvStructInfo;
+                info->Issuer.pbData = ((BYTE *)info +
+                 sizeof(CMSG_SIGNER_INFO));
+                ret = CRYPT_AsnDecodePKCSSignerInfoInternal(pbEncoded,
+                 cbEncoded, dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo,
+                 pcbStructInfo, NULL);
             }
         }
-        else
-        {
-            SetLastError(CRYPT_E_ASN1_BADTAG);
-            ret = FALSE;
-        }
     }
     __EXCEPT_PAGE_FAULT
     {
         SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
     }
     __ENDTRY
+    TRACE("returning %d\n", ret);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeDistPointName(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeCMSCertEncoded(const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded)
 {
     BOOL ret;
+    struct AsnArrayDescriptor arrayDesc = { 0,
+     offsetof(CRYPT_SIGNED_INFO, cCertEncoded),
+     offsetof(CRYPT_SIGNED_INFO, rgCertEncoded),
+     MEMBERSIZE(CRYPT_SIGNED_INFO, cCertEncoded, cCrlEncoded),
+     CRYPT_AsnDecodeCopyBytes,
+     sizeof(CRYPT_DER_BLOB), TRUE, offsetof(CRYPT_DER_BLOB, pbData) };
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, pvStructInfo ? *pcbStructInfo : 0, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    return ret;
+}
 
-    if (pbEncoded[0] == (ASN_CONTEXT | ASN_CONSTRUCTOR | 0))
-    {
-        DWORD bytesNeeded, dataLen;
-
-        if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
-        {
-            struct AsnArrayDescriptor arrayDesc = {
-             ASN_CONTEXT | ASN_CONSTRUCTOR | 0, CRYPT_AsnDecodeAltNameEntry,
-             sizeof(CERT_ALT_NAME_ENTRY), TRUE,
-             offsetof(CERT_ALT_NAME_ENTRY, u.pwszURL) };
-            BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
-
-            if (dataLen)
-            {
-                DWORD nameLen;
+static BOOL CRYPT_AsnDecodeCMSCrlEncoded(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    BOOL ret;
+    struct AsnArrayDescriptor arrayDesc = { 0,
+     offsetof(CRYPT_SIGNED_INFO, cCrlEncoded),
+     offsetof(CRYPT_SIGNED_INFO, rgCrlEncoded),
+     MEMBERSIZE(CRYPT_SIGNED_INFO, cCrlEncoded, content),
+     CRYPT_AsnDecodeCopyBytes, sizeof(CRYPT_DER_BLOB),
+     TRUE, offsetof(CRYPT_DER_BLOB, pbData) };
+
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, pvStructInfo ? *pcbStructInfo : 0, pcbDecoded);
+
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+    return ret;
+}
 
-                ret = CRYPT_AsnDecodeArray(&arrayDesc,
-                 pbEncoded + 1 + lenBytes, cbEncoded - 1 - lenBytes,
-                 0, NULL, NULL, &nameLen, NULL);
-                bytesNeeded = sizeof(CRL_DIST_POINT_NAME) + nameLen;
-            }
-            else
-                bytesNeeded = sizeof(CRL_DIST_POINT_NAME);
-            if (!pvStructInfo)
-                *pcbStructInfo = bytesNeeded;
-            else if (*pcbStructInfo < bytesNeeded)
-            {
-                *pcbStructInfo = bytesNeeded;
-                SetLastError(ERROR_MORE_DATA);
-                ret = FALSE;
-            }
-            else
-            {
-                CRL_DIST_POINT_NAME *name = (CRL_DIST_POINT_NAME *)pvStructInfo;
+static BOOL CRYPT_AsnDecodeCMSSignerId(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+    CERT_ID *id = pvStructInfo;
+    BOOL ret = FALSE;
 
-                if (dataLen)
-                {
-                    name->dwDistPointNameChoice = CRL_DIST_POINT_FULL_NAME;
-                    ret = CRYPT_AsnDecodeArray(&arrayDesc,
-                     pbEncoded + 1 + lenBytes, cbEncoded - 1 - lenBytes,
-                     0, NULL, &name->u.FullName, pcbStructInfo,
-                     name->u.FullName.rgAltEntry);
-                }
-                else
-                    name->dwDistPointNameChoice = CRL_DIST_POINT_NO_NAME;
-            }
+    if (*pbEncoded == ASN_SEQUENCEOF)
+    {
+        ret = CRYPT_AsnDecodeIssuerSerialNumber(pbEncoded, cbEncoded, dwFlags,
+         id ? &id->u.IssuerSerialNumber : NULL, pcbStructInfo, pcbDecoded);
+        if (ret)
+        {
+            if (id)
+                id->dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
+            if (*pcbStructInfo > sizeof(CERT_ISSUER_SERIAL_NUMBER))
+                *pcbStructInfo = sizeof(CERT_ID) + *pcbStructInfo -
+                 sizeof(CERT_ISSUER_SERIAL_NUMBER);
+            else
+                *pcbStructInfo = sizeof(CERT_ID);
         }
     }
-    else
+    else if (*pbEncoded == (ASN_CONTEXT | 0))
     {
-        SetLastError(CRYPT_E_ASN1_BADTAG);
-        ret = FALSE;
+        ret = CRYPT_AsnDecodeOctetsInternal(pbEncoded, cbEncoded, dwFlags,
+         id ? &id->u.KeyId : NULL, pcbStructInfo, pcbDecoded);
+        if (ret)
+        {
+            if (id)
+                id->dwIdChoice = CERT_ID_KEY_IDENTIFIER;
+            if (*pcbStructInfo > sizeof(CRYPT_DATA_BLOB))
+                *pcbStructInfo = sizeof(CERT_ID) + *pcbStructInfo -
+                 sizeof(CRYPT_DATA_BLOB);
+            else
+                *pcbStructInfo = sizeof(CERT_ID);
+        }
     }
+    else
+        SetLastError(CRYPT_E_ASN1_BADTAG);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeDistPoint(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_AsnDecodeCMSSignerInfoInternal(const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD *pcbDecoded)
 {
+    CMSG_CMS_SIGNER_INFO *info = pvStructInfo;
     struct AsnDecodeSequenceItem items[] = {
-     { ASN_CONTEXT | ASN_CONSTRUCTOR | 0, offsetof(CRL_DIST_POINT,
-       DistPointName), CRYPT_AsnDecodeDistPointName,
-       sizeof(CRL_DIST_POINT_NAME), TRUE, TRUE, offsetof(CRL_DIST_POINT,
-       DistPointName.u.FullName.rgAltEntry), 0 },
-     { ASN_CONTEXT | 1, offsetof(CRL_DIST_POINT, ReasonFlags),
-       CRYPT_AsnDecodeBitsInternal, sizeof(CRYPT_BIT_BLOB), TRUE, TRUE,
-       offsetof(CRL_DIST_POINT, ReasonFlags.pbData), 0 },
-     { ASN_CONTEXT | ASN_CONSTRUCTOR | 2, offsetof(CRL_DIST_POINT, CRLIssuer),
-       CRYPT_AsnDecodeAltNameInternal, sizeof(CERT_ALT_NAME_INFO), TRUE, TRUE,
-       offsetof(CRL_DIST_POINT, CRLIssuer.rgAltEntry), 0 },
+     { ASN_INTEGER, offsetof(CMSG_CMS_SIGNER_INFO, dwVersion),
+       CRYPT_AsnDecodeIntInternal, sizeof(DWORD), FALSE, FALSE, 0, 0 },
+     { 0, offsetof(CMSG_CMS_SIGNER_INFO, SignerId),
+       CRYPT_AsnDecodeCMSSignerId, sizeof(CERT_ID), FALSE, TRUE,
+       offsetof(CMSG_CMS_SIGNER_INFO, SignerId.u.KeyId.pbData), 0 },
+     { ASN_SEQUENCEOF, offsetof(CMSG_CMS_SIGNER_INFO, HashAlgorithm),
+       CRYPT_AsnDecodeAlgorithmId, sizeof(CRYPT_ALGORITHM_IDENTIFIER),
+       FALSE, TRUE, offsetof(CMSG_CMS_SIGNER_INFO, HashAlgorithm.pszObjId), 0 },
+     { ASN_CONSTRUCTOR | ASN_CONTEXT | 0,
+       offsetof(CMSG_CMS_SIGNER_INFO, AuthAttrs),
+       CRYPT_AsnDecodePKCSAttributesInternal, sizeof(CRYPT_ATTRIBUTES),
+       TRUE, TRUE, offsetof(CMSG_CMS_SIGNER_INFO, AuthAttrs.rgAttr), 0 },
+     { ASN_SEQUENCEOF, offsetof(CMSG_CMS_SIGNER_INFO, HashEncryptionAlgorithm),
+       CRYPT_AsnDecodeAlgorithmId, sizeof(CRYPT_ALGORITHM_IDENTIFIER),
+       FALSE, TRUE, offsetof(CMSG_CMS_SIGNER_INFO,
+       HashEncryptionAlgorithm.pszObjId), 0 },
+     { ASN_OCTETSTRING, offsetof(CMSG_CMS_SIGNER_INFO, EncryptedHash),
+       CRYPT_AsnDecodeOctetsInternal, sizeof(CRYPT_DER_BLOB),
+       FALSE, TRUE, offsetof(CMSG_CMS_SIGNER_INFO, EncryptedHash.pbData), 0 },
+     { ASN_CONSTRUCTOR | ASN_CONTEXT | 1,
+       offsetof(CMSG_CMS_SIGNER_INFO, UnauthAttrs),
+       CRYPT_AsnDecodePKCSAttributesInternal, sizeof(CRYPT_ATTRIBUTES),
+       TRUE, TRUE, offsetof(CMSG_CMS_SIGNER_INFO, UnauthAttrs.rgAttr), 0 },
     };
     BOOL ret;
 
-    ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
-     sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded,
-     dwFlags, pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo);
+
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+     pcbDecoded, info ? info->SignerId.u.KeyId.pbData : NULL);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeCRLDistPoints(DWORD dwCertEncodingType,
+static BOOL WINAPI CRYPT_AsnDecodeCMSSignerInfo(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
-    BOOL ret;
+    BOOL ret = FALSE;
 
     TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
      pDecodePara, pvStructInfo, *pcbStructInfo);
 
     __TRY
     {
-        struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
-         CRYPT_AsnDecodeDistPoint, sizeof(CRL_DIST_POINT), TRUE,
-         offsetof(CRL_DIST_POINT, DistPointName.u.FullName.rgAltEntry) };
+        ret = CRYPT_AsnDecodeCMSSignerInfoInternal(pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pcbStructInfo, NULL);
+        if (ret && pvStructInfo)
+        {
+            ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
+             pcbStructInfo, *pcbStructInfo);
+            if (ret)
+            {
+                CMSG_CMS_SIGNER_INFO *info;
 
-        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                info = pvStructInfo;
+                info->SignerId.u.KeyId.pbData = ((BYTE *)info +
+                 sizeof(CMSG_CMS_SIGNER_INFO));
+                ret = CRYPT_AsnDecodeCMSSignerInfoInternal(pbEncoded,
+                 cbEncoded, dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, pvStructInfo,
+                 pcbStructInfo, NULL);
+            }
+        }
     }
     __EXCEPT_PAGE_FAULT
     {
         SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
     }
     __ENDTRY
+    TRACE("returning %d\n", ret);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeEnhancedKeyUsage(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static BOOL CRYPT_DecodeSignerArray(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
 {
     BOOL ret;
+    struct AsnArrayDescriptor arrayDesc = { ASN_CONSTRUCTOR | ASN_SETOF,
+     offsetof(CRYPT_SIGNED_INFO, cSignerInfo),
+     offsetof(CRYPT_SIGNED_INFO, rgSignerInfo),
+     FINALMEMBERSIZE(CRYPT_SIGNED_INFO, cSignerInfo),
+     CRYPT_AsnDecodeCMSSignerInfoInternal, sizeof(CMSG_CMS_SIGNER_INFO), TRUE,
+     offsetof(CMSG_CMS_SIGNER_INFO, SignerId.u.KeyId.pbData) };
 
-    TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
-
-    __TRY
-    {
-        struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF,
-         CRYPT_AsnDecodeOidInternal, sizeof(LPSTR), TRUE, 0 };
+    TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo, pcbDecoded);
 
-        ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
-    }
-    __EXCEPT_PAGE_FAULT
-    {
-        SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
-    }
-    __ENDTRY
+    ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+     dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnDecodeIssuingDistPoint(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+BOOL CRYPT_AsnDecodeCMSSignedInfo(const BYTE *pbEncoded, DWORD cbEncoded,
DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
CRYPT_SIGNED_INFO *signedInfo, DWORD *pcbSignedInfo)
 {
-    BOOL ret;
+    BOOL ret = FALSE;
+    struct AsnDecodeSequenceItem items[] = {
+     { ASN_INTEGER, offsetof(CRYPT_SIGNED_INFO, version),
+       CRYPT_AsnDecodeIntInternal, sizeof(DWORD), FALSE, FALSE, 0, 0 },
+     /* Placeholder for the hash algorithms - redundant with those in the
+      * signers, so just ignore them.
+      */
+     { ASN_CONSTRUCTOR | ASN_SETOF, 0, NULL, 0, TRUE, FALSE, 0, 0 },
+     { ASN_SEQUENCE, offsetof(CRYPT_SIGNED_INFO, content),
+       CRYPT_AsnDecodePKCSContentInfoInternal, sizeof(CRYPT_CONTENT_INFO),
+       FALSE, TRUE, offsetof(CRYPT_SIGNED_INFO, content.pszObjId), 0 },
+     { ASN_CONSTRUCTOR | ASN_CONTEXT | 0,
+       offsetof(CRYPT_SIGNED_INFO, cCertEncoded), CRYPT_AsnDecodeCMSCertEncoded,
+       MEMBERSIZE(CRYPT_SIGNED_INFO, cCertEncoded, cCrlEncoded), TRUE, TRUE,
+       offsetof(CRYPT_SIGNED_INFO, rgCertEncoded), 0 },
+     { ASN_CONSTRUCTOR | ASN_CONTEXT | 1,
+       offsetof(CRYPT_SIGNED_INFO, cCrlEncoded), CRYPT_AsnDecodeCMSCrlEncoded,
+       MEMBERSIZE(CRYPT_SIGNED_INFO, cCrlEncoded, content), TRUE, TRUE,
+       offsetof(CRYPT_SIGNED_INFO, rgCrlEncoded), 0 },
+     { ASN_CONSTRUCTOR | ASN_SETOF, offsetof(CRYPT_SIGNED_INFO, cSignerInfo),
+       CRYPT_DecodeSignerArray,
+       FINALMEMBERSIZE(CRYPT_SIGNED_INFO, cSignerInfo), TRUE, TRUE,
+       offsetof(CRYPT_SIGNED_INFO, rgSignerInfo), 0 },
+    };
 
     TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, *pcbStructInfo);
-
-    __TRY
-    {
-        struct AsnDecodeSequenceItem items[] = {
-         { ASN_CONTEXT | ASN_CONSTRUCTOR | 0, offsetof(CRL_ISSUING_DIST_POINT,
-           DistPointName), CRYPT_AsnDecodeDistPointName,
-           sizeof(CRL_DIST_POINT_NAME), TRUE, TRUE,
-           offsetof(CRL_ISSUING_DIST_POINT,
-           DistPointName.u.FullName.rgAltEntry), 0 },
-         { ASN_CONTEXT | 1, offsetof(CRL_ISSUING_DIST_POINT,
-           fOnlyContainsUserCerts), CRYPT_AsnDecodeBool, sizeof(BOOL), TRUE,
-           FALSE, 0 },
-         { ASN_CONTEXT | 2, offsetof(CRL_ISSUING_DIST_POINT,
-           fOnlyContainsCACerts), CRYPT_AsnDecodeBool, sizeof(BOOL), TRUE,
-           FALSE, 0 },
-         { ASN_CONTEXT | 3, offsetof(CRL_ISSUING_DIST_POINT,
-           OnlySomeReasonFlags), CRYPT_AsnDecodeBitsInternal,
-           sizeof(CRYPT_BIT_BLOB), TRUE, TRUE, offsetof(CRL_ISSUING_DIST_POINT,
-           OnlySomeReasonFlags.pbData), 0 },
-         { ASN_CONTEXT | 4, offsetof(CRL_ISSUING_DIST_POINT,
-           fIndirectCRL), CRYPT_AsnDecodeBool, sizeof(BOOL), TRUE, FALSE, 0 },
-        };
+     pDecodePara, signedInfo, *pcbSignedInfo);
 
-        ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
-         sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded,
-         dwFlags, pDecodePara, pvStructInfo, pcbStructInfo, NULL);
-    }
-    __EXCEPT_PAGE_FAULT
-    {
-        SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
-    }
-    __ENDTRY
+    ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+     pbEncoded, cbEncoded, dwFlags, pDecodePara, signedInfo, pcbSignedInfo,
+     NULL, NULL);
+    TRACE("returning %d\n", ret);
     return ret;
 }
 
-BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
- const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
- PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+static CryptDecodeObjectExFunc CRYPT_GetBuiltinDecoder(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType)
 {
-    static HCRYPTOIDFUNCSET set = NULL;
-    BOOL ret = FALSE;
     CryptDecodeObjectExFunc decodeFunc = NULL;
-    HCRYPTOIDFUNCADDR hFunc = NULL;
-
-    TRACE("(0x%08x, %s, %p, %d, 0x%08x, %p, %p, %p)\n",
-     dwCertEncodingType, debugstr_a(lpszStructType), pbEncoded,
-     cbEncoded, dwFlags, pDecodePara, pvStructInfo, pcbStructInfo);
 
-    if (!pvStructInfo && !pcbStructInfo)
-    {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
     if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING
      && (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) != PKCS_7_ASN_ENCODING)
     {
         SetLastError(ERROR_FILE_NOT_FOUND);
-        return FALSE;
-    }
-    if (!cbEncoded)
-    {
-        SetLastError(CRYPT_E_ASN1_EOD);
-        return FALSE;
-    }
-    if (cbEncoded > MAX_ENCODED_LEN)
-    {
-        SetLastError(CRYPT_E_ASN1_LARGE);
-        return FALSE;
+        return NULL;
     }
-
-    SetLastError(NOERROR);
-    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG && pvStructInfo)
-        *(BYTE **)pvStructInfo = NULL;
     if (!HIWORD(lpszStructType))
     {
         switch (LOWORD(lpszStructType))
         {
-        case (WORD)X509_CERT:
+        case LOWORD(X509_CERT):
             decodeFunc = CRYPT_AsnDecodeCertSignedContent;
             break;
-        case (WORD)X509_CERT_TO_BE_SIGNED:
+        case LOWORD(X509_CERT_TO_BE_SIGNED):
             decodeFunc = CRYPT_AsnDecodeCert;
             break;
-        case (WORD)X509_CERT_CRL_TO_BE_SIGNED:
+        case LOWORD(X509_CERT_CRL_TO_BE_SIGNED):
             decodeFunc = CRYPT_AsnDecodeCRL;
             break;
-        case (WORD)X509_EXTENSIONS:
+        case LOWORD(X509_EXTENSIONS):
             decodeFunc = CRYPT_AsnDecodeExtensions;
             break;
-        case (WORD)X509_NAME_VALUE:
+        case LOWORD(X509_NAME_VALUE):
             decodeFunc = CRYPT_AsnDecodeNameValue;
             break;
-        case (WORD)X509_NAME:
+        case LOWORD(X509_NAME):
             decodeFunc = CRYPT_AsnDecodeName;
             break;
-        case (WORD)X509_PUBLIC_KEY_INFO:
+        case LOWORD(X509_PUBLIC_KEY_INFO):
             decodeFunc = CRYPT_AsnDecodePubKeyInfo;
             break;
-        case (WORD)X509_AUTHORITY_KEY_ID:
+        case LOWORD(X509_AUTHORITY_KEY_ID):
             decodeFunc = CRYPT_AsnDecodeAuthorityKeyId;
             break;
-        case (WORD)X509_ALTERNATE_NAME:
+        case LOWORD(X509_ALTERNATE_NAME):
             decodeFunc = CRYPT_AsnDecodeAltName;
             break;
-        case (WORD)X509_BASIC_CONSTRAINTS:
+        case LOWORD(X509_BASIC_CONSTRAINTS):
             decodeFunc = CRYPT_AsnDecodeBasicConstraints;
             break;
-        case (WORD)X509_BASIC_CONSTRAINTS2:
+        case LOWORD(X509_BASIC_CONSTRAINTS2):
             decodeFunc = CRYPT_AsnDecodeBasicConstraints2;
             break;
-        case (WORD)RSA_CSP_PUBLICKEYBLOB:
+        case LOWORD(X509_CERT_POLICIES):
+            decodeFunc = CRYPT_AsnDecodeCertPolicies;
+            break;
+        case LOWORD(RSA_CSP_PUBLICKEYBLOB):
             decodeFunc = CRYPT_AsnDecodeRsaPubKey;
             break;
-        case (WORD)X509_UNICODE_NAME:
+        case LOWORD(X509_UNICODE_NAME):
             decodeFunc = CRYPT_AsnDecodeUnicodeName;
             break;
-        case (WORD)X509_UNICODE_NAME_VALUE:
+        case LOWORD(PKCS_ATTRIBUTE):
+            decodeFunc = CRYPT_AsnDecodePKCSAttribute;
+            break;
+        case LOWORD(X509_UNICODE_NAME_VALUE):
             decodeFunc = CRYPT_AsnDecodeUnicodeNameValue;
             break;
-        case (WORD)X509_OCTET_STRING:
+        case LOWORD(X509_OCTET_STRING):
             decodeFunc = CRYPT_AsnDecodeOctets;
             break;
-        case (WORD)X509_BITS:
-        case (WORD)X509_KEY_USAGE:
+        case LOWORD(X509_BITS):
+        case LOWORD(X509_KEY_USAGE):
             decodeFunc = CRYPT_AsnDecodeBits;
             break;
-        case (WORD)X509_INTEGER:
+        case LOWORD(X509_INTEGER):
             decodeFunc = CRYPT_AsnDecodeInt;
             break;
-        case (WORD)X509_MULTI_BYTE_INTEGER:
+        case LOWORD(X509_MULTI_BYTE_INTEGER):
             decodeFunc = CRYPT_AsnDecodeInteger;
             break;
-        case (WORD)X509_MULTI_BYTE_UINT:
+        case LOWORD(X509_MULTI_BYTE_UINT):
             decodeFunc = CRYPT_AsnDecodeUnsignedInteger;
             break;
-        case (WORD)X509_ENUMERATED:
+        case LOWORD(X509_ENUMERATED):
             decodeFunc = CRYPT_AsnDecodeEnumerated;
             break;
-        case (WORD)X509_CHOICE_OF_TIME:
+        case LOWORD(X509_CHOICE_OF_TIME):
             decodeFunc = CRYPT_AsnDecodeChoiceOfTime;
             break;
-        case (WORD)X509_SEQUENCE_OF_ANY:
+        case LOWORD(X509_AUTHORITY_KEY_ID2):
+            decodeFunc = CRYPT_AsnDecodeAuthorityKeyId2;
+            break;
+        case LOWORD(X509_AUTHORITY_INFO_ACCESS):
+            decodeFunc = CRYPT_AsnDecodeAuthorityInfoAccess;
+            break;
+        case LOWORD(PKCS_CONTENT_INFO):
+            decodeFunc = CRYPT_AsnDecodePKCSContentInfo;
+            break;
+        case LOWORD(X509_SEQUENCE_OF_ANY):
             decodeFunc = CRYPT_AsnDecodeSequenceOfAny;
             break;
-        case (WORD)PKCS_UTC_TIME:
+        case LOWORD(PKCS_UTC_TIME):
             decodeFunc = CRYPT_AsnDecodeUtcTime;
             break;
-        case (WORD)X509_CRL_DIST_POINTS:
+        case LOWORD(X509_CRL_DIST_POINTS):
             decodeFunc = CRYPT_AsnDecodeCRLDistPoints;
             break;
-        case (WORD)X509_ENHANCED_KEY_USAGE:
+        case LOWORD(X509_ENHANCED_KEY_USAGE):
             decodeFunc = CRYPT_AsnDecodeEnhancedKeyUsage;
             break;
-        case (WORD)X509_ISSUING_DIST_POINT:
+        case LOWORD(PKCS_CTL):
+            decodeFunc = CRYPT_AsnDecodeCTL;
+            break;
+        case LOWORD(PKCS_SMIME_CAPABILITIES):
+            decodeFunc = CRYPT_AsnDecodeSMIMECapabilities;
+            break;
+        case LOWORD(X509_PKIX_POLICY_QUALIFIER_USERNOTICE):
+            decodeFunc = CRYPT_AsnDecodePolicyQualifierUserNotice;
+            break;
+        case LOWORD(PKCS_ATTRIBUTES):
+            decodeFunc = CRYPT_AsnDecodePKCSAttributes;
+            break;
+        case LOWORD(X509_ISSUING_DIST_POINT):
             decodeFunc = CRYPT_AsnDecodeIssuingDistPoint;
             break;
-        default:
-            FIXME("%d: unimplemented\n", LOWORD(lpszStructType));
+        case LOWORD(X509_NAME_CONSTRAINTS):
+            decodeFunc = CRYPT_AsnDecodeNameConstraints;
+            break;
+        case LOWORD(X509_POLICY_MAPPINGS):
+            decodeFunc = CRYPT_AsnDecodeCertPolicyMappings;
+            break;
+        case LOWORD(X509_POLICY_CONSTRAINTS):
+            decodeFunc = CRYPT_AsnDecodeCertPolicyConstraints;
+            break;
+        case LOWORD(PKCS7_SIGNER_INFO):
+            decodeFunc = CRYPT_AsnDecodePKCSSignerInfo;
+            break;
+        case LOWORD(CMS_SIGNER_INFO):
+            decodeFunc = CRYPT_AsnDecodeCMSSignerInfo;
+            break;
         }
     }
     else if (!strcmp(lpszStructType, szOID_CERT_EXTENSIONS))
         decodeFunc = CRYPT_AsnDecodeExtensions;
     else if (!strcmp(lpszStructType, szOID_RSA_signingTime))
         decodeFunc = CRYPT_AsnDecodeUtcTime;
+    else if (!strcmp(lpszStructType, szOID_RSA_SMIMECapabilities))
+        decodeFunc = CRYPT_AsnDecodeSMIMECapabilities;
     else if (!strcmp(lpszStructType, szOID_AUTHORITY_KEY_IDENTIFIER))
         decodeFunc = CRYPT_AsnDecodeAuthorityKeyId;
+    else if (!strcmp(lpszStructType, szOID_LEGACY_POLICY_MAPPINGS))
+        decodeFunc = CRYPT_AsnDecodeCertPolicyMappings;
+    else if (!strcmp(lpszStructType, szOID_AUTHORITY_KEY_IDENTIFIER2))
+        decodeFunc = CRYPT_AsnDecodeAuthorityKeyId2;
     else if (!strcmp(lpszStructType, szOID_CRL_REASON_CODE))
         decodeFunc = CRYPT_AsnDecodeEnumerated;
     else if (!strcmp(lpszStructType, szOID_KEY_USAGE))
@@ -3600,26 +5776,207 @@ BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
         decodeFunc = CRYPT_AsnDecodeAltName;
     else if (!strcmp(lpszStructType, szOID_CRL_DIST_POINTS))
         decodeFunc = CRYPT_AsnDecodeCRLDistPoints;
+    else if (!strcmp(lpszStructType, szOID_CERT_POLICIES))
+        decodeFunc = CRYPT_AsnDecodeCertPolicies;
+    else if (!strcmp(lpszStructType, szOID_POLICY_MAPPINGS))
+        decodeFunc = CRYPT_AsnDecodeCertPolicyMappings;
+    else if (!strcmp(lpszStructType, szOID_POLICY_CONSTRAINTS))
+        decodeFunc = CRYPT_AsnDecodeCertPolicyConstraints;
     else if (!strcmp(lpszStructType, szOID_ENHANCED_KEY_USAGE))
         decodeFunc = CRYPT_AsnDecodeEnhancedKeyUsage;
     else if (!strcmp(lpszStructType, szOID_ISSUING_DIST_POINT))
         decodeFunc = CRYPT_AsnDecodeIssuingDistPoint;
-    else
-        TRACE("OID %s not found or unimplemented, looking for DLL\n",
+    else if (!strcmp(lpszStructType, szOID_NAME_CONSTRAINTS))
+        decodeFunc = CRYPT_AsnDecodeNameConstraints;
+    else if (!strcmp(lpszStructType, szOID_AUTHORITY_INFO_ACCESS))
+        decodeFunc = CRYPT_AsnDecodeAuthorityInfoAccess;
+    else if (!strcmp(lpszStructType, szOID_PKIX_POLICY_QUALIFIER_USERNOTICE))
+        decodeFunc = CRYPT_AsnDecodePolicyQualifierUserNotice;
+    else if (!strcmp(lpszStructType, szOID_CTL))
+        decodeFunc = CRYPT_AsnDecodeCTL;
+    return decodeFunc;
+}
+
+static CryptDecodeObjectFunc CRYPT_LoadDecoderFunc(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, HCRYPTOIDFUNCADDR *hFunc)
+{
+    static HCRYPTOIDFUNCSET set = NULL;
+    CryptDecodeObjectFunc decodeFunc = NULL;
+
+    if (!set)
+        set = CryptInitOIDFunctionSet(CRYPT_OID_DECODE_OBJECT_FUNC, 0);
+    CryptGetOIDFunctionAddress(set, dwCertEncodingType, lpszStructType, 0,
+     (void **)&decodeFunc, hFunc);
+    return decodeFunc;
+}
+
+static CryptDecodeObjectExFunc CRYPT_LoadDecoderExFunc(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, HCRYPTOIDFUNCADDR *hFunc)
+{
+    static HCRYPTOIDFUNCSET set = NULL;
+    CryptDecodeObjectExFunc decodeFunc = NULL;
+
+    if (!set)
+        set = CryptInitOIDFunctionSet(CRYPT_OID_DECODE_OBJECT_EX_FUNC, 0);
+    CryptGetOIDFunctionAddress(set, dwCertEncodingType, lpszStructType, 0,
+     (void **)&decodeFunc, hFunc);
+    return decodeFunc;
+}
+
+BOOL WINAPI CryptDecodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType,
+ const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo,
+ DWORD *pcbStructInfo)
+{
+    BOOL ret = FALSE;
+    CryptDecodeObjectFunc pCryptDecodeObject = NULL;
+    CryptDecodeObjectExFunc pCryptDecodeObjectEx = NULL;
+    HCRYPTOIDFUNCADDR hFunc = NULL;
+
+    TRACE_(crypt)("(0x%08x, %s, %p, %d, 0x%08x, %p, %p)\n", dwCertEncodingType,
+     debugstr_a(lpszStructType), pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, pcbStructInfo);
+
+    if (!pvStructInfo && !pcbStructInfo)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (cbEncoded > MAX_ENCODED_LEN)
+    {
+        SetLastError(CRYPT_E_ASN1_LARGE);
+        return FALSE;
+    }
+
+    if (!(pCryptDecodeObjectEx = CRYPT_GetBuiltinDecoder(dwCertEncodingType,
+     lpszStructType)))
+    {
+        TRACE_(crypt)("OID %s not found or unimplemented, looking for DLL\n",
          debugstr_a(lpszStructType));
+        pCryptDecodeObject = CRYPT_LoadDecoderFunc(dwCertEncodingType,
+         lpszStructType, &hFunc);
+        if (!pCryptDecodeObject)
+            pCryptDecodeObjectEx = CRYPT_LoadDecoderExFunc(dwCertEncodingType,
+             lpszStructType, &hFunc);
+    }
+    if (pCryptDecodeObject)
+        ret = pCryptDecodeObject(dwCertEncodingType, lpszStructType,
+         pbEncoded, cbEncoded, dwFlags, pvStructInfo, pcbStructInfo);
+    else if (pCryptDecodeObjectEx)
+        ret = pCryptDecodeObjectEx(dwCertEncodingType, lpszStructType,
+         pbEncoded, cbEncoded, dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL,
+         pvStructInfo, pcbStructInfo);
+    if (hFunc)
+        CryptFreeOIDFunctionAddress(hFunc, 0);
+    TRACE_(crypt)("returning %d\n", ret);
+    return ret;
+}
+
+BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
+ const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = FALSE;
+    CryptDecodeObjectExFunc decodeFunc;
+    HCRYPTOIDFUNCADDR hFunc = NULL;
+
+    TRACE_(crypt)("(0x%08x, %s, %p, %d, 0x%08x, %p, %p, %p)\n",
+     dwCertEncodingType, debugstr_a(lpszStructType), pbEncoded,
+     cbEncoded, dwFlags, pDecodePara, pvStructInfo, pcbStructInfo);
+
+    if (!pvStructInfo && !pcbStructInfo)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (cbEncoded > MAX_ENCODED_LEN)
+    {
+        SetLastError(CRYPT_E_ASN1_LARGE);
+        return FALSE;
+    }
+
+    SetLastError(NOERROR);
+    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG && pvStructInfo)
+        *(BYTE **)pvStructInfo = NULL;
+    decodeFunc = CRYPT_GetBuiltinDecoder(dwCertEncodingType, lpszStructType);
     if (!decodeFunc)
     {
-        if (!set)
-            set = CryptInitOIDFunctionSet(CRYPT_OID_DECODE_OBJECT_EX_FUNC, 0);
-        CryptGetOIDFunctionAddress(set, dwCertEncodingType, lpszStructType, 0,
-         (void **)&decodeFunc, &hFunc);
+        TRACE_(crypt)("OID %s not found or unimplemented, looking for DLL\n",
+         debugstr_a(lpszStructType));
+        decodeFunc = CRYPT_LoadDecoderExFunc(dwCertEncodingType, lpszStructType,
+         &hFunc);
     }
     if (decodeFunc)
         ret = decodeFunc(dwCertEncodingType, lpszStructType, pbEncoded,
          cbEncoded, dwFlags, pDecodePara, pvStructInfo, pcbStructInfo);
     else
-        SetLastError(ERROR_FILE_NOT_FOUND);
+    {
+        CryptDecodeObjectFunc pCryptDecodeObject =
+         CRYPT_LoadDecoderFunc(dwCertEncodingType, lpszStructType, &hFunc);
+
+        /* Try CryptDecodeObject function.  Don't call CryptDecodeObject
+         * directly, as that could cause an infinite loop.
+         */
+        if (pCryptDecodeObject)
+        {
+            if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+            {
+                ret = pCryptDecodeObject(dwCertEncodingType, lpszStructType,
+                 pbEncoded, cbEncoded, dwFlags, NULL, pcbStructInfo);
+                if (ret && (ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+                 pvStructInfo, pcbStructInfo, *pcbStructInfo)))
+                    ret = pCryptDecodeObject(dwCertEncodingType,
+                     lpszStructType, pbEncoded, cbEncoded, dwFlags,
+                     *(BYTE **)pvStructInfo, pcbStructInfo);
+            }
+            else
+                ret = pCryptDecodeObject(dwCertEncodingType, lpszStructType,
+                 pbEncoded, cbEncoded, dwFlags, pvStructInfo, pcbStructInfo);
+        }
+    }
     if (hFunc)
         CryptFreeOIDFunctionAddress(hFunc, 0);
+    TRACE_(crypt)("returning %d\n", ret);
+    return ret;
+}
+
+BOOL WINAPI PFXIsPFXBlob(CRYPT_DATA_BLOB *pPFX)
+{
+    BOOL ret;
+
+    TRACE_(crypt)("(%p)\n", pPFX);
+
+    /* A PFX blob is an asn.1-encoded sequence, consisting of at least a
+     * version integer of length 1 (3 encoded byes) and at least one other
+     * datum (two encoded bytes), plus at least two bytes for the outer
+     * sequence.  Thus, even an empty PFX blob is at least 7 bytes in length.
+     */
+    if (pPFX->cbData < 7)
+        ret = FALSE;
+    else if (pPFX->pbData[0] == ASN_SEQUENCE)
+    {
+        DWORD len;
+
+        if ((ret = CRYPT_GetLengthIndefinite(pPFX->pbData, pPFX->cbData, &len)))
+        {
+            BYTE lenLen = GET_LEN_BYTES(pPFX->pbData[1]);
+
+            /* Need at least three bytes for the integer version */
+            if (pPFX->cbData < 1 + lenLen + 3)
+                ret = FALSE;
+            else if (pPFX->pbData[1 + lenLen] != ASN_INTEGER || /* Tag */
+             pPFX->pbData[1 + lenLen + 1] != 1 ||          /* Definite length */
+             pPFX->pbData[1 + lenLen + 2] != 3)            /* PFX version */
+                ret = FALSE;
+        }
+    }
+    else
+        ret = FALSE;
     return ret;
 }
+
+HCERTSTORE WINAPI PFXImportCertStore(CRYPT_DATA_BLOB *pPFX, LPCWSTR szPassword,
+ DWORD dwFlags)
+{
+    FIXME_(crypt)("(%p, %p, %08x): stub\n", pPFX, szPassword, dwFlags);
+    return NULL;
+}