- implement encoding and decoding of enumerated types, unsigned
  integers, octet strings, and bit strings
- correct length handling
- the usual tests

diff --git a/dlls/crypt32/encode.c b/dlls/crypt32/encode.c
index 1a6c277..a97fbe0 100644
--- a/dlls/crypt32/encode.c
+++ b/dlls/crypt32/encode.c
@@ -27,6 +27,9 @@
 #include "wine/debug.h"
 
 /* a few asn.1 tags we need */
+#define ASN_BITSTRING       (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x03)
+#define ASN_OCTETSTRING     (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x04)
+#define ASN_ENUMERATED      (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x0a)
 #define ASN_SETOF           (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x11)
 #define ASN_NUMERICSTRING   (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x12)
 #define ASN_PRINTABLESTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x13)
@@ -353,10 +356,52 @@
     return ret;
 }
 
+static BOOL CRYPT_EncodeLen(DWORD len, BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    DWORD bytesNeeded, significantBytes = 0;
+
+    if (len <= 0x7f)
+        bytesNeeded = 1;
+    else
+    {
+        DWORD temp;
+
+        for (temp = len, significantBytes = sizeof(temp); !(temp & 0xff000000);
+         temp <<= 8, significantBytes--)
+            ;
+        bytesNeeded = significantBytes + 1;
+    }
+    if (!pbEncoded)
+    {
+        *pcbEncoded = bytesNeeded;
+        return TRUE;
+    }
+    if (*pcbEncoded < bytesNeeded)
+    {
+        SetLastError(ERROR_MORE_DATA);
+        return FALSE;
+    }
+    if (len <= 0x7f)
+        *pbEncoded = (BYTE)len;
+    else
+    {
+        DWORD i;
+
+        *pbEncoded++ = significantBytes | 0x80;
+        for (i = 0; i < significantBytes; i++)
+        {
+            *(pbEncoded + significantBytes - i - 1) = (BYTE)(len & 0xff);
+            len >>= 8;
+        }
+    }
+    *pcbEncoded = bytesNeeded;
+    return TRUE;
+}
+
 static BOOL WINAPI CRYPT_AsnEncodeOid(DWORD dwCertEncodingType,
  LPCSTR pszObjId, BYTE *pbEncoded, DWORD *pcbEncoded)
 {
-    DWORD bytesNeeded = 2;
+    DWORD bytesNeeded = 0, lenBytes;
     BOOL ret = TRUE;
     int firstPos = 0;
     BYTE firstByte = 0;
@@ -401,7 +446,11 @@
                 return FALSE;
             }
         }
+        CRYPT_EncodeLen(bytesNeeded, NULL, &lenBytes);
     }
