[PATCH v3] crypt32: Implement CNG_RSA_PUBLIC_KEY_BLOB encoding/decoding

Aaron Hill aa1ronham at gmail.com
Wed Jul 7 21:52:24 CDT 2021


Add support for the OID CNG_RSA_PUBLIC_KEY_BLOB to
CryptEncodeObjectEx and CryptDecodeObjectEx. This OID
decodes to / encodes from memory consisting of a
BCRYPT_RSAKEY_BLOB, followed in memory by the exponent and
modulus in big-endian format.

This allows the Visual Studio 2019 web installer to progress further -
it now reachs the stubbed NCryptOpenStorageProvider function.

To aid in debugging similar issues, CryptEncodeObjectEx
and CryptDecodeObjectEX now print a FIXME when encountering an
unknown OID/struct type, instead of silently returning an error.

Signed-off-by: Aaron Hill <aa1ronham at gmail.com>
---
v2: Correctly handle endianness
v3: Fix function definition in header file
---
 dlls/crypt32/crypt32_private.h |   6 ++
 dlls/crypt32/decode.c          | 129 ++++++++++++++++++++++++---
 dlls/crypt32/encode.c          |  56 ++++++++++++
 dlls/crypt32/tests/encode.c    | 158 +++++++++++++++++++++++++++++++++
 include/wincrypt.h             |   1 +
 5 files changed, 338 insertions(+), 12 deletions(-)

diff --git a/dlls/crypt32/crypt32_private.h b/dlls/crypt32/crypt32_private.h
index 9f8f43e7632..a4c31e8e1f8 100644
--- a/dlls/crypt32/crypt32_private.h
+++ b/dlls/crypt32/crypt32_private.h
@@ -42,6 +42,12 @@ BOOL CNG_ImportPubKey(CERT_PUBLIC_KEY_INFO *pubKeyInfo, BCRYPT_KEY_HANDLE *key)
 #define ASN_UNIVERSALSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x1c)
 #define ASN_BMPSTRING       (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x1e)
 
+/* Copies `len` bytes from `src` to `dst`.
+ * On a little-endian platform, additionally swaps
+ * the order of the bytes
+ */
+void CRYPT_memcpy_be(BYTE *dst, const BYTE *src, size_t len);
+
 BOOL CRYPT_EncodeLen(DWORD len, BYTE *pbEncoded, DWORD *pcbEncoded) DECLSPEC_HIDDEN;
 
 typedef BOOL (WINAPI *CryptEncodeObjectExFunc)(DWORD, LPCSTR, const void *,
diff --git a/dlls/crypt32/decode.c b/dlls/crypt32/decode.c
index a0e78d62b4d..6dbd5b57f34 100644
--- a/dlls/crypt32/decode.c
+++ b/dlls/crypt32/decode.c
@@ -55,6 +55,27 @@
 WINE_DEFAULT_DEBUG_CHANNEL(cryptasn);
 WINE_DECLARE_DEBUG_CHANNEL(crypt);
 
+/* Copies `len` bytes from `src` to `dst`.
+ * On a little-endian platform, additionally swaps
+ * the order of the bytes
+ */
+#ifdef WORDS_BIGENDIAN
+
+void CRYPT_memcpy_be(BYTE *dst, const BYTE *src, size_t len)
+{
+    memcpy(dst, src, len);
+}
+
+#else
+void CRYPT_memcpy_be(BYTE *dst, const BYTE *src, size_t len)
+{
+    DWORD i;
+    for (i = 0; i < len; i++) {
+        dst[len - i - 1] = src[i];
+    }
+}
+#endif
+
 typedef BOOL (WINAPI *CryptDecodeObjectFunc)(DWORD, LPCSTR, const BYTE *,
  DWORD, DWORD, void *, DWORD *);
 typedef BOOL (WINAPI *CryptDecodeObjectExFunc)(DWORD, LPCSTR, const BYTE *,
@@ -3870,7 +3891,31 @@ struct DECODED_RSA_PUB_KEY
     CRYPT_INTEGER_BLOB modulus;
 };
 
-static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey(DWORD dwCertEncodingType,
+/* Helper function to decode an ASN.1 DER encoded RSA public key, writing the decoded
+ * key into 'decodedKey', and the length into 'size'. The memory
+ * for 'decodedKey' is allocated with 'CRYPT_DECODE_ALLOC_FLAG'
+ */
+static BOOL CRYPT_raw_decode_rsa_pub_key(struct DECODED_RSA_PUB_KEY **decodedKey,
+        DWORD *size, const BYTE *pbEncoded, DWORD cbEncoded)
+{
+    BOOL ret;
+
+    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 },
+    };
+
+    ret = CRYPT_AsnDecodeSequence(items, ARRAY_SIZE(items),
+     pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, decodedKey,
+     size, NULL, NULL);
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey_Bcrypt(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
@@ -3878,20 +3923,73 @@ static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey(DWORD dwCertEncodingType,
 
     __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, ARRAY_SIZE(items),
-         pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &decodedKey,
-         &size, NULL, NULL);
+        TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+         pvStructInfo, *pcbStructInfo);
+
+        ret = CRYPT_raw_decode_rsa_pub_key(&decodedKey, &size, pbEncoded, cbEncoded);
+        if (ret)
+        {
+            /* Header, exponent, and modulus */
+            DWORD bytesNeeded = sizeof(BCRYPT_RSAKEY_BLOB) + sizeof(DWORD) +
+             decodedKey->modulus.cbData;
+
+            if (!pvStructInfo)
+            {
+                *pcbStructInfo = bytesNeeded;
+                ret = TRUE;
+            }
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                BCRYPT_RSAKEY_BLOB *hdr;
+
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+
+                hdr = pvStructInfo;
+                hdr->Magic = BCRYPT_RSAPUBLIC_MAGIC;
+                hdr->BitLength = decodedKey->modulus.cbData * 8;
+                hdr->cbPublicExp = sizeof(DWORD);
+                hdr->cbModulus = decodedKey->modulus.cbData;
+                hdr->cbPrime1 = 0;
+                hdr->cbPrime2 = 0;
+                /* CNG_RSA_PUBLIC_KEY_BLOB always stores the exponent and modulus
+                 * in big-endian format, so we need to convert from the native
+                 * endianness
+                 */
+                CRYPT_memcpy_be((BYTE *)pvStructInfo + sizeof(BCRYPT_RSAKEY_BLOB),
+                 (BYTE *)&decodedKey->pubexp, sizeof(DWORD));
+                CRYPT_memcpy_be((BYTE *)pvStructInfo + sizeof(BCRYPT_RSAKEY_BLOB) +
+                 sizeof(DWORD), decodedKey->modulus.pbData,
+                 decodedKey->modulus.cbData);
+            }
+            LocalFree(decodedKey);
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
+
+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 DECODED_RSA_PUB_KEY *decodedKey = NULL;
+        DWORD size = 0;
+        ret = CRYPT_raw_decode_rsa_pub_key(&decodedKey, &size, pbEncoded, cbEncoded);
         if (ret)
         {
             DWORD bytesNeeded = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) +
@@ -4591,6 +4689,7 @@ static BOOL CRYPT_AsnDecodeUnsignedIntegerInternal(const BYTE *pbEncoded,
             }
             else
             {
+                /* FIXME This assumes that the system is little-endian */
                 CRYPT_INTEGER_BLOB *blob = pvStructInfo;
 
                 *pcbStructInfo = bytesNeeded;
@@ -6207,7 +6306,11 @@ static CryptDecodeObjectExFunc CRYPT_GetBuiltinDecoder(DWORD dwCertEncodingType,
             break;
         case LOWORD(X509_ECC_SIGNATURE):
             decodeFunc = CRYPT_AsnDecodeEccSignature;
+        case LOWORD(CNG_RSA_PUBLIC_KEY_BLOB):
+            decodeFunc = CRYPT_AsnDecodeRsaPubKey_Bcrypt;
             break;
+        default:
+            FIXME("Unimplemented decoder for lpszStructType OID %d\n", LOWORD(lpszStructType));
         }
     }
     else if (!strcmp(lpszStructType, szOID_CERT_EXTENSIONS))
@@ -6264,6 +6367,8 @@ static CryptDecodeObjectExFunc CRYPT_GetBuiltinDecoder(DWORD dwCertEncodingType,
         decodeFunc = CRYPT_AsnDecodeCTL;
     else if (!strcmp(lpszStructType, szOID_ECC_PUBLIC_KEY))
         decodeFunc = CRYPT_AsnDecodeObjectIdentifier;
+    else
+        FIXME("Unsupported decoder for lpszStructType %s\n", lpszStructType);
     return decodeFunc;
 }
 
diff --git a/dlls/crypt32/encode.c b/dlls/crypt32/encode.c
index 02b235696ce..94c3033facc 100644
--- a/dlls/crypt32/encode.c
+++ b/dlls/crypt32/encode.c
@@ -3130,6 +3130,53 @@ static BOOL WINAPI CRYPT_AsnEncodeCertPolicyConstraints(
     return ret;
 }
 
+static BOOL WINAPI CRYPT_AsnEncodeRsaPubKey_Bcrypt(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
+ PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    BOOL ret;
+
+    __TRY
+    {
+        const BCRYPT_RSAKEY_BLOB *hdr = pvStructInfo;
+
+        BYTE *pubexp = (BYTE*)pvStructInfo + sizeof(BCRYPT_RSAKEY_BLOB);
+        BYTE *modulus = (BYTE*)pvStructInfo + sizeof(BCRYPT_RSAKEY_BLOB) + hdr->cbPublicExp;
+        BYTE *pubexp_be = CryptMemAlloc(hdr->cbPublicExp);
+        BYTE *modulus_be = CryptMemAlloc(hdr->cbModulus);
+        CRYPT_INTEGER_BLOB pubexp_int = { hdr->cbPublicExp, pubexp_be };
+        CRYPT_INTEGER_BLOB modulus_int = { hdr->cbModulus, modulus_be};
+
+        struct AsnEncodeSequenceItem items[] = {
+         { &modulus_int, CRYPT_AsnEncodeUnsignedInteger, 0 },
+         { &pubexp_int, CRYPT_AsnEncodeInteger, 0 },
+        };
+
+        /* CNG_RSA_PUBLIC_KEY_BLOB stores the exponent and modulus
+         * in big-endian format, so we need to convert them
+         * to the native endianness before encoding
+         */
+        CRYPT_memcpy_be(pubexp_be, pubexp, hdr->cbPublicExp);
+        CRYPT_memcpy_be(modulus_be, modulus, hdr->cbModulus);
+
+        ret = CRYPT_AsnEncodeSequence(dwCertEncodingType, items,
+         ARRAY_SIZE(items), dwFlags, pEncodePara, pbEncoded, pcbEncoded);
+
+        CryptMemFree(pubexp_be);
+        CryptMemFree(modulus_be);
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        ERR("PAGE FAULT\n");
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+
+}
+
+
 static BOOL WINAPI CRYPT_AsnEncodeRsaPubKey(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
@@ -3350,6 +3397,7 @@ static BOOL WINAPI CRYPT_AsnEncodeInteger(DWORD dwCertEncodingType,
         significantBytes = blob->cbData;
         if (significantBytes)
         {
+            /* FIXME This assumes that the system is little-endian */
             if (blob->pbData[significantBytes - 1] & 0x80)
             {
                 /* negative, lop off leading (little-endian) 0xffs */
@@ -3484,6 +3532,7 @@ static BOOL WINAPI CRYPT_AsnEncodeUnsignedInteger(DWORD dwCertEncodingType,
     }
     __EXCEPT_PAGE_FAULT
     {
+        ERR("Other page fault\n");
         SetLastError(STATUS_ACCESS_VIOLATION);
         ret = FALSE;
     }
@@ -4563,6 +4612,11 @@ static CryptEncodeObjectExFunc CRYPT_GetBuiltinEncoder(DWORD dwCertEncodingType,
         case LOWORD(CMS_SIGNER_INFO):
             encodeFunc = CRYPT_AsnEncodeCMSSignerInfo;
             break;
+        case LOWORD(CNG_RSA_PUBLIC_KEY_BLOB):
+            encodeFunc = CRYPT_AsnEncodeRsaPubKey_Bcrypt;
+            break;
+        default:
+            FIXME("Unimplemented encoder for lpszStructType OID %d\n", LOWORD(lpszStructType));
         }
     }
     else if (!strcmp(lpszStructType, szOID_CERT_EXTENSIONS))
@@ -4617,6 +4671,8 @@ static CryptEncodeObjectExFunc CRYPT_GetBuiltinEncoder(DWORD dwCertEncodingType,
         encodeFunc = CRYPT_AsnEncodePolicyQualifierUserNotice;
     else if (!strcmp(lpszStructType, szOID_CTL))
         encodeFunc = CRYPT_AsnEncodeCTL;
+    else
+        FIXME("Unsupported encoder for lpszStructType %s\n", lpszStructType);
     return encodeFunc;
 }
 
diff --git a/dlls/crypt32/tests/encode.c b/dlls/crypt32/tests/encode.c
index 9597f1dfec1..fbdb05992d5 100644
--- a/dlls/crypt32/tests/encode.c
+++ b/dlls/crypt32/tests/encode.c
@@ -31,6 +31,35 @@
 static BOOL (WINAPI *pCryptDecodeObjectEx)(DWORD,LPCSTR,const BYTE*,DWORD,DWORD,PCRYPT_DECODE_PARA,void*,DWORD*);
 static BOOL (WINAPI *pCryptEncodeObjectEx)(DWORD,LPCSTR,const void*,DWORD,PCRYPT_ENCODE_PARA,void*,DWORD*);
 
+/* Copies `len` bytes from `src` to `dst`.
+ * On a little-endian platform, additionally swaps
+ * the order of the bytes
+ */
+#ifdef WORDS_BIGENDIAN
+
+void CRYPT_memcpy_be(BYTE *dst, const BYTE *src, size_t len)
+{
+    memcpy(dst, src, len);
+}
+
+#else
+void CRYPT_memcpy_be(BYTE *dst, const BYTE *src, size_t len)
+{
+    DWORD i;
+    for (i = 0; i < len; i++) {
+        dst[len - i - 1] = src[i];
+    }
+}
+#endif
+
+static void CRYPT_ReverseBytes(BYTE *dst, const BYTE *src, DWORD len)
+{
+    DWORD i;
+    for (i = 0; i < len; i++) {
+        dst[len - i - 1] = src[i];
+    }
+}
+
 struct encodedInt
 {
     int val;
@@ -2572,6 +2601,133 @@ static void test_decodeRsaPublicKey(DWORD dwEncoding)
     }
 }
 
+static void test_encodeRsaPublicKey_Bcrypt(DWORD dwEncoding)
+{
+    BYTE toEncode[sizeof(BCRYPT_RSAKEY_BLOB) + sizeof(DWORD) + sizeof(modulus1)];
+    BCRYPT_RSAKEY_BLOB *hdr = (BCRYPT_RSAKEY_BLOB *)toEncode;
+    BOOL ret;
+    BYTE *buf = NULL;
+    DWORD bufSize = 0, i;
+    DWORD pubexp = 65537;
+
+    /* Verify that the Magic value doesn't matter */
+    hdr->Magic = 1;
+    hdr->BitLength = sizeof(modulus1) * 8;
+    hdr->cbPublicExp = sizeof(DWORD);
+    hdr->cbModulus = sizeof(modulus1);
+    hdr->cbPrime1 = 0;
+    hdr->cbPrime2 = 0;
+
+    /* CNG_RSA_PUBLIC_KEY_BLOB stores the exponent
+     * in big-endian format, so we need to it
+     * from native endianness before encoding
+     */
+    CRYPT_memcpy_be(toEncode + sizeof(BCRYPT_RSAKEY_BLOB), (BYTE *)&pubexp, sizeof(DWORD));
+    /* Our modulus is always stored in little-endian format, so we need to reverse
+     * the bytes to get it into big-endian format for CNG_RSA_PUBLIC_KEY_BLOB
+     */
+    CRYPT_ReverseBytes(toEncode + sizeof(BCRYPT_RSAKEY_BLOB) + sizeof(DWORD), modulus1, sizeof(modulus1));
+
+    ret = pCryptEncodeObjectEx(dwEncoding, CNG_RSA_PUBLIC_KEY_BLOB,
+     toEncode, CRYPT_ENCODE_ALLOC_FLAG, NULL, &buf, &bufSize);
+    ok(ret, "CryptEncodeObjectEx failed: %08x\n", GetLastError());
+
+    /* Finally, all valid */
+    hdr->Magic = BCRYPT_RSAPUBLIC_MAGIC;
+    for (i = 0; i < ARRAY_SIZE(rsaPubKeys); i++)
+    {
+        hdr->BitLength = rsaPubKeys[i].modulusLen * 8;
+        hdr->cbModulus = rsaPubKeys[i].modulusLen;
+
+        /* Our modulus is always stored in little-endian format, so we need to reverse
+         * the bytes to get it into big-endian format for CNG_RSA_PUBLIC_KEY_BLOB
+         */
+        CRYPT_ReverseBytes(toEncode + sizeof(BCRYPT_RSAKEY_BLOB) + sizeof(DWORD),
+         rsaPubKeys[i].modulus, rsaPubKeys[i].modulusLen);
+
+        ret = pCryptEncodeObjectEx(dwEncoding, CNG_RSA_PUBLIC_KEY_BLOB,
+         toEncode, CRYPT_ENCODE_ALLOC_FLAG, NULL, &buf, &bufSize);
+        ok(ret, "CryptEncodeObjectEx failed: %08x\n", GetLastError());
+        if (ret)
+        {
+            ok(bufSize == rsaPubKeys[i].encoded[1] + 2,
+             "Expected size %d, got %d\n", rsaPubKeys[i].encoded[1] + 2,
+             bufSize);
+            ok(!memcmp(buf, rsaPubKeys[i].encoded, bufSize),
+             "Unexpected value\n");
+            LocalFree(buf);
+        }
+    }
+}
+
+static void test_decodeRsaPublicKey_Bcrypt(DWORD dwEncoding)
+{
+    DWORD i;
+    LPBYTE buf = NULL;
+    LPBYTE leModulus = NULL;
+    DWORD bufSize = 0;
+    BOOL ret;
+
+    /* Try with a bad length */
+    ret = pCryptDecodeObjectEx(dwEncoding, CNG_RSA_PUBLIC_KEY_BLOB,
+     rsaPubKeys[0].encoded, rsaPubKeys[0].encoded[1],
+     CRYPT_DECODE_ALLOC_FLAG, NULL, &buf, &bufSize);
+    ok(!ret && (GetLastError() == CRYPT_E_ASN1_EOD ||
+     GetLastError() == OSS_MORE_INPUT /* Win9x/NT4 */),
+     "Expected CRYPT_E_ASN1_EOD or OSS_MORE_INPUT, got %08x\n",
+     GetLastError());
+    /* Now try success cases */
+    for (i = 0; i < ARRAY_SIZE(rsaPubKeys); i++)
+    {
+        bufSize = 0;
+        ret = pCryptDecodeObjectEx(dwEncoding, CNG_RSA_PUBLIC_KEY_BLOB,
+         rsaPubKeys[i].encoded, rsaPubKeys[i].encoded[1] + 2,
+         CRYPT_DECODE_ALLOC_FLAG, NULL, &buf, &bufSize);
+        ok(ret, "CryptDecodeObjectEx failed: %08x\n", GetLastError());
+        if (ret)
+        {
+            BCRYPT_RSAKEY_BLOB *hdr = (BCRYPT_RSAKEY_BLOB *)buf;
+            DWORD pubexp = 0;
+            /* CNG_RSA_PUBLIC_KEY_BLOB always stores the exponent and modulus
+             * in big-endian format, so we need to convert it to the native
+             * endianness
+             */
+            CRYPT_memcpy_be((BYTE *)&pubexp, buf + sizeof(BCRYPT_RSAKEY_BLOB), hdr->cbPublicExp);
+            ok(bufSize >= sizeof(BCRYPT_RSAKEY_BLOB) +
+             rsaPubKeys[i].decodedModulusLen,
+             "Wrong size %d\n", bufSize);
+            ok(hdr->Magic == BCRYPT_RSAPUBLIC_MAGIC,
+             "Expected magic BCRYPT_RSAPUBLIC_MAGIC (%d), got %d\n", BCRYPT_RSAPUBLIC_MAGIC,
+             hdr->Magic);
+            ok(hdr->BitLength == rsaPubKeys[i].decodedModulusLen * 8,
+             "Wrong bit len %d\n", hdr->BitLength);
+            /* Windows decodes the exponent to 3 bytes, since it will fit.
+             * Our implementation currently unconditionally decodes to a DWORD (4 bytes)
+             */
+            ok(hdr->cbPublicExp == sizeof(DWORD) || hdr->cbPublicExp == 3, "Expected cbPublicExp %d, got %d\n",
+              sizeof(DWORD), hdr->cbPublicExp);
+            ok(hdr->cbModulus == rsaPubKeys[i].decodedModulusLen,
+              "Wrong modulus len %d\n", hdr->cbModulus);
+            ok(hdr->cbPrime1 == 0,"Wrong cbPrime1 %d\n", hdr->cbPrime1);
+            ok(hdr->cbPrime2 == 0,"Wrong cbPrime2 %d\n", hdr->cbPrime2);
+            ok(pubexp== 65537, "Expected pubexp 65537, got %d\n",
+             pubexp);
+
+            leModulus = HeapAlloc(GetProcessHeap(), 0, hdr->cbModulus);
+            /* Unconditionally reverse the bytes, since rsaPubKeys always stores
+             * the modulus in little-endian format, and CNG_RSA_PUBLIC_KEY_BLOB always
+             * stores the modulus in big-endian format */
+            CRYPT_ReverseBytes(leModulus, buf + sizeof(BCRYPT_RSAKEY_BLOB) + hdr->cbPublicExp,
+                    hdr->cbModulus);
+            ok(!memcmp(leModulus,
+             rsaPubKeys[i].modulus, rsaPubKeys[i].decodedModulusLen),
+             "Unexpected modulus\n");
+            LocalFree(buf);
+            LocalFree(leModulus);
+        }
+    }
+}
+
 static const BYTE intSequence[] = { 0x30, 0x1b, 0x02, 0x01, 0x01, 0x02, 0x01,
  0x7f, 0x02, 0x02, 0x00, 0x80, 0x02, 0x02, 0x01, 0x00, 0x02, 0x01, 0x80, 0x02,
  0x02, 0xff, 0x7f, 0x02, 0x04, 0xba, 0xdd, 0xf0, 0x0d };
@@ -8547,6 +8703,8 @@ START_TEST(encode)
         test_decodeBasicConstraints(encodings[i]);
         test_encodeRsaPublicKey(encodings[i]);
         test_decodeRsaPublicKey(encodings[i]);
+        test_encodeRsaPublicKey_Bcrypt(encodings[i]);
+        test_decodeRsaPublicKey_Bcrypt(encodings[i]);
         test_encodeSequenceOfAny(encodings[i]);
         test_decodeSequenceOfAny(encodings[i]);
         test_encodeExtensions(encodings[i]);
diff --git a/include/wincrypt.h b/include/wincrypt.h
index 28bebf8dc4b..456f2f14446 100644
--- a/include/wincrypt.h
+++ b/include/wincrypt.h
@@ -3179,6 +3179,7 @@ typedef struct _CTL_FIND_SUBJECT_PARA
 #define CMC_ADD_EXTENSIONS                   ((LPCSTR)62)
 #define CMC_ADD_ATTRIBUTES                   ((LPCSTR)63)
 #define X509_CERTIFICATE_TEMPLATE            ((LPCSTR)64)
+#define CNG_RSA_PUBLIC_KEY_BLOB              ((LPCSTR)72)
 #define X509_OBJECT_IDENTIFIER               ((LPCSTR)73)
 #define PKCS7_SIGNER_INFO                    ((LPCSTR)500)
 #define CMS_SIGNER_INFO                      ((LPCSTR)501)
-- 
2.32.0




More information about the wine-devel mailing list