+    else
+        lenBytes = 1;
+    bytesNeeded += 1 + lenBytes;
     if (pbEncoded)
     {
         if (*pbEncoded < bytesNeeded)
@@ -412,7 +461,8 @@
         else
         {
             *pbEncoded++ = ASN_OBJECTIDENTIFIER;
-            *pbEncoded++ = bytesNeeded - 2;
+            CRYPT_EncodeLen(bytesNeeded - 1 - lenBytes, pbEncoded, &lenBytes);
+            pbEncoded += lenBytes;
             if (pszObjId)
             {
                 const char *ptr;
@@ -461,22 +511,22 @@
  CERT_NAME_VALUE *value, BYTE *pbEncoded, DWORD *pcbEncoded)
 {
     BYTE tag;
-    DWORD bytesNeeded;
+    DWORD bytesNeeded, lenBytes, encodedLen;
     BOOL ret = TRUE;
 
     switch (value->dwValueType)
     {
     case CERT_RDN_NUMERIC_STRING:
         tag = ASN_NUMERICSTRING;
-        bytesNeeded = 2 + value->Value.cbData;
+        encodedLen = value->Value.cbData;
         break;
     case CERT_RDN_PRINTABLE_STRING:
         tag = ASN_PRINTABLESTRING;
-        bytesNeeded = 2 + value->Value.cbData;
+        encodedLen = value->Value.cbData;
         break;
     case CERT_RDN_IA5_STRING:
         tag = ASN_IA5STRING;
-        bytesNeeded = 2 + value->Value.cbData;
+        encodedLen = value->Value.cbData;
         break;
     case CERT_RDN_ANY_TYPE:
         /* explicitly disallowed */
@@ -486,6 +536,8 @@
         FIXME("String type %ld unimplemented\n", value->dwValueType);
         return FALSE;
     }
+    CRYPT_EncodeLen(encodedLen, NULL, &lenBytes);
+    bytesNeeded = 1 + lenBytes + encodedLen;
     if (pbEncoded)
     {
         if (*pcbEncoded < bytesNeeded)
@@ -496,7 +548,8 @@
         else
         {
             *pbEncoded++ = tag;
-            *pbEncoded++ = bytesNeeded - 2;
+            CRYPT_EncodeLen(encodedLen, pbEncoded, &lenBytes);
+            pbEncoded += lenBytes;
             switch (value->dwValueType)
             {
             case CERT_RDN_NUMERIC_STRING:
@@ -513,10 +566,9 @@
 static BOOL WINAPI CRYPT_AsnEncodeRdnAttr(DWORD dwCertEncodingType,
  CERT_RDN_ATTR *attr, BYTE *pbEncoded, DWORD *pcbEncoded)
 {
-    DWORD bytesNeeded, size;
+    DWORD bytesNeeded = 0, lenBytes, size;
     BOOL ret;
 
-    bytesNeeded = 2; /* tag and len */
     ret = CRYPT_AsnEncodeOid(dwCertEncodingType, attr->pszObjId, NULL, &size);
     if (ret)
     {
@@ -529,6 +581,8 @@
         if (ret)
         {
             bytesNeeded += size;
+            CRYPT_EncodeLen(bytesNeeded, NULL, &lenBytes);
+            bytesNeeded += 1 + lenBytes;
             if (pbEncoded)
             {
                 if (*pcbEncoded < bytesNeeded)
@@ -539,14 +593,16 @@
                 else
                 {
                     *pbEncoded++ = ASN_CONSTRUCTOR | ASN_SEQUENCE;
-                    *pbEncoded++ = bytesNeeded - 2;
-                    size = bytesNeeded - 2;
+                    CRYPT_EncodeLen(bytesNeeded - lenBytes - 1, pbEncoded,
+                     &lenBytes);
+                    pbEncoded += lenBytes;
+                    size = bytesNeeded - 1 - lenBytes;
                     ret = CRYPT_AsnEncodeOid(dwCertEncodingType, attr->pszObjId,
                      pbEncoded, &size);
                     if (ret)
                     {
                         pbEncoded += size;
-                        size = bytesNeeded - 2 - size;
+                        size = bytesNeeded - 1 - lenBytes - size;
                         ret = CRYPT_AsnEncodeNameValue(dwCertEncodingType,
                          (CERT_NAME_VALUE *)&attr->dwValueType, pbEncoded,
                          &size);
@@ -574,7 +630,7 @@
 static BOOL WINAPI CRYPT_AsnEncodeRdn(DWORD dwCertEncodingType, CERT_RDN *rdn,
  BYTE *pbEncoded, DWORD *pcbEncoded)
 {
-    DWORD bytesNeeded, i;
+    DWORD bytesNeeded = 0, lenBytes, i;
     BOOL ret;
     CRYPT_DER_BLOB *blobs = NULL;
    
@@ -594,7 +650,6 @@
                 ret = FALSE;
         }
     }
-    bytesNeeded = 2; /* tag and len */
     for (i = 0; ret && i < rdn->cRDNAttr; i++)
     {
         ret = CRYPT_AsnEncodeRdnAttr(dwCertEncodingType, &rdn->rgRDNAttr[i],
@@ -602,6 +657,8 @@
         if (ret)
             bytesNeeded += blobs[i].cbData;
     }
+    CRYPT_EncodeLen(bytesNeeded, NULL, &lenBytes);
+    bytesNeeded += 1 + lenBytes;
     if (ret)
     {
         if (pbEncoded)
@@ -628,7 +685,9 @@
                     qsort(blobs, rdn->cRDNAttr, sizeof(CRYPT_DER_BLOB),
                      BLOBComp);
                     *pbEncoded++ = ASN_CONSTRUCTOR | ASN_SETOF;
-                    *pbEncoded++ = (BYTE)bytesNeeded - 2;
+                    CRYPT_EncodeLen(bytesNeeded - lenBytes - 1, pbEncoded,
+                     &lenBytes);
+                    pbEncoded += lenBytes;
                     for (i = 0; ret && i < rdn->cRDNAttr; i++)
                     {
                         memcpy(pbEncoded, blobs[i].pbData, blobs[i].cbData);
@@ -653,7 +712,7 @@
  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
 {
     CERT_NAME_INFO *info = (CERT_NAME_INFO *)pvStructInfo;
-    DWORD bytesNeeded, size, i;
+    DWORD bytesNeeded = 0, lenBytes, size, i;
     BOOL ret;
 
     if (!pvStructInfo)
@@ -667,7 +726,6 @@
         return FALSE;
     }
     TRACE("encoding name with %ld RDNs\n", info->cRDN);
-    bytesNeeded = 2; /* tag and len */
     ret = TRUE;
     for (i = 0; ret && i < info->cRDN; i++)
     {
@@ -676,6 +734,8 @@
         if (ret)
             bytesNeeded += size;
     }
+    CRYPT_EncodeLen(bytesNeeded, NULL, &lenBytes);
+    bytesNeeded += 1 + lenBytes;
     if (ret)
     {
         if (!pbEncoded)
@@ -689,7 +749,8 @@
         if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
             pbEncoded = *(BYTE **)pbEncoded;
         *pbEncoded++ = ASN_CONSTRUCTOR | ASN_SEQUENCE;
-        *pbEncoded++ = (BYTE)bytesNeeded - 2;
+        CRYPT_EncodeLen(bytesNeeded - lenBytes - 1, pbEncoded, &size);
+        pbEncoded += size;
         for (i = 0; ret && i < info->cRDN; i++)
         {
             size = bytesNeeded;
@@ -705,6 +766,95 @@
     return ret;
 }
 
+static BOOL WINAPI CRYPT_AsnEncodeOctets(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
+ PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    CRYPT_DATA_BLOB *blob = (CRYPT_DATA_BLOB *)pvStructInfo;
+    DWORD bytesNeeded, lenBytes;
+
+    if (!pvStructInfo)
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        return FALSE;
+    }
+    /* FIXME: use exception handling to catch bogus pointers */
+    CRYPT_EncodeLen(blob->cbData, NULL, &lenBytes);
+    bytesNeeded = 1 + lenBytes + blob->cbData;
+    if (!pbEncoded)
+    {
+        *pcbEncoded = bytesNeeded;
+        return TRUE;
+    }
+    if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded,
+     bytesNeeded))
+        return FALSE;
+    if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
+        pbEncoded = *(BYTE **)pbEncoded;
+    *pbEncoded++ = ASN_OCTETSTRING;
+    CRYPT_EncodeLen(blob->cbData, pbEncoded, &lenBytes);
+    pbEncoded += lenBytes;
+    if (blob->cbData)
+        memcpy(pbEncoded, blob->pbData, blob->cbData);
+    return TRUE;
+}
+
+static BOOL WINAPI CRYPT_AsnEncodeBits(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
+ PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    CRYPT_BIT_BLOB *blob = (CRYPT_BIT_BLOB *)pvStructInfo;
+    DWORD bytesNeeded, lenBytes, dataBytes;
+    BYTE unusedBits;
+
+    if (!pvStructInfo)
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        return FALSE;
+    }
+    /* FIXME: use exception handling to catch bogus pointers */
+    /* yep, MS allows cUnusedBits to be >= 8 */
+    if (blob->cbData * 8 > blob->cUnusedBits)
+    {
+        dataBytes = (blob->cbData * 8 - blob->cUnusedBits) / 8 + 1;
+        unusedBits = blob->cUnusedBits >= 8 ? blob->cUnusedBits / 8 :
+         blob->cUnusedBits;
+    }
+    else
+    {
+        dataBytes = 0;
+        unusedBits = 0;
+    }
+    CRYPT_EncodeLen(dataBytes + 1, NULL, &lenBytes);
+    bytesNeeded = 1 + lenBytes + dataBytes + 1;
+    if (!pbEncoded)
+    {
+        *pcbEncoded = bytesNeeded;
+        return TRUE;
+    }
+    if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded,
+     bytesNeeded))
+        return FALSE;
+    if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
+        pbEncoded = *(BYTE **)pbEncoded;
+    *pbEncoded++ = ASN_BITSTRING;
+    CRYPT_EncodeLen(dataBytes + 1, pbEncoded, &lenBytes);
+    pbEncoded += lenBytes;
+    *pbEncoded++ = unusedBits;
+    if (dataBytes)
+    {
+        BYTE mask = 0xff << unusedBits;
+
+        if (dataBytes > 1)
+        {
+            memcpy(pbEncoded, blob->pbData, dataBytes - 1);
+            pbEncoded += dataBytes - 1;
+        }
+        *pbEncoded = *(blob->pbData + dataBytes - 1) & mask;
+    }
+    return TRUE;
+}
+
 static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
@@ -775,7 +925,7 @@
  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
 {
-    DWORD significantBytes;
+    DWORD significantBytes, lenBytes;
     BYTE padByte = 0, bytesNeeded;
     BOOL pad = FALSE;
     CRYPT_INTEGER_BLOB *blob = (CRYPT_INTEGER_BLOB *)pvStructInfo;
@@ -815,7 +965,11 @@
             }
         }
     }
-    bytesNeeded = 2 + significantBytes;
+    if (pad)
+        CRYPT_EncodeLen(significantBytes + 1, NULL, &lenBytes);
+    else
+        CRYPT_EncodeLen(significantBytes, NULL, &lenBytes);
+    bytesNeeded = 1 + lenBytes + significantBytes;
     if (pad)
         bytesNeeded++;
     if (!pbEncoded)
@@ -831,16 +985,101 @@
     *pbEncoded++ = ASN_INTEGER;
     if (pad)
     {
-        *pbEncoded++ = significantBytes + 1;
+        CRYPT_EncodeLen(significantBytes + 1, pbEncoded, &lenBytes);
+        pbEncoded += lenBytes;
         *pbEncoded++ = padByte;
     }
     else
-        *pbEncoded++ = significantBytes;
+    {
+        CRYPT_EncodeLen(significantBytes, pbEncoded, &lenBytes);
+        pbEncoded += lenBytes;
+    }
     for (; significantBytes > 0; significantBytes--)
         *(pbEncoded++) = blob->pbData[significantBytes - 1];
     return TRUE;
 }
 
+static BOOL WINAPI CRYPT_AsnEncodeUnsignedInteger(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
+ PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    DWORD significantBytes, lenBytes;
+    BYTE bytesNeeded;
+    BOOL pad = FALSE;
+    CRYPT_INTEGER_BLOB *blob = (CRYPT_INTEGER_BLOB *)pvStructInfo;
+
+    if (!pvStructInfo)
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        return FALSE;
+    }
+
+    /* FIXME: use exception handling to protect against bogus pointers */
+    significantBytes = blob->cbData;
+    if (significantBytes)
+    {
+        /* positive, lop off leading (little-endian) zeroes */
+        for (; significantBytes > 0 && !blob->pbData[significantBytes - 1];
+         significantBytes--)
+            ;
+        if (blob->pbData[significantBytes - 1] > 0x7f)
+            pad = TRUE;
+    }
+    if (pad)
+        CRYPT_EncodeLen(significantBytes + 1, NULL, &lenBytes);
+    else
+        CRYPT_EncodeLen(significantBytes, NULL, &lenBytes);
+    bytesNeeded = 1 + lenBytes + significantBytes;
+    if (pad)
+        bytesNeeded++;
+    if (!pbEncoded)
+    {
+        *pcbEncoded = bytesNeeded;
+        return TRUE;
+    }
+    if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded,
+     bytesNeeded))
+        return FALSE;
+    if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
+        pbEncoded = *(BYTE **)pbEncoded;
+    *pbEncoded++ = ASN_INTEGER;
+    if (pad)
+    {
+        CRYPT_EncodeLen(significantBytes + 1, pbEncoded, &lenBytes);
+        pbEncoded += lenBytes;
+        *pbEncoded++ = 0;
+    }
+    else
+    {
+        CRYPT_EncodeLen(significantBytes, pbEncoded, &lenBytes);
+        pbEncoded += lenBytes;
+    }
+    for (; significantBytes > 0; significantBytes--)
+        *(pbEncoded++) = blob->pbData[significantBytes - 1];
+    return TRUE;
+}
+
+static BOOL WINAPI CRYPT_AsnEncodeEnumerated(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
+ PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    CRYPT_INTEGER_BLOB blob;
+    BOOL ret;
+
+    /* Encode as an unsigned integer, then change the tag to enumerated */
+    blob.cbData = sizeof(DWORD);
+    blob.pbData = (BYTE *)pvStructInfo;
+    ret = CRYPT_AsnEncodeUnsignedInteger(dwCertEncodingType,
+     X509_MULTI_BYTE_UINT, &blob, dwFlags, pEncodePara, pbEncoded, pcbEncoded);
+    if (ret && pbEncoded)
+    {
+        if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
+            pbEncoded = *(BYTE **)pbEncoded;
+        pbEncoded[0] = ASN_ENUMERATED;
+    }
+    return ret;
+}
+
 static BOOL WINAPI CRYPT_AsnEncodeUtcTime(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
@@ -958,7 +1197,7 @@
     HMODULE lib = NULL;
     CryptEncodeObjectExFunc encodeFunc = NULL;
 
-    TRACE("(0x%08lx, %s, %p, 0x%08lx, %p, %p, %p): semi-stub\n",
+    TRACE("(0x%08lx, %s, %p, 0x%08lx, %p, %p, %p)\n",
      dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
      "(integer value)", pvStructInfo, dwFlags, pEncodePara, pbEncoded,
      pcbEncoded);
@@ -983,12 +1222,25 @@
         case (WORD)X509_NAME:
             encodeFunc = CRYPT_AsnEncodeName;
             break;
+        case (WORD)X509_OCTET_STRING:
+            encodeFunc = CRYPT_AsnEncodeOctets;
+            break;
+        case (WORD)X509_BITS:
+        case (WORD)X509_KEY_USAGE:
+            encodeFunc = CRYPT_AsnEncodeBits;
+            break;
         case (WORD)X509_INTEGER:
             encodeFunc = CRYPT_AsnEncodeInt;
             break;
         case (WORD)X509_MULTI_BYTE_INTEGER:
             encodeFunc = CRYPT_AsnEncodeInteger;
             break;
+        case (WORD)X509_MULTI_BYTE_UINT:
+            encodeFunc = CRYPT_AsnEncodeUnsignedInteger;
+            break;
+        case (WORD)X509_ENUMERATED:
+            encodeFunc = CRYPT_AsnEncodeEnumerated;
+            break;
         case (WORD)X509_CHOICE_OF_TIME:
             encodeFunc = CRYPT_AsnEncodeChoiceOfTime;
             break;
@@ -1001,6 +1253,12 @@
     }
     else if (!strcmp(lpszStructType, szOID_RSA_signingTime))
         encodeFunc = CRYPT_AsnEncodeUtcTime;
+    else if (!strcmp(lpszStructType, szOID_CRL_REASON_CODE))
+        encodeFunc = CRYPT_AsnEncodeEnumerated;
+    else if (!strcmp(lpszStructType, szOID_KEY_USAGE))
+        encodeFunc = CRYPT_AsnEncodeBits;
+    else if (!strcmp(lpszStructType, szOID_SUBJECT_KEY_IDENTIFIER))
+        encodeFunc = CRYPT_AsnEncodeOctets;
     if (!encodeFunc)
         encodeFunc = (CryptEncodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType,
          lpszStructType, "CryptEncodeObjectEx", &lib);
@@ -1055,6 +1313,68 @@
     return ret;
 }
 
+/* Gets the number of length bytes from the given (leading) length byte */
+#define GET_LEN_BYTES(b) ((b) <= 0x7f ? 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.
+ */
+static BOOL WINAPI CRYPT_GetLen(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD *len)
+{
+    BOOL ret;
+
+    if (cbEncoded <= 1)
+    {
+        SetLastError(CRYPT_E_ASN1_EOD);
+        ret = FALSE;
+    }
+    else if (pbEncoded[1] <= 0x7f)
+    {
+        *len = pbEncoded[1];
+        ret = TRUE;
+    }
+    else
+    {
+        BYTE lenLen = GET_LEN_BYTES(pbEncoded[1]);
+
+        if (lenLen > sizeof(DWORD))
+        {
+            SetLastError(CRYPT_E_ASN1_LARGE);
+            ret = FALSE;
+        }
+        else if (lenLen + 2 > cbEncoded)
+        {
+            SetLastError(CRYPT_E_ASN1_CORRUPT);
+            ret = FALSE;
+        }
+        else
+        {
+            DWORD out = 0;
+
+            pbEncoded += 2;
+            while (lenLen--)
+            {
+                out <<= 8;
+                out |= *pbEncoded++;
+            }
+            if (out + lenLen + 1 > cbEncoded)
+            {
+                SetLastError(CRYPT_E_ASN1_EOD);
+                ret = FALSE;
+            }
+            else
+            {
+                *len = out;
+                ret = TRUE;
+            }
+        }
+    }
+    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
@@ -1092,22 +1412,18 @@
  DWORD *pcbObjId)
 {
     BOOL ret = TRUE;
-    DWORD bytesNeeded;
+    DWORD bytesNeeded, dataLen;
+    BYTE lenBytes;
 
-    /* cbEncoded is an upper bound on the number of bytes, not the actual
-     * count: check the count for sanity.
-     */
-    if (cbEncoded <= 1 || pbEncoded[1] > cbEncoded - 2)
-    {
-        SetLastError(CRYPT_E_ASN1_EOD);
+    if (!CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))
         return FALSE;
-    }
     if (pbEncoded[0] != ASN_OBJECTIDENTIFIER)
     {
         SetLastError(CRYPT_E_ASN1_BADTAG);
         return FALSE;
     }
-    if (pbEncoded[1])
+    lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+    if (dataLen)
     {
         /* The largest possible string for the first two components is 2.175
          * (= 2 * 40 + 175 = 255), so this is big enough.
@@ -1115,22 +1431,24 @@
         char firstTwo[6];
         const BYTE *ptr;
 
-        snprintf(firstTwo, sizeof(firstTwo), "%d.%d", pbEncoded[2] / 40,
-         pbEncoded[2] - (pbEncoded[2] / 40) * 40);
+        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 + 3; ret && ptr - pbEncoded - 2 < pbEncoded[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 - 2 < pbEncoded[1] && (*ptr & 0x80))
+            while (ptr - pbEncoded - 1 - lenBytes < dataLen && (*ptr & 0x80))
             {
                 val <<= 7;
                 val |= *ptr & 0x7f;
                 ptr++;
             }
-            if (ptr - pbEncoded - 2 >= pbEncoded[1] || (*ptr & 0x80))
+            if (ptr - pbEncoded - 1 - lenBytes >= dataLen || (*ptr & 0x80))
             {
                 SetLastError(CRYPT_E_ASN1_CORRUPT);
                 ret = FALSE;
@@ -1153,15 +1471,16 @@
         }
         else
         {
-            sprintf(pszObjId, "%d.%d", pbEncoded[2] / 40,
-             pbEncoded[2] - (pbEncoded[2] / 40) * 40);
+            sprintf(pszObjId, "%d.%d", pbEncoded[1 + lenBytes] / 40,
+             pbEncoded[1 + lenBytes] - (pbEncoded[1 + lenBytes] / 40) * 40);
             pszObjId += strlen(pszObjId);
-            for (ptr = pbEncoded + 3; ret && ptr - pbEncoded - 2 < pbEncoded[1];
-             )
+            for (ptr = pbEncoded + 2 + lenBytes; ret &&
+             ptr - pbEncoded - 1 - lenBytes < dataLen; )
             {
                 int val = 0;
 
-                while (ptr - pbEncoded - 2 < pbEncoded[1] && (*ptr & 0x80))
+                while (ptr - pbEncoded - 1 - lenBytes < dataLen &&
+                 (*ptr & 0x80))
                 {
                     val <<= 7;
                     val |= *ptr & 0x7f;
@@ -1188,17 +1507,13 @@
  const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, CERT_NAME_VALUE *value,
  DWORD *pcbValue)
 {
-    DWORD bytesNeeded;
+    DWORD bytesNeeded, dataLen;
     BOOL ret = TRUE;
+    BYTE lenBytes;
 
-    /* cbEncoded is an upper bound on the number of bytes, not the actual
-     * count: check the count for sanity.
-     */
-    if (cbEncoded <= 1 || pbEncoded[1] > cbEncoded - 2)
-    {
-        SetLastError(CRYPT_E_ASN1_EOD);
+    if (!CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))
         return FALSE;
-    }
+    lenBytes = GET_LEN_BYTES(pbEncoded[1]);
     switch (pbEncoded[0])
     {
     case ASN_NUMERICSTRING:
@@ -1211,7 +1526,7 @@
         return FALSE;
     }
     bytesNeeded = sizeof(CERT_NAME_VALUE);
-    if (pbEncoded[1])
+    if (dataLen)
     {
         switch (pbEncoded[0])
         {
@@ -1219,7 +1534,7 @@
         case ASN_PRINTABLESTRING:
         case ASN_IA5STRING:
             if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG))
-                bytesNeeded += pbEncoded[1];
+                bytesNeeded += dataLen;
             break;
         }
     }
@@ -1247,16 +1562,16 @@
         value->dwValueType = CERT_RDN_IA5_STRING;
         break;
     }
-    if (pbEncoded[1])
+    if (dataLen)
     {
         switch (pbEncoded[0])
         {
         case ASN_NUMERICSTRING:
         case ASN_PRINTABLESTRING:
         case ASN_IA5STRING:
-            value->Value.cbData = pbEncoded[1];
+            value->Value.cbData = dataLen;
             if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
-                value->Value.pbData = (BYTE *)pbEncoded + 2;
+                value->Value.pbData = (BYTE *)pbEncoded + 1 + lenBytes;
             else
             {
                 if (!value->Value.pbData)
@@ -1265,7 +1580,8 @@
                     ret = FALSE;
                 }
                 else
-                    memcpy(value->Value.pbData, pbEncoded + 2, pbEncoded[1]);
+                    memcpy(value->Value.pbData, pbEncoded + 1 + lenBytes,
+                     dataLen);
             }
             break;
         }
@@ -1283,13 +1599,15 @@
  DWORD *pcbAttr)
 {
     BOOL ret = TRUE;
-    DWORD bytesNeeded, size;
+    DWORD bytesNeeded, dataLen, size;
+    BYTE lenBytes;
 
-    /* cbEncoded is an upper bound on the number of bytes, not the actual
-     * count: check the count for sanity.  It must be at least 6, two for the
-     * tag and length for the RDN_ATTR, two for the OID, and two for the string.
+    if (!CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))
+        return FALSE;
+    /* The data length must be at least 4, two for the tag and length for the
+     * OID, and two for the string (assuming both have short-form lengths.)
      */
-    if (cbEncoded < 6 || pbEncoded[1] < 4 || pbEncoded[1] > cbEncoded - 2)
+    if (dataLen < 4)
     {
         SetLastError(CRYPT_E_ASN1_EOD);
         return FALSE;
@@ -1300,21 +1618,30 @@
         return FALSE;
     }
     bytesNeeded = sizeof(CERT_RDN_ATTR);
-    ret = CRYPT_AsnDecodeOid(dwCertEncodingType, pbEncoded + 2,
-     cbEncoded - 2, dwFlags, NULL, &size);
+    lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+    ret = CRYPT_AsnDecodeOid(dwCertEncodingType, pbEncoded + 1 + lenBytes,
+     cbEncoded - 1 - lenBytes, dwFlags, NULL, &size);
     if (ret)
     {
         /* ugly: need to know the size of the next element of the sequence,
          * so get it directly
          */
-        BYTE objIdLen = pbEncoded[3];
+        DWORD objIdOfset = 1 + lenBytes, objIdLen, nameValueOffset = 0;
 
+        ret = CRYPT_GetLen(pbEncoded + objIdOfset, cbEncoded - objIdOfset,
+         &objIdLen);
         bytesNeeded += size;
         /* hack: like encoding, this takes advantage of the fact that the rest
          * of the structure is identical to a CERT_NAME_VALUE.
          */
-        ret = CRYPT_AsnDecodeNameValue(dwCertEncodingType, pbEncoded + 4 +
-         objIdLen, cbEncoded - 4 - objIdLen, dwFlags, NULL, &size);
+        if (ret)
+        {
+            nameValueOffset = objIdOfset + objIdLen + 1 +
+             GET_LEN_BYTES(pbEncoded[objIdOfset]);
+            ret = CRYPT_AsnDecodeNameValue(dwCertEncodingType,
+             pbEncoded + nameValueOffset, cbEncoded - nameValueOffset, dwFlags,
+             NULL, &size);
+        }
         if (ret)
         {
             bytesNeeded += size;
@@ -1338,7 +1665,7 @@
                  */
                 size = bytesNeeded;
                 ret = CRYPT_AsnDecodeNameValue(dwCertEncodingType,
-                 pbEncoded + 4 + objIdLen, cbEncoded - 4 - objIdLen,
+                 pbEncoded + nameValueOffset, cbEncoded - nameValueOffset,
                  dwFlags, (CERT_NAME_VALUE *)&attr->dwValueType, &size);
                 if (ret)
                 {
@@ -1355,8 +1682,8 @@
                             attr->pszObjId = originalData;
                         size = bytesNeeded - size;
                         ret = CRYPT_AsnDecodeOid(dwCertEncodingType,
-                         pbEncoded + 2, cbEncoded - 2, dwFlags, attr->pszObjId,
-                         &size);
+                         pbEncoded + objIdOfset, cbEncoded - objIdOfset,
+                         dwFlags, attr->pszObjId, &size);
                     }
                     else
                         attr->pszObjId = NULL;
@@ -1372,36 +1699,38 @@
  DWORD *pcbRdn)
 {
     BOOL ret = TRUE;
-    DWORD bytesNeeded, cRDNAttr = 0;
+    DWORD bytesNeeded, dataLen, cRDNAttr = 0;
+    BYTE lenBytes;
 
-    /* cbEncoded is an upper bound on the number of bytes, not the actual
-     * count: check the count for sanity.
-     */
-    if (cbEncoded <= 1 || pbEncoded[1] > cbEncoded - 2)
-    {
-        SetLastError(CRYPT_E_ASN1_EOD);
+    if (!CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))
         return FALSE;
-    }
     if (pbEncoded[0] != (ASN_CONSTRUCTOR | ASN_SETOF))
     {
         SetLastError(CRYPT_E_ASN1_BADTAG);
         return FALSE;
     }
+    lenBytes = GET_LEN_BYTES(pbEncoded[1]);
     bytesNeeded = sizeof(CERT_RDN);
-    if (pbEncoded[1])
+    if (dataLen)
     {
         const BYTE *ptr;
         DWORD size;
 
-        for (ptr = pbEncoded + 2; ret && ptr - pbEncoded - 2 < pbEncoded[1]; )
+        for (ptr = pbEncoded + 1 + lenBytes; ret &&
+         ptr - pbEncoded - 1 - lenBytes < dataLen; )
         {
             ret = CRYPT_AsnDecodeRdnAttr(dwCertEncodingType, ptr,
              cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size);
             if (ret)
             {
+                DWORD nextLen;
+
                 cRDNAttr++;
                 bytesNeeded += size;
-                ptr += ptr[1] + 2;
+                ret = CRYPT_GetLen(ptr, cbEncoded - (ptr - pbEncoded),
+                 &nextLen);
+                if (ret)
+                    ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
             }
         }
     }
@@ -1431,8 +1760,8 @@
             rdn->rgRDNAttr = (CERT_RDN_ATTR *)((BYTE *)rdn + sizeof(CERT_RDN));
             nextData = (BYTE *)rdn->rgRDNAttr +
              rdn->cRDNAttr * sizeof(CERT_RDN_ATTR);
-            for (i = 0, ptr = pbEncoded + 2; ret && i < cRDNAttr &&
-             ptr - pbEncoded - 2 < pbEncoded[1]; i++)
+            for (i = 0, ptr = pbEncoded + 1 + lenBytes; ret && i < cRDNAttr &&
+             ptr - pbEncoded - 1 - lenBytes < dataLen; i++)
             {
                 rdn->rgRDNAttr[i].Value.pbData = nextData;
                 size = bytesNeeded;
@@ -1441,6 +1770,8 @@
                  &size);
                 if (ret)
                 {
+                    DWORD nextLen;
+
                     bytesNeeded -= size;
                     /* If dwFlags & CRYPT_DECODE_NOCOPY_FLAG, the data may not
                      * have been copied.
@@ -1453,7 +1784,10 @@
                      */
                     if ((const BYTE *)rdn->rgRDNAttr[i].pszObjId == nextData)
                         nextData += strlen(rdn->rgRDNAttr[i].pszObjId) + 1;
-                    ptr += ptr[1] + 2;
+                    ret = CRYPT_GetLen(ptr, cbEncoded - (ptr - pbEncoded),
+                     &nextLen);
+                    if (ret)
+                        ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
                 }
             }
         }
@@ -1466,38 +1800,43 @@
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
     BOOL ret = TRUE;
-    DWORD bytesNeeded, cRDN = 0;
+    DWORD bytesNeeded, dataLen, cRDN = 0;
+    BYTE lenBytes;
 
-    if (!pbEncoded || !cbEncoded)
+    if (!pbEncoded)
     {
-        SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError(CRYPT_E_ASN1_EOD);
         return FALSE;
     }
+    if (!CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))
+        return FALSE;
     if (pbEncoded[0] != (ASN_CONSTRUCTOR | ASN_SEQUENCEOF))
     {
         SetLastError(CRYPT_E_ASN1_BADTAG);
         return FALSE;
     }
-    if (cbEncoded <= 1)
-    {
-        SetLastError(CRYPT_E_ASN1_EOD);
-        return FALSE;
-    }
+    lenBytes = GET_LEN_BYTES(pbEncoded[1]);
     bytesNeeded = sizeof(CERT_NAME_INFO);
-    if (pbEncoded[1])
+    if (dataLen)
     {
         const BYTE *ptr;
         DWORD size;
 
-        for (ptr = pbEncoded + 2; ret && ptr - pbEncoded - 2 < pbEncoded[1]; )
+        for (ptr = pbEncoded + 1 + lenBytes; ret && ptr - pbEncoded - 1 -
+         lenBytes < dataLen; )
         {
             ret = CRYPT_AsnDecodeRdn(dwCertEncodingType, ptr,
              cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size);
             if (ret)
             {
+                DWORD nextLen;
+
                 cRDN++;
                 bytesNeeded += size;
-                ptr += ptr[1] + 2;
+                ret = CRYPT_GetLen(ptr, cbEncoded - (ptr - pbEncoded),
+                 &nextLen);
+                if (ret)
+                    ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
             }
         }
     }
@@ -1528,8 +1867,8 @@
             info->rgRDN = (CERT_RDN *)((BYTE *)pvStructInfo +
              sizeof(CERT_NAME_INFO));
             nextData = (BYTE *)info->rgRDN + info->cRDN * sizeof(CERT_RDN);
-            for (i = 0, ptr = pbEncoded + 2; ret && i < cRDN &&
-             ptr - pbEncoded - 2 < pbEncoded[1]; i++)
+            for (i = 0, ptr = pbEncoded + 1 + lenBytes; ret && i < cRDN &&
+             ptr - pbEncoded - 1 - lenBytes < dataLen; i++)
             {
                 info->rgRDN[i].rgRDNAttr = (CERT_RDN_ATTR *)nextData;
                 size = bytesNeeded;
@@ -1538,9 +1877,14 @@
                  &size);
                 if (ret)
                 {
+                    DWORD nextLen;
+
                     nextData += size;
                     bytesNeeded -= size;
-                    ptr += ptr[1] + 2;
+                    ret = CRYPT_GetLen(ptr, cbEncoded - (ptr - pbEncoded),
+                     &nextLen);
+                    if (ret)
+                        ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
                 }
             }
         }
@@ -1548,6 +1892,106 @@
     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)
+{
+    CRYPT_DATA_BLOB *blob;
+    DWORD bytesNeeded, dataLen;
+
+    if (!pbEncoded)
+    {
+        SetLastError(CRYPT_E_ASN1_EOD);
+        return FALSE;
+    }
+    if (!CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))
+        return FALSE;
+    if (pbEncoded[0] != ASN_OCTETSTRING)
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        return FALSE;
+    }
+    if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+        bytesNeeded = sizeof(CRYPT_DATA_BLOB);
+    else
+        bytesNeeded = dataLen + sizeof(CRYPT_DATA_BLOB);
+    if (!pvStructInfo)
+    {
+        *pcbStructInfo = bytesNeeded;
+        return TRUE;
+    }
+    if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
+     pcbStructInfo, bytesNeeded))
+        return FALSE;
+    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+        pvStructInfo = *(BYTE **)pvStructInfo;
+    blob = (CRYPT_DATA_BLOB *)pvStructInfo;
+    blob->cbData = dataLen;
+    if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+        blob->pbData = (BYTE *)pbEncoded + 1 + GET_LEN_BYTES(pbEncoded[1]);
+    else
+    {
+        blob->pbData = (BYTE *)pvStructInfo + sizeof(CRYPT_DATA_BLOB);
+        if (blob->cbData)
+            memcpy(blob->pbData, pbEncoded + 1 + GET_LEN_BYTES(pbEncoded[1]),
+             blob->cbData);
+    }
+    return TRUE;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeBits(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    CRYPT_BIT_BLOB *blob;
+    DWORD bytesNeeded, dataLen;
+
+    if (!pbEncoded)
+    {
+        SetLastError(CRYPT_E_ASN1_EOD);
+        return FALSE;
+    }
+    if (!CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))
+        return FALSE;
+    if (pbEncoded[0] != ASN_BITSTRING)
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        return FALSE;
+    }
+    if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+        bytesNeeded = sizeof(CRYPT_BIT_BLOB);
+    else
+        bytesNeeded = dataLen - 1 + sizeof(CRYPT_BIT_BLOB);
+    if (!pvStructInfo)
+    {
+        *pcbStructInfo = bytesNeeded;
+        return TRUE;
+    }
+    if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
+     pcbStructInfo, bytesNeeded))
+        return FALSE;
+    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+        pvStructInfo = *(BYTE **)pvStructInfo;
+    blob = (CRYPT_BIT_BLOB *)pvStructInfo;
+    blob->cbData = dataLen - 1;
+    blob->cUnusedBits = *(pbEncoded + 1 + GET_LEN_BYTES(pbEncoded[1]));
+    if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+        blob->pbData = (BYTE *)pbEncoded + 2 + GET_LEN_BYTES(pbEncoded[1]);
+    else
+    {
+        blob->pbData = (BYTE *)pvStructInfo + sizeof(CRYPT_BIT_BLOB);
+        if (blob->cbData)
+        {
+            BYTE mask = 0xff << blob->cUnusedBits;
+
+            memcpy(blob->pbData, pbEncoded + 2 + GET_LEN_BYTES(pbEncoded[1]),
+             blob->cbData);
+            blob->pbData[blob->cbData - 1] &= mask;
+        }
+    }
+    return TRUE;
+}
+
 static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
@@ -1609,30 +2053,24 @@
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
-    DWORD bytesNeeded;
+    DWORD bytesNeeded, dataLen;
+    BYTE lenBytes;
     CRYPT_INTEGER_BLOB *blob;
 
-    if (!pbEncoded || !cbEncoded)
+    if (!pbEncoded)
     {
         SetLastError(CRYPT_E_ASN1_EOD);
         return FALSE;
     }
+    if (!CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))
+        return FALSE;
     if (pbEncoded[0] != ASN_INTEGER)
     {
         SetLastError(CRYPT_E_ASN1_BADTAG);
         return FALSE;
     }
-    if (cbEncoded <= 1)
-    {
-        SetLastError(CRYPT_E_ASN1_EOD);
-        return FALSE;
-    }
-    if (pbEncoded[1] > cbEncoded)
-    {
-        SetLastError(CRYPT_E_ASN1_EOD);
-        return FALSE;
-    }
-    bytesNeeded = pbEncoded[1] + sizeof(CRYPT_INTEGER_BLOB);
+    lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+    bytesNeeded = dataLen + sizeof(CRYPT_INTEGER_BLOB);
     if (!pvStructInfo)
     {
         *pcbStructInfo = bytesNeeded;
@@ -1644,18 +2082,125 @@
     if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
         pvStructInfo = *(BYTE **)pvStructInfo;
     blob = (CRYPT_INTEGER_BLOB *)pvStructInfo;
-    blob->cbData = pbEncoded[1];
+    blob->cbData = dataLen;
     blob->pbData = (BYTE *)pvStructInfo + sizeof(CRYPT_INTEGER_BLOB);
     if (blob->cbData)
     {
         DWORD i;
 
         for (i = 0; i < blob->cbData; i++)
-            blob->pbData[i] = *(pbEncoded + 2 + pbEncoded[1] - i - 1);
+            blob->pbData[i] = *(pbEncoded + 1 + lenBytes + dataLen - i - 1);
     }
     return TRUE;
 }
 
+static BOOL WINAPI CRYPT_AsnDecodeUnsignedInteger(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    DWORD bytesNeeded, dataLen;
+    BYTE lenBytes;
+    CRYPT_INTEGER_BLOB *blob;
+
+    if (!pbEncoded)
+    {
+        SetLastError(CRYPT_E_ASN1_EOD);
+        return FALSE;
+    }
+    if (!CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))
+        return FALSE;
+    if (pbEncoded[0] != ASN_INTEGER)
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        return FALSE;
+    }
+    lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+    bytesNeeded = dataLen + sizeof(CRYPT_INTEGER_BLOB);
+    if (!pvStructInfo)
+    {
+        *pcbStructInfo = bytesNeeded;
+        return TRUE;
+    }
+    if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
+     pcbStructInfo, bytesNeeded))
+        return FALSE;
+    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+        pvStructInfo = *(BYTE **)pvStructInfo;
+    blob = (CRYPT_INTEGER_BLOB *)pvStructInfo;
+    blob->cbData = dataLen;
+    blob->pbData = (BYTE *)pvStructInfo + sizeof(CRYPT_INTEGER_BLOB);
+    /* 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 + pbEncoded[1] - i -
+             1);
+    }
+    return TRUE;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeEnumerated(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    unsigned int val = 0, i;
+
+    /* Based on CRYPT_AsnDecodeInt, but interprets as unsigned */
+    if (!pbEncoded || !cbEncoded)
+    {
+        SetLastError(CRYPT_E_ASN1_EOD);
+        return FALSE;
+    }
+    if (!pvStructInfo)
+    {
+        *pcbStructInfo = sizeof(int);
+        return TRUE;
+    }
+    if (pbEncoded[0] != ASN_ENUMERATED)
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        return FALSE;
+    }
+    if (cbEncoded <= 1)
+    {
+        SetLastError(CRYPT_E_ASN1_EOD);
+        return FALSE;
+    }
+    if (pbEncoded[1] == 0)
+    {
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        return FALSE;
+    }
+    /* 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 (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
+     pcbStructInfo, sizeof(unsigned int)))
+        return FALSE;
+    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+        pvStructInfo = *(BYTE **)pvStructInfo;
+    memcpy(pvStructInfo, &val, sizeof(unsigned int));
+    return TRUE;
+}
+
 #define CRYPT_TIME_GET_DIGITS(pbEncoded, len, numDigits, word) \
  do { \
     BYTE i; \
@@ -1738,7 +2283,7 @@
 
     if (!pbEncoded || !cbEncoded)
     {
-        SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError(CRYPT_E_ASN1_EOD);
         return FALSE;
     }
     if (!pvStructInfo)
@@ -1756,6 +2301,12 @@
         SetLastError(CRYPT_E_ASN1_EOD);
         return FALSE;
     }
+    if (pbEncoded[1] > 0x7f)
+    {
+        /* long-form date strings really can't be valid */
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        return FALSE;
+    }
     len = pbEncoded[1];
     /* FIXME: magic # */
     if (len < 10)
@@ -1806,7 +2357,7 @@
 
     if (!pbEncoded || !cbEncoded)
     {
-        SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError(CRYPT_E_ASN1_EOD);
         return FALSE;
     }
     if (!pvStructInfo)
@@ -1824,6 +2375,12 @@
         SetLastError(CRYPT_E_ASN1_EOD);
         return FALSE;
     }
+    if (pbEncoded[1] > 0x7f)
+    {
+        /* long-form date strings really can't be valid */
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        return FALSE;
+    }
     len = pbEncoded[1];
     /* FIXME: magic # */
     if (len < 10)
@@ -1876,7 +2433,7 @@
 
     if (!pbEncoded || !cbEncoded)
     {
-        SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError(CRYPT_E_ASN1_EOD);
         return FALSE;
     }
     if (!pvStructInfo)
@@ -1912,7 +2469,7 @@
     HMODULE lib = NULL;
     CryptDecodeObjectExFunc decodeFunc = NULL;
 
-    TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p, %p): semi-stub\n",
+    TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p, %p)\n",
      dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
      "(integer value)", pbEncoded, cbEncoded, dwFlags, pDecodePara,
      pvStructInfo, pcbStructInfo);
@@ -1937,12 +2494,25 @@
         case (WORD)X509_NAME:
             decodeFunc = CRYPT_AsnDecodeName;
             break;
+        case (WORD)X509_OCTET_STRING:
+            decodeFunc = CRYPT_AsnDecodeOctets;
+            break;
+        case (WORD)X509_BITS:
+        case (WORD)X509_KEY_USAGE:
+            decodeFunc = CRYPT_AsnDecodeBits;
+            break;
         case (WORD)X509_INTEGER:
             decodeFunc = CRYPT_AsnDecodeInt;
             break;
         case (WORD)X509_MULTI_BYTE_INTEGER:
             decodeFunc = CRYPT_AsnDecodeInteger;
             break;
+        case (WORD)X509_MULTI_BYTE_UINT:
+            decodeFunc = CRYPT_AsnDecodeUnsignedInteger;
+            break;
+        case (WORD)X509_ENUMERATED:
+            decodeFunc = CRYPT_AsnDecodeEnumerated;
+            break;
         case (WORD)X509_CHOICE_OF_TIME:
             decodeFunc = CRYPT_AsnDecodeChoiceOfTime;
             break;
@@ -1955,6 +2525,12 @@
     }
     else if (!strcmp(lpszStructType, szOID_RSA_signingTime))
         decodeFunc = CRYPT_AsnDecodeUtcTime;
+    else if (!strcmp(lpszStructType, szOID_CRL_REASON_CODE))
+        decodeFunc = CRYPT_AsnDecodeEnumerated;
+    else if (!strcmp(lpszStructType, szOID_KEY_USAGE))
+        decodeFunc = CRYPT_AsnDecodeBits;
+    else if (!strcmp(lpszStructType, szOID_SUBJECT_KEY_IDENTIFIER))
+        decodeFunc = CRYPT_AsnDecodeOctets;
     if (!decodeFunc)
         decodeFunc = (CryptDecodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType,
          lpszStructType, "CryptDecodeObjectEx", &lib);
diff --git a/dlls/crypt32/tests/encode.c b/dlls/crypt32/tests/encode.c
index c9e1c77..96db89b 100644
--- a/dlls/crypt32/tests/encode.c
+++ b/dlls/crypt32/tests/encode.c
@@ -29,17 +29,17 @@
 struct encodedInt
 {
     int val;
-    BYTE encoded[6];
+    BYTE *encoded;
 };
 
 static const struct encodedInt ints[] = {
- { 1,          { 2, 1, 1 } },
- { 127,        { 2, 1, 0x7f } },
- { 128,        { 2, 2, 0x00, 0x80 } },
- { 256,        { 2, 2, 0x01, 0x00 } },
- { -128,       { 2, 1, 0x80 } },
- { -129,       { 2, 2, 0xff, 0x7f } },
- { 0xbaddf00d, { 2, 4, 0xba, 0xdd, 0xf0, 0x0d } },
+ { 1,          "\x02\x01\x01" },
+ { 127,        "\x02\x01\x7f" },
+ { 128,        "\x02\x02\x00\x80" },
+ { 256,        "\x02\x02\x01\x00" },
+ { -128,       "\x02\x01\x80" },
+ { -129,       "\x02\x02\xff\x7f" },
+ { 0xbaddf00d, "\x02\x04\xba\xdd\xf0\x0d" },
 };
 
 struct encodedBigInt
@@ -58,6 +58,14 @@
    "\x08\x07\x06\x05\x04\x03\x02\x01\xff" },
 };
 
+/* Decoded is the same as original, so don't bother storing a separate copy */
+static const struct encodedBigInt bigUInts[] = {
+ { "\xff\xff\x01\x02\x03\x04\x05\x06\x07\x08",
+   "\x02\x0a\x08\x07\x06\x05\x04\x03\x02\x01\xff\xff", NULL },
+ { "\x08\x07\x06\x05\x04\x03\x02\x01\xff\xff\xff",
+   "\x02\x0c\x00\xff\xff\xff\x01\x02\x03\x04\x05\x06\x07\x08", NULL },
+};
+
 static void test_encodeInt(DWORD dwEncoding)
 {
     DWORD bufSize = 0;
@@ -144,6 +152,29 @@
             LocalFree(buf);
         }
     }
+    /* and, encode some uints */
+    for (i = 0; i < sizeof(bigUInts) / sizeof(bigUInts[0]); i++)
+    {
+        blob.cbData = strlen(bigUInts[i].val);
+        blob.pbData = (BYTE *)bigUInts[i].val;
+        ret = CryptEncodeObjectEx(dwEncoding, X509_MULTI_BYTE_UINT, &blob,
+         0, NULL, NULL, &bufSize);
+        ok(ret, "Expected success, got %ld\n", GetLastError());
+        ret = CryptEncodeObjectEx(dwEncoding, X509_MULTI_BYTE_UINT, &blob,
+         CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
+        ok(ret, "CryptEncodeObjectEx failed: %ld\n", GetLastError());
+        if (buf)
+        {
+            ok(buf[0] == 2, "Got unexpected type %d for integer (expected 2)\n",
+             buf[0]);
+            ok(buf[1] == bigUInts[i].encoded[1], "Got length %d, expected %d\n",
+             buf[1], bigUInts[i].encoded[1]);
+            ok(!memcmp(buf + 1, bigUInts[i].encoded + 1,
+             bigUInts[i].encoded[1] + 1),
+             "Encoded value didn't match expected\n");
+            LocalFree(buf);
+        }
+    }
 }
 
 static void test_decodeInt(DWORD dwEncoding)
@@ -184,12 +215,12 @@
         /* When the output buffer is NULL, this always succeeds */
         SetLastError(0xdeadbeef);
         ret = CryptDecodeObjectEx(dwEncoding, X509_INTEGER,
-         (BYTE *)&ints[i].encoded, ints[i].encoded[1] + 2, 0, NULL, NULL,
+         (BYTE *)ints[i].encoded, ints[i].encoded[1] + 2, 0, NULL, NULL,
          &bufSize);
         ok(ret && GetLastError() == NOERROR,
          "Expected success and NOERROR, got %ld\n", GetLastError());
         ret = CryptDecodeObjectEx(dwEncoding, X509_INTEGER,
-         (BYTE *)&ints[i].encoded, ints[i].encoded[1] + 2,
+         (BYTE *)ints[i].encoded, ints[i].encoded[1] + 2,
          CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
         ok(ret, "CryptDecodeObjectEx failed: %ld\n", GetLastError());
         ok(bufSize == sizeof(int), "Expected size %d, got %ld\n", sizeof(int),
@@ -229,6 +260,105 @@
             LocalFree(buf);
         }
     }
+    for (i = 0; i < sizeof(bigUInts) / sizeof(bigUInts[0]); i++)
+    {
+        ret = CryptDecodeObjectEx(dwEncoding, X509_MULTI_BYTE_UINT,
+         (BYTE *)bigUInts[i].encoded, bigUInts[i].encoded[1] + 2, 0, NULL, NULL,
+         &bufSize);
+        ok(ret && GetLastError() == NOERROR,
+         "Expected success and NOERROR, got %ld\n", GetLastError());
+        ret = CryptDecodeObjectEx(dwEncoding, X509_MULTI_BYTE_UINT,
+         (BYTE *)bigUInts[i].encoded, bigUInts[i].encoded[1] + 2,
+         CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
+        ok(ret, "CryptDecodeObjectEx failed: %ld\n", GetLastError());
+        ok(bufSize >= sizeof(CRYPT_INTEGER_BLOB),
+         "Expected size at least %d, got %ld\n", sizeof(CRYPT_INTEGER_BLOB),
+         bufSize);
+        ok(buf != NULL, "Expected allocated buffer\n");
+        if (buf)
+        {
+            CRYPT_INTEGER_BLOB *blob = (CRYPT_INTEGER_BLOB *)buf;
+
+            ok(blob->cbData == strlen(bigUInts[i].val),
+             "Expected len %d, got %ld\n", strlen(bigUInts[i].val),
+             blob->cbData);
+            ok(!memcmp(blob->pbData, bigUInts[i].val, blob->cbData),
+             "Unexpected value\n");
+            LocalFree(buf);
+        }
+    }
+}
+
+/* These are always encoded unsigned, and aren't constrained to be any
+ * particular value
+ */
+static const struct encodedInt enums[] = {
+ { 1,    "\x0a\x01\x01" },
+ { -128, "\x0a\x05\x00\xff\xff\xff\x80" },
+};
+
+/* X509_CRL_REASON_CODE is also an enumerated type, but it's #defined to
+ * X509_ENUMERATED.
+ */
+static const LPCSTR enumeratedTypes[] = { X509_ENUMERATED,
+ szOID_CRL_REASON_CODE };
+
+static void test_encodeEnumerated(DWORD dwEncoding)
+{
+    DWORD i, j;
+
+    for (i = 0; i < sizeof(enumeratedTypes) / sizeof(enumeratedTypes[0]); i++)
+    {
+        for (j = 0; j < sizeof(enums) / sizeof(enums[0]); j++)
+        {
+            BOOL ret;
+            BYTE *buf = NULL;
+            DWORD bufSize = 0;
+
+            ret = CryptEncodeObjectEx(dwEncoding, enumeratedTypes[i],
+             &enums[j].val, CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+             &bufSize);
+            ok(ret, "CryptEncodeObjectEx failed: %ld\n", GetLastError());
+            if (buf)
+            {
+                ok(buf[0] == 0xa,
+                 "Got unexpected type %d for enumerated (expected 0xa)\n",
+                 buf[0]);
+                ok(buf[1] == enums[j].encoded[1],
+                 "Got length %d, expected %d\n", buf[1], enums[j].encoded[1]);
+                ok(!memcmp(buf + 1, enums[j].encoded + 1,
+                 enums[j].encoded[1] + 1),
+                 "Encoded value of 0x%08x didn't match expected\n",
+                 enums[j].val);
+                LocalFree(buf);
+            }
+        }
+    }
+}
+
+static void test_decodeEnumerated(DWORD dwEncoding)
+{
+    DWORD i, j;
+
+    for (i = 0; i < sizeof(enumeratedTypes) / sizeof(enumeratedTypes[0]); i++)
+    {
+        for (j = 0; j < sizeof(enums) / sizeof(enums[0]); j++)
+        {
+            BOOL ret;
+            DWORD bufSize = sizeof(int);
+            int val;
+
+            ret = CryptDecodeObjectEx(dwEncoding, enumeratedTypes[i],
+             enums[j].encoded, enums[j].encoded[1] + 2, 0, NULL,
+             (BYTE *)&val, &bufSize);
+            ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+            ok(bufSize == sizeof(int),
+             "Got unexpected size %ld for enumerated (expected %d)\n",
+             bufSize, sizeof(int));
+            ok(val == enums[j].val, "Unexpected value %d, expected %d\n",
+             val, enums[j].val);
+        }
+    }
 }
 
 struct encodedFiletime
@@ -672,6 +802,181 @@
     }
 }
 
+struct encodedOctets
+{
+    BYTE *val;
+    BYTE *encoded;
+};
+
+static const struct encodedOctets octets[] = {
+    { "hi", "\x04\x02hi" },
+    { "somelong\xffstring", "\x04\x0fsomelong\xffstring" },
+    { "", "\x04\x00" },
+};
+
+static void test_encodeOctets(DWORD dwEncoding)
+{
+    CRYPT_DATA_BLOB blob;
+    DWORD i;
+
+    for (i = 0; i < sizeof(octets) / sizeof(octets[0]); i++)
+    {
+        BYTE *buf = NULL;
+        BOOL ret;
+        DWORD bufSize = 0;
+
+        blob.cbData = strlen(octets[i].val);
+        blob.pbData = (BYTE *)octets[i].val;
+        ret = CryptEncodeObjectEx(dwEncoding, X509_OCTET_STRING, &blob,
+         CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
+        ok(ret, "CryptEncodeObjectEx failed: %ld\n", GetLastError());
+        if (buf)
+        {
+            ok(buf[0] == 4,
+             "Got unexpected type %d for octet string (expected 4)\n", buf[0]);
+            ok(buf[1] == octets[i].encoded[1], "Got length %d, expected %d\n",
+             buf[1], octets[i].encoded[1]);
+            ok(!memcmp(buf + 1, octets[i].encoded + 1,
+             octets[i].encoded[1] + 1), "Got unexpected value\n");
+            LocalFree(buf);
+        }
+    }
+}
+
+static void test_decodeOctets(DWORD dwEncoding)
+{
+    DWORD i;
+
+    for (i = 0; i < sizeof(octets) / sizeof(octets[0]); i++)
+    {
+        BYTE *buf = NULL;
+        BOOL ret;
+        DWORD bufSize = 0;
+
+        ret = CryptDecodeObjectEx(dwEncoding, X509_OCTET_STRING,
+         (BYTE *)octets[i].encoded, octets[i].encoded[1] + 2,
+         CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
+        ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+        ok(bufSize >= sizeof(CRYPT_DATA_BLOB) + octets[i].encoded[1],
+         "Expected size >= %d, got %ld\n",
+         sizeof(CRYPT_DATA_BLOB) + octets[i].encoded[1], bufSize);
+        ok(buf != NULL, "Expected allocated buffer\n");
+        if (buf)
+        {
+            CRYPT_DATA_BLOB *blob = (CRYPT_DATA_BLOB *)buf;
+
+            if (blob->cbData)
+                ok(!memcmp(blob->pbData, octets[i].val, blob->cbData),
+                 "Unexpected value\n");
+            LocalFree(buf);
+        }
+    }
+}
+
+static const BYTE bytesToEncode[] = { 0xff, 0xff };
+
+struct encodedBits
+{
+    DWORD cUnusedBits;
+    BYTE *encoded;
+    DWORD cbDecoded;
+    BYTE *decoded;
+};
+
+static const struct encodedBits bits[] = {
+    /* normal test case */
+    { 1, "\x03\x03\x01\xff\xfe", 2, "\xff\xfe" },
+    /* strange test case, showing cUnusedBits >= 8 is allowed */
+    { 9, "\x03\x02\x01\xfe", 1, "\xfe" },
+    /* even stranger test case, showing cUnusedBits > cbData * 8 is allowed */
+    { 17, "\x03\x01\x00", 0, NULL },
+};
+
+static void test_encodeBits(DWORD dwEncoding)
+{
+    DWORD i;
+
+    for (i = 0; i < sizeof(bits) / sizeof(bits[0]); i++)
+    {
+        CRYPT_BIT_BLOB blob;
+        BOOL ret;
+        BYTE *buf = NULL;
+        DWORD bufSize = 0;
+
+        blob.cbData = sizeof(bytesToEncode);
+        blob.pbData = (BYTE *)bytesToEncode;
+        blob.cUnusedBits = bits[i].cUnusedBits;
+        ret = CryptEncodeObjectEx(dwEncoding, X509_BITS, &blob,
+         CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
+        ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
+        if (buf)
+        {
+            ok(bufSize == bits[i].encoded[1] + 2,
+             "Got unexpected size %ld, expected %d\n", bufSize,
+             bits[i].encoded[1] + 2);
+            ok(!memcmp(buf, bits[i].encoded, bits[i].encoded[1] + 2),
+             "Unexpected value\n");
+            LocalFree(buf);
+        }
+    }
+}
+
+static void test_decodeBits(DWORD dwEncoding)
+{
+    static const BYTE ber[] = "\x03\x02\x01\xff";
+    static const BYTE berDecoded = 0xfe;
+    DWORD i;
+    BOOL ret;
+    BYTE *buf = NULL;
+    DWORD bufSize = 0;
+
+    /* normal cases */
+    for (i = 0; i < sizeof(bits) / sizeof(bits[0]); i++)
+    {
+        ret = CryptDecodeObjectEx(dwEncoding, X509_BITS, bits[i].encoded,
+         bits[i].encoded[1] + 2, CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+         &bufSize);
+        ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+        if (buf)
+        {
+            CRYPT_BIT_BLOB *blob;
+
+            ok(bufSize >= sizeof(CRYPT_BIT_BLOB) + bits[i].cbDecoded,
+             "Got unexpected size %ld, expected >= %ld\n", bufSize,
+             sizeof(CRYPT_BIT_BLOB) + bits[i].cbDecoded);
+            blob = (CRYPT_BIT_BLOB *)buf;
+            ok(blob->cbData == bits[i].cbDecoded,
+             "Got unexpected length %ld, expected %ld\n", blob->cbData,
+             bits[i].cbDecoded);
+            if (blob->cbData && bits[i].cbDecoded)
+                ok(!memcmp(blob->pbData, bits[i].decoded, bits[i].cbDecoded),
+                 "Unexpected value\n");
+            LocalFree(buf);
+        }
+    }
+    /* special case: check that something that's valid in BER but not in DER
+     * decodes successfully
+     */
+    ret = CryptDecodeObjectEx(dwEncoding, X509_BITS, ber, ber[1] + 2,
+     CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
+    ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        CRYPT_BIT_BLOB *blob;
+
+        ok(bufSize >= sizeof(CRYPT_BIT_BLOB) + sizeof(berDecoded),
+         "Got unexpected size %ld, expected >= %d\n", bufSize,
+         sizeof(CRYPT_BIT_BLOB) + berDecoded);
+        blob = (CRYPT_BIT_BLOB *)buf;
+        ok(blob->cbData == sizeof(berDecoded),
+         "Got unexpected length %ld, expected %d\n", blob->cbData,
+         sizeof(berDecoded));
+        if (blob->cbData)
+            ok(*blob->pbData == berDecoded, "Unexpected value\n");
+        LocalFree(buf);
+    }
+}
+
 static void test_registerOIDFunction(void)
 {
     static const WCHAR bogusDll[] = { 'b','o','g','u','s','.','d','l','l',0 };
@@ -736,10 +1041,16 @@
     {
         test_encodeInt(encodings[i]);
         test_decodeInt(encodings[i]);
+        test_encodeEnumerated(encodings[i]);
+        test_decodeEnumerated(encodings[i]);
         test_encodeFiletime(encodings[i]);
         test_decodeFiletime(encodings[i]);
         test_encodeName(encodings[i]);
         test_decodeName(encodings[i]);
+        test_encodeOctets(encodings[i]);
+        test_decodeOctets(encodings[i]);
+        test_encodeBits(encodings[i]);
+        test_decodeBits(encodings[i]);
     }
     test_registerOIDFunction();
 }