Fixes Age of Empire 2 failing to start due to WinVerifyTrust failing to successfully verify game files' signatures which are signed with ECC key.
I added a test in patch 1 to show that ECC certificate signature raw data indeed has bytes reversed (unlike the signature in messages). The existing code appears to be correct but removing this reversal wasn't resulting in test failures.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/crypt32/tests/cert.c | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
diff --git a/dlls/crypt32/tests/cert.c b/dlls/crypt32/tests/cert.c index ac0987e76bd..c56578f1e21 100644 --- a/dlls/crypt32/tests/cert.c +++ b/dlls/crypt32/tests/cert.c @@ -2042,6 +2042,43 @@ static void testVerifyCertSigEx(HCRYPTPROV csp, const CRYPT_DATA_BLOB *toBeSigne
static BYTE emptyCert[] = { 0x30, 0x00 };
+ +/* Generated with: + * openssl ecparam -name prime256v1 -genkey -out private-key.pem + * openssl req -new -x509 -key private-key.pem -out certificate.der -outform der -days 900000 -subj "/C=US/ST=T/L=T/O=T/CN=T" + */ +static const BYTE self_signed_ecc_prime256v1[] = { +0x30,0x82,0x01,0xd1,0x30,0x82,0x01,0x77,0xa0,0x03,0x02,0x01,0x02,0x02,0x14,0x32, +0xc2,0xbe,0x7b,0xa2,0x85,0x78,0x89,0x82,0xf8,0x10,0x66,0xd4,0x1d,0xd4,0x97,0x61, +0x83,0x02,0xc8,0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x30, +0x3d,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x0a, +0x30,0x08,0x06,0x03,0x55,0x04,0x08,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03, +0x55,0x04,0x07,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x0a,0x0c, +0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x03,0x0c,0x01,0x54,0x30,0x20, +0x17,0x0d,0x32,0x33,0x30,0x36,0x32,0x39,0x30,0x32,0x32,0x34,0x30,0x33,0x5a,0x18, +0x0f,0x34,0x34,0x38,0x37,0x30,0x38,0x31,0x30,0x30,0x32,0x32,0x34,0x30,0x33,0x5a, +0x30,0x3d,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31, +0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x08,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06, +0x03,0x55,0x04,0x07,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x0a, +0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x03,0x0c,0x01,0x54,0x30, +0x59,0x30,0x13,0x06,0x07,0x2a,0x86,0x48,0xce,0x3d,0x02,0x01,0x06,0x08,0x2a,0x86, +0x48,0xce,0x3d,0x03,0x01,0x07,0x03,0x42,0x00,0x04,0xfe,0xdb,0x26,0x60,0xf6,0x89, +0x3d,0xa4,0x50,0x1f,0x06,0x91,0x4e,0x07,0x86,0x70,0x2b,0xc0,0x7c,0x5e,0xb3,0xca, +0xdc,0x1a,0x8b,0x82,0xdd,0x41,0x8a,0x62,0x0f,0xba,0xd1,0xd7,0x80,0xc8,0x20,0x77, +0xba,0xe7,0xe1,0x36,0xf8,0x76,0x9a,0x54,0x6a,0x1b,0x67,0x45,0x3b,0xd7,0x85,0x84, +0xbe,0x11,0xe6,0x6c,0x70,0xd8,0x18,0x68,0xd8,0xa7,0xa3,0x53,0x30,0x51,0x30,0x1d, +0x06,0x03,0x55,0x1d,0x0e,0x04,0x16,0x04,0x14,0x94,0x15,0x14,0xad,0x7e,0xaf,0x63, +0xa4,0x12,0x29,0xaa,0xe4,0x26,0x54,0x7b,0x4e,0x2c,0xb9,0xdb,0xc8,0x30,0x1f,0x06, +0x03,0x55,0x1d,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x94,0x15,0x14,0xad,0x7e,0xaf, +0x63,0xa4,0x12,0x29,0xaa,0xe4,0x26,0x54,0x7b,0x4e,0x2c,0xb9,0xdb,0xc8,0x30,0x0f, +0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x05,0x30,0x03,0x01,0x01,0xff,0x30, +0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x03,0x48,0x00,0x30,0x45, +0x02,0x21,0x00,0x83,0xae,0xa2,0x23,0x95,0x1a,0x65,0x09,0x48,0x40,0x10,0xeb,0x94, +0x90,0x02,0xde,0xe3,0x0f,0x4b,0xd1,0x23,0x73,0xc6,0xd5,0x49,0xa8,0x9c,0x06,0x9c, +0xd3,0xfb,0xc1,0x02,0x20,0x0c,0xf3,0x92,0xec,0xc8,0xb5,0x7e,0x9c,0x14,0x5d,0xb0, +0x26,0xfd,0x2a,0x3c,0x4e,0x08,0x55,0x09,0x35,0x40,0x7c,0xf8,0xf9,0x1b,0x22,0x55, +0x08,0x9b,0x3f,0x37,0x29, }; + static void testCertSigs(void) { HCRYPTPROV csp; @@ -2049,6 +2086,7 @@ static void testCertSigs(void) BOOL ret; BYTE sig[64]; DWORD sigSize = sizeof(sig); + PCCERT_CONTEXT cert;
/* Just in case a previous run failed, delete this thing */ CryptAcquireContextA(&csp, cspNameA, MS_DEF_PROV_A, PROV_RSA_FULL, @@ -2065,6 +2103,13 @@ static void testCertSigs(void) ret = CryptAcquireContextA(&csp, cspNameA, MS_DEF_PROV_A, PROV_RSA_FULL, CRYPT_DELETEKEYSET); ok(ret, "CryptAcquireContext failed: %08lx\n", GetLastError()); + + cert = CertCreateCertificateContext(X509_ASN_ENCODING, self_signed_ecc_prime256v1, sizeof(self_signed_ecc_prime256v1)); + ok(!!cert, "failed, error %#lx.\n", GetLastError()); + ret = CryptVerifyCertificateSignature(0, X509_ASN_ENCODING, self_signed_ecc_prime256v1, + sizeof(self_signed_ecc_prime256v1), &cert->pCertInfo->SubjectPublicKeyInfo); + ok(ret, "failed, error %#lx.\n", GetLastError()); + CertFreeCertificateContext(cert); }
static const BYTE md5SignedEmptyCert[] = {
From: Paul Gofman pgofman@codeweavers.com
--- dlls/crypt32/tests/msg.c | 140 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+)
diff --git a/dlls/crypt32/tests/msg.c b/dlls/crypt32/tests/msg.c index 16f7402c613..c663909ed7c 100644 --- a/dlls/crypt32/tests/msg.c +++ b/dlls/crypt32/tests/msg.c @@ -3483,6 +3483,145 @@ static void test_msg_get_and_verify_signer(void) CryptMsgClose(msg); }
+/* Generated with: + * openssl ecparam -name prime256v1 -genkey -out private-key.pem + * openssl req -new -x509 -key private-key.pem -out certificate.der -outform der -days 10000 -subj "/C=US/ST=T/L=T/O=T/CN=T" + * openssl pkcs12 -export -out certificate.pfx -inkey private-key.pem -in certificate.der + * - import certificate.pfx on Windows + * signtool /sign /v /fd SHA256 certificate.pfx a.exe + * - extract signed message from a.exe + */ +static const BYTE msg_signed_ecc_prime256v1[] = { +0x30,0x82,0x03,0x85,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x07,0x02,0xa0, +0x82,0x03,0x76,0x30,0x82,0x03,0x72,0x02,0x01,0x01,0x31,0x0f,0x30,0x0d,0x06,0x09, +0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x30,0x5c,0x06,0x0a,0x2b, +0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x04,0xa0,0x4e,0x30,0x4c,0x30,0x17,0x06, +0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x0f,0x30,0x09,0x03,0x01,0x00, +0xa0,0x04,0xa2,0x02,0x80,0x00,0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01, +0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20,0x32,0x54,0x6a,0x85,0xd7,0xe6,0x83, +0x46,0x6c,0x94,0x58,0x3b,0x17,0xa4,0xa8,0x8b,0xea,0xea,0x11,0xe0,0x6e,0xc4,0x3c, +0xea,0xde,0xbb,0x2e,0x7d,0xa3,0xb6,0xbe,0x69,0xa0,0x82,0x01,0xd5,0x30,0x82,0x01, +0xd1,0x30,0x82,0x01,0x77,0xa0,0x03,0x02,0x01,0x02,0x02,0x14,0x13,0x09,0x38,0x76, +0x3a,0x38,0xef,0x36,0xac,0xc3,0xa5,0x7e,0xa5,0xad,0x56,0x50,0x8d,0x77,0x55,0x2c, +0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x30,0x3d,0x31,0x0b, +0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x0a,0x30,0x08,0x06, +0x03,0x55,0x04,0x08,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x07, +0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x0a,0x0c,0x01,0x54,0x31, +0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x03,0x0c,0x01,0x54,0x30,0x20,0x17,0x0d,0x32, +0x33,0x30,0x36,0x32,0x39,0x30,0x33,0x31,0x38,0x35,0x35,0x5a,0x18,0x0f,0x32,0x30, +0x35,0x30,0x31,0x31,0x31,0x34,0x30,0x33,0x31,0x38,0x35,0x35,0x5a,0x30,0x3d,0x31, +0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x0a,0x30,0x08, +0x06,0x03,0x55,0x04,0x08,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04, +0x07,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x0a,0x0c,0x01,0x54, +0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x03,0x0c,0x01,0x54,0x30,0x59,0x30,0x13, +0x06,0x07,0x2a,0x86,0x48,0xce,0x3d,0x02,0x01,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d, +0x03,0x01,0x07,0x03,0x42,0x00,0x04,0xfe,0xdb,0x26,0x60,0xf6,0x89,0x3d,0xa4,0x50, +0x1f,0x06,0x91,0x4e,0x07,0x86,0x70,0x2b,0xc0,0x7c,0x5e,0xb3,0xca,0xdc,0x1a,0x8b, +0x82,0xdd,0x41,0x8a,0x62,0x0f,0xba,0xd1,0xd7,0x80,0xc8,0x20,0x77,0xba,0xe7,0xe1, +0x36,0xf8,0x76,0x9a,0x54,0x6a,0x1b,0x67,0x45,0x3b,0xd7,0x85,0x84,0xbe,0x11,0xe6, +0x6c,0x70,0xd8,0x18,0x68,0xd8,0xa7,0xa3,0x53,0x30,0x51,0x30,0x1d,0x06,0x03,0x55, +0x1d,0x0e,0x04,0x16,0x04,0x14,0x94,0x15,0x14,0xad,0x7e,0xaf,0x63,0xa4,0x12,0x29, +0xaa,0xe4,0x26,0x54,0x7b,0x4e,0x2c,0xb9,0xdb,0xc8,0x30,0x1f,0x06,0x03,0x55,0x1d, +0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x94,0x15,0x14,0xad,0x7e,0xaf,0x63,0xa4,0x12, +0x29,0xaa,0xe4,0x26,0x54,0x7b,0x4e,0x2c,0xb9,0xdb,0xc8,0x30,0x0f,0x06,0x03,0x55, +0x1d,0x13,0x01,0x01,0xff,0x04,0x05,0x30,0x03,0x01,0x01,0xff,0x30,0x0a,0x06,0x08, +0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x03,0x48,0x00,0x30,0x45,0x02,0x21,0x00, +0xe6,0xb6,0x11,0x8d,0x75,0x3a,0x62,0xf3,0x08,0x17,0xce,0xa5,0x5a,0xcb,0x61,0xc7, +0x0a,0x33,0xdb,0x30,0x29,0x6b,0x5e,0xac,0xfc,0xaa,0xed,0x14,0xd1,0xd7,0xae,0x24, +0x02,0x20,0x2e,0x4d,0x70,0xc7,0x26,0xf7,0xea,0xa3,0x07,0x8a,0x6f,0x98,0x07,0xe1, +0xbc,0x38,0x13,0x88,0x17,0xdd,0x01,0x21,0x1e,0xb0,0xbb,0x32,0xfc,0x7a,0xc0,0xd5, +0x80,0x45,0x31,0x82,0x01,0x23,0x30,0x82,0x01,0x1f,0x02,0x01,0x01,0x30,0x55,0x30, +0x3d,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x0a, +0x30,0x08,0x06,0x03,0x55,0x04,0x08,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03, +0x55,0x04,0x07,0x0c,0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x0a,0x0c, +0x01,0x54,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x03,0x0c,0x01,0x54,0x02,0x14, +0x13,0x09,0x38,0x76,0x3a,0x38,0xef,0x36,0xac,0xc3,0xa5,0x7e,0xa5,0xad,0x56,0x50, +0x8d,0x77,0x55,0x2c,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02, +0x01,0x05,0x00,0xa0,0x5e,0x30,0x10,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37, +0x02,0x01,0x0c,0x31,0x02,0x30,0x00,0x30,0x19,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7, +0x0d,0x01,0x09,0x03,0x31,0x0c,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x02, +0x01,0x04,0x30,0x2f,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x04,0x31, +0x22,0x04,0x20,0x25,0xc1,0x32,0xc0,0x4f,0x1a,0xae,0x84,0xd2,0x6a,0xff,0x0e,0xc9, +0xe8,0x85,0xbc,0x38,0x63,0x7b,0x22,0x89,0x1c,0x97,0x29,0xc2,0x8f,0x70,0x40,0xc2, +0xdf,0x42,0x9a,0x30,0x0b,0x06,0x07,0x2a,0x86,0x48,0xce,0x3d,0x02,0x01,0x05,0x00, +0x04,0x47,0x30,0x45,0x02,0x20,0x07,0x66,0x32,0x9a,0x15,0x8f,0x39,0x0a,0xb0,0xe1, +0x80,0xc9,0x82,0x23,0xb8,0x99,0x54,0x4c,0xa7,0x65,0xf2,0x99,0x11,0x70,0x1e,0xdf, +0xf5,0x40,0x73,0x7a,0x8d,0xd1,0x02,0x21,0x00,0x84,0xe0,0xec,0x38,0x33,0x01,0x28, +0x2b,0x4b,0x72,0xed,0x6a,0x64,0xb7,0xaf,0x7a,0x34,0x4b,0x6b,0x69,0xf6,0x55,0x9a, +0x8e,0x0d,0xe9,0xc1,0x85,0x80,0x4d,0xef,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; + +static void test_verify_ecc_signature(void) +{ + HCERTSTORE store; + HCRYPTKEY key; + BCRYPT_KEY_HANDLE bkey; + HCRYPTMSG msg; + BOOL bret; + CERT_INFO *cert_info; + PCCERT_CONTEXT cert; + DWORD size; + CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA verify_para = { sizeof(verify_para) }; + HCRYPTOIDFUNCSET set; + void *import_func; + HCRYPTOIDFUNCADDR hfunc = NULL; + CMSG_CMS_SIGNER_INFO *signer_info; + + msg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING, 0, 0, 0, NULL, NULL); + ok(!!msg, "failed, error %#lx.\n", GetLastError()); + bret = CryptMsgUpdate(msg, msg_signed_ecc_prime256v1, sizeof(msg_signed_ecc_prime256v1), TRUE); + ok(bret, "failed, error %#lx.\n", GetLastError()); + store = CertOpenStore(CERT_STORE_PROV_MSG, X509_ASN_ENCODING, 0, 0, msg); + ok(!!store, "failed, error %#lx.\n", GetLastError()); + size = 0; + bret = CryptMsgGetParam(msg, CMSG_SIGNER_CERT_INFO_PARAM, 0, NULL, &size); + ok(bret, "failed, error %#lx.\n", GetLastError()); + cert_info = malloc(size); + bret = CryptMsgGetParam(msg, CMSG_SIGNER_CERT_INFO_PARAM, 0, cert_info, &size); + ok(bret, "failed, error %#lx.\n", GetLastError()); + cert = CertGetSubjectCertificateFromStore(store, X509_ASN_ENCODING, cert_info); + ok(!!cert, "failed, error %#lx.\n", GetLastError()); + + ok(!strcmp(cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, szOID_ECC_PUBLIC_KEY), + "got OID %s.\n", cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId); + size = 0; + bret = CryptMsgGetParam(msg, CMSG_CMS_SIGNER_INFO_PARAM, 0, NULL, &size); + ok(bret, "failed, error %#lx.\n", GetLastError()); + signer_info = malloc(size); + bret = CryptMsgGetParam(msg, CMSG_CMS_SIGNER_INFO_PARAM, 0, signer_info, &size); + ok(bret, "failed, error %#lx.\n", GetLastError()); + ok(!strcmp(signer_info->HashAlgorithm.pszObjId, szOID_NIST_sha256), "got %s.\n", + signer_info->HashAlgorithm.pszObjId); + ok(!strcmp(signer_info->HashEncryptionAlgorithm.pszObjId, szOID_ECC_PUBLIC_KEY), "got %s.\n", + signer_info->HashEncryptionAlgorithm.pszObjId); + + set = CryptInitOIDFunctionSet(CRYPT_OID_IMPORT_PUBLIC_KEY_INFO_FUNC, 0); + ok(!!set, "failed, error %#lx.\n", GetLastError()); + bret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, + 0, (void **)&import_func, &hfunc); + ok(!bret, "succeeded.\n"); + + bret = CryptImportPublicKeyInfo(0, X509_ASN_ENCODING, &cert->pCertInfo->SubjectPublicKeyInfo, &key); + ok(!bret && GetLastError() == CRYPT_E_ASN1_BADTAG, "got ret %d, error %#lx.\n", bret, GetLastError()); + + bret = CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, &cert->pCertInfo->SubjectPublicKeyInfo, 0, NULL, &bkey); + ok(bret, "failed, error %#lx.\n", GetLastError()); + BCryptDestroyKey(bkey); + + bret = CryptMsgControl(msg, 0, CMSG_CTRL_VERIFY_SIGNATURE, cert->pCertInfo); + todo_wine ok(bret, "failed, error %#lx.\n", GetLastError()); + + verify_para.dwSignerType = CMSG_VERIFY_SIGNER_CERT; + verify_para.pvSigner = (void *)cert; + bret = CryptMsgControl(msg, 0, CMSG_CTRL_VERIFY_SIGNATURE_EX, &verify_para); + todo_wine ok(bret, "failed, error %#lx.\n", GetLastError()); + + free(signer_info); + free(cert_info); + CertFreeCertificateContext(cert); + CertCloseStore(store, 0); + CryptMsgClose(msg); +} + START_TEST(msg) { /* Basic parameter checking tests */ @@ -3500,4 +3639,5 @@ START_TEST(msg) test_decode_msg();
test_msg_get_and_verify_signer(); + test_verify_ecc_signature(); }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/crypt32/oid.c | 2 ++ dlls/crypt32/tests/oid.c | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/dlls/crypt32/oid.c b/dlls/crypt32/oid.c index a4dcc3997f0..70b9e638ab7 100644 --- a/dlls/crypt32/oid.c +++ b/dlls/crypt32/oid.c @@ -1271,6 +1271,8 @@ static const struct OIDInfoConstructor { { 3, szOID_INFOSEC_mosaicKMandUpdSig, CALG_DSS_SIGN, L"mosaicKMandUpdSig", &mosaicFlagsBlob }, { 3, szOID_RSA_SMIMEalgESDH, CALG_DH_EPHEM, L"ESDH", &noNullBlob }, { 3, szOID_PKIX_NO_SIGNATURE, CALG_NO_SIGN, L"NOSIGN", NULL }, + { 3, szOID_ECC_PUBLIC_KEY, CALG_OID_INFO_PARAMETERS, L"ECC", NULL, + CRYPT_OID_INFO_ECC_PARAMETERS_ALGORITHM, L"" },
{ 4, szOID_RSA_SHA1RSA, CALG_SHA1, L"sha1RSA", &rsaSignBlob }, { 4, szOID_RSA_SHA256RSA, CALG_SHA_256, L"sha256RSA", &rsaSignBlob }, diff --git a/dlls/crypt32/tests/oid.c b/dlls/crypt32/tests/oid.c index 715d76b9c31..2423c819229 100644 --- a/dlls/crypt32/tests/oid.c +++ b/dlls/crypt32/tests/oid.c @@ -72,7 +72,8 @@ static const struct OIDToAlgID oidToAlgID[] = { { szOID_INFOSEC_mosaicKMandUpdSig, CALG_DSS_SIGN }, { szOID_NIST_sha256, CALG_SHA_256, -1 }, { szOID_NIST_sha384, CALG_SHA_384, -1 }, - { szOID_NIST_sha512, CALG_SHA_512, -1 } + { szOID_NIST_sha512, CALG_SHA_512, -1 }, + { szOID_ECC_PUBLIC_KEY, CALG_OID_INFO_PARAMETERS }, };
static const struct OIDToAlgID algIDToOID[] = { @@ -509,6 +510,7 @@ static void test_findOIDInfo(void) { static CHAR oid_rsa_md5[] = szOID_RSA_MD5, oid_sha256[] = szOID_NIST_sha256; static CHAR oid_ecdsa_sha256[] = szOID_ECDSA_SHA256; + static CHAR oid_ecc_public_key[] = szOID_ECC_PUBLIC_KEY; ALG_ID alg = CALG_SHA1; ALG_ID algs[2] = { CALG_MD5, CALG_RSA_SIGN }; const struct oid_info @@ -573,6 +575,17 @@ static void test_findOIDInfo(void) } else win_skip("Host does not support ECDSA_SHA256, skipping test\n"); + + info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, oid_ecc_public_key, 0); + ok(!!info, "got error %#lx.\n", GetLastError()); + ok(!strcmp(info->pszOID, oid_ecc_public_key), "got %s.\n", info->pszOID); + ok(!wcscmp(info->pwszName, L"ECC"), "got %s.\n", wine_dbgstr_w(info->pwszName)); + ok(info->dwGroupId == CRYPT_PUBKEY_ALG_OID_GROUP_ID, "got %lu.\n", info->dwGroupId); + ok(U(*info).Algid == CALG_OID_INFO_PARAMETERS, "got %d.\n", U(*info).Algid); + ok(!info->ExtraInfo.cbData, "got %ld.\n", info->ExtraInfo.cbData); + ok(!wcscmp(info->pwszCNGAlgid, CRYPT_OID_INFO_ECC_PARAMETERS_ALGORITHM), "got %s.\n", wine_dbgstr_w(info->pwszCNGAlgid)); + ok(info->pwszCNGExtraAlgid && !wcscmp(info->pwszCNGExtraAlgid, L""), "got %s.\n", + wine_dbgstr_w(info->pwszCNGExtraAlgid)); }
static void test_registerOIDInfo(void)
From: Paul Gofman pgofman@codeweavers.com
--- dlls/crypt32/msg.c | 63 +++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 34 deletions(-)
diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c index 63884d0c275..de5730bc064 100644 --- a/dlls/crypt32/msg.c +++ b/dlls/crypt32/msg.c @@ -43,6 +43,24 @@ typedef BOOL (*CryptMsgUpdateFunc)(HCRYPTMSG hCryptMsg, const BYTE *pbData, typedef BOOL (*CryptMsgControlFunc)(HCRYPTMSG hCryptMsg, DWORD dwFlags, DWORD dwCtrlType, const void *pvCtrlPara);
+static BOOL extract_hash(HCRYPTHASH hash, BYTE **data, DWORD *size) +{ + DWORD sz; + + *data = NULL; + sz = sizeof(*size); + if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *)size, &sz, 0)) return FALSE; + if (!(*data = CryptMemAlloc(*size))) + { + ERR("No memory.\n"); + return FALSE; + } + if (CryptGetHashParam(hash, HP_HASHVAL, *data, size, 0)) return TRUE; + CryptMemFree(*data); + *data = NULL; + return FALSE; +} + static BOOL CRYPT_DefaultMsgControl(HCRYPTMSG hCryptMsg, DWORD dwFlags, DWORD dwCtrlType, const void *pvCtrlPara) { @@ -412,18 +430,7 @@ static BOOL CRYPT_EncodePKCSDigestedData(CHashEncodeMsg *msg, void *pvData, &digestedData.ContentInfo.Content.cbData); } if (msg->base.state == MsgStateFinalized) - { - size = sizeof(DWORD); - ret = CryptGetHashParam(msg->hash, HP_HASHSIZE, - (LPBYTE)&digestedData.hash.cbData, &size, 0); - if (ret) - { - digestedData.hash.pbData = CryptMemAlloc( - digestedData.hash.cbData); - ret = CryptGetHashParam(msg->hash, HP_HASHVAL, - digestedData.hash.pbData, &digestedData.hash.cbData, 0); - } - } + ret = extract_hash(msg->hash, &digestedData.hash.pbData, &digestedData.hash.cbData); if (ret) ret = CRYPT_AsnEncodePKCSDigestedData(&digestedData, pvData, pcbData); @@ -1025,35 +1032,23 @@ static BOOL CSignedMsgData_AppendMessageDigestAttribute( CSignedMsgData *msg_data, DWORD signerIndex) { BOOL ret; - DWORD size; CRYPT_HASH_BLOB hash = { 0, NULL }, encodedHash = { 0, NULL }; char messageDigest[] = szOID_RSA_messageDigest; CRYPT_ATTRIBUTE messageDigestAttr = { messageDigest, 1, &encodedHash };
- size = sizeof(DWORD); - ret = CryptGetHashParam( - msg_data->signerHandles[signerIndex].contentHash, HP_HASHSIZE, - (LPBYTE)&hash.cbData, &size, 0); + if (!(ret = extract_hash(msg_data->signerHandles[signerIndex].contentHash, &hash.pbData, &hash.cbData))) + return FALSE; + + ret = CRYPT_AsnEncodeOctets(0, NULL, &hash, CRYPT_ENCODE_ALLOC_FLAG, NULL, (LPBYTE)&encodedHash.pbData, + &encodedHash.cbData); if (ret) { - hash.pbData = CryptMemAlloc(hash.cbData); - ret = CryptGetHashParam( - msg_data->signerHandles[signerIndex].contentHash, HP_HASHVAL, - hash.pbData, &hash.cbData, 0); - if (ret) - { - ret = CRYPT_AsnEncodeOctets(0, NULL, &hash, CRYPT_ENCODE_ALLOC_FLAG, - NULL, (LPBYTE)&encodedHash.pbData, &encodedHash.cbData); - if (ret) - { - ret = CRYPT_AppendAttribute( - &msg_data->info->rgSignerInfo[signerIndex].AuthAttrs, - &messageDigestAttr); - LocalFree(encodedHash.pbData); - } - } - CryptMemFree(hash.pbData); + ret = CRYPT_AppendAttribute( + &msg_data->info->rgSignerInfo[signerIndex].AuthAttrs, + &messageDigestAttr); + LocalFree(encodedHash.pbData); } + CryptMemFree(hash.pbData); return ret; }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/crypt32/cert.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c index b9645770ce1..0005d5db14a 100644 --- a/dlls/crypt32/cert.c +++ b/dlls/crypt32/cert.c @@ -2810,7 +2810,18 @@ static BOOL CNG_PrepareSignatureECC(BYTE *encoded_sig, DWORD encoded_size, BYTE return TRUE; }
-static BOOL CNG_PrepareSignature(CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert, +static BOOL cng_prepare_signature(const char *alg_oid, BYTE *encoded_sig, DWORD encoded_sig_len, + BYTE **sig_value, DWORD *sig_len) +{ + if (!strcmp(alg_oid, szOID_ECC_PUBLIC_KEY)) + return CNG_PrepareSignatureECC(encoded_sig, encoded_sig_len, sig_value, sig_len); + + FIXME("Unsupported public key type: %s\n", debugstr_a(alg_oid)); + SetLastError(NTE_BAD_ALGID); + return FALSE; +} + +static BOOL CNG_PrepareCertSignature(CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert, BYTE **sig_value, DWORD *sig_len) { BYTE *encoded_sig; @@ -2832,14 +2843,8 @@ static BOOL CNG_PrepareSignature(CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SI for (i = 0; i < signedCert->Signature.cbData; i++) encoded_sig[i] = signedCert->Signature.pbData[signedCert->Signature.cbData - i - 1];
- if (!strcmp(pubKeyInfo->Algorithm.pszObjId, szOID_ECC_PUBLIC_KEY)) - ret = CNG_PrepareSignatureECC(encoded_sig, signedCert->Signature.cbData, sig_value, sig_len); - else - { - FIXME("Unsupported public key type: %s\n", debugstr_a(pubKeyInfo->Algorithm.pszObjId)); - SetLastError(NTE_BAD_ALGID); - } - + ret = cng_prepare_signature(pubKeyInfo->Algorithm.pszObjId, encoded_sig, signedCert->Signature.cbData, + sig_value, sig_len); CryptMemFree(encoded_sig); return ret; } @@ -2859,7 +2864,7 @@ static BOOL CNG_VerifySignature(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEncodi ret = CNG_CalcHash(info->pwszCNGAlgid, signedCert, &hash_value, &hash_len); if (ret) { - ret = CNG_PrepareSignature(pubKeyInfo, signedCert, &sig_value, &sig_len); + ret = CNG_PrepareCertSignature(pubKeyInfo, signedCert, &sig_value, &sig_len); if (ret) { status = BCryptVerifySignature(key, NULL, hash_value, hash_len, sig_value, sig_len, 0);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/crypt32/cert.c | 2 +- dlls/crypt32/crypt32_private.h | 2 ++ dlls/crypt32/msg.c | 42 ++++++++++++++++++++++++++++++---- dlls/crypt32/tests/msg.c | 4 ++-- 4 files changed, 42 insertions(+), 8 deletions(-)
diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c index 0005d5db14a..a6fefb30b05 100644 --- a/dlls/crypt32/cert.c +++ b/dlls/crypt32/cert.c @@ -2810,7 +2810,7 @@ static BOOL CNG_PrepareSignatureECC(BYTE *encoded_sig, DWORD encoded_size, BYTE return TRUE; }
-static BOOL cng_prepare_signature(const char *alg_oid, BYTE *encoded_sig, DWORD encoded_sig_len, +BOOL cng_prepare_signature(const char *alg_oid, BYTE *encoded_sig, DWORD encoded_sig_len, BYTE **sig_value, DWORD *sig_len) { if (!strcmp(alg_oid, szOID_ECC_PUBLIC_KEY)) diff --git a/dlls/crypt32/crypt32_private.h b/dlls/crypt32/crypt32_private.h index d61fcfb7d6e..b5beb1b8a0e 100644 --- a/dlls/crypt32/crypt32_private.h +++ b/dlls/crypt32/crypt32_private.h @@ -23,6 +23,8 @@ #include "wine/unixlib.h"
BOOL CNG_ImportPubKey(CERT_PUBLIC_KEY_INFO *pubKeyInfo, BCRYPT_KEY_HANDLE *key) DECLSPEC_HIDDEN; +BOOL cng_prepare_signature(const char *alg_oid, BYTE *encoded_sig, DWORD encoded_sig_len, + BYTE **sig_value, DWORD *sig_len) DECLSPEC_HIDDEN;
/* a few asn.1 tags we need */ #define ASN_BOOL (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x01) diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c index de5730bc064..94642ecfce2 100644 --- a/dlls/crypt32/msg.c +++ b/dlls/crypt32/msg.c @@ -3316,24 +3316,56 @@ static BOOL CDecodeHashMsg_VerifyHash(CDecodeMsg *msg) return ret; }
+static BOOL cng_verify_msg_signature(CMSG_CMS_SIGNER_INFO *signer, HCRYPTHASH hash, CERT_PUBLIC_KEY_INFO *key_info) +{ + BYTE *hash_value, *sig_value = NULL; + DWORD hash_len, sig_len; + BCRYPT_KEY_HANDLE key; + BOOL ret = FALSE; + NTSTATUS status; + + if (!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, key_info, 0, NULL, &key)) return FALSE; + if (!extract_hash(hash, &hash_value, &hash_len)) goto done; + if (!cng_prepare_signature(key_info->Algorithm.pszObjId, signer->EncryptedHash.pbData, + signer->EncryptedHash.cbData, &sig_value, &sig_len)) goto done; + status = BCryptVerifySignature(key, NULL, hash_value, hash_len, sig_value, sig_len, 0); + if (status) + { + FIXME("Failed to verify signature: %08lx.\n", status); + SetLastError(RtlNtStatusToDosError(status)); + } + ret = !status; +done: + CryptMemFree(sig_value); + CryptMemFree(hash_value); + BCryptDestroyKey(key); + return ret; +} + static BOOL CDecodeSignedMsg_VerifySignatureWithKey(CDecodeMsg *msg, HCRYPTPROV prov, DWORD signerIndex, PCERT_PUBLIC_KEY_INFO keyInfo) { + HCRYPTHASH hash; HCRYPTKEY key; BOOL ret; + ALG_ID alg_id = 0; + + if (msg->u.signed_data.info->rgSignerInfo[signerIndex].AuthAttrs.cAttr) + hash = msg->u.signed_data.signerHandles[signerIndex].authAttrHash; + else + hash = msg->u.signed_data.signerHandles[signerIndex].contentHash; + + if (keyInfo->Algorithm.pszObjId) alg_id = CertOIDToAlgId(keyInfo->Algorithm.pszObjId); + if (alg_id == CALG_OID_INFO_PARAMETERS || alg_id == CALG_OID_INFO_CNG_ONLY) + return cng_verify_msg_signature(&msg->u.signed_data.info->rgSignerInfo[signerIndex], hash, keyInfo);
if (!prov) prov = msg->crypt_prov; ret = CryptImportPublicKeyInfo(prov, X509_ASN_ENCODING, keyInfo, &key); if (ret) { - HCRYPTHASH hash; CRYPT_HASH_BLOB reversedHash;
- if (msg->u.signed_data.info->rgSignerInfo[signerIndex].AuthAttrs.cAttr) - hash = msg->u.signed_data.signerHandles[signerIndex].authAttrHash; - else - hash = msg->u.signed_data.signerHandles[signerIndex].contentHash; ret = CRYPT_ConstructBlob(&reversedHash, &msg->u.signed_data.info->rgSignerInfo[signerIndex].EncryptedHash); if (ret) diff --git a/dlls/crypt32/tests/msg.c b/dlls/crypt32/tests/msg.c index c663909ed7c..d8ac7f75d78 100644 --- a/dlls/crypt32/tests/msg.c +++ b/dlls/crypt32/tests/msg.c @@ -3608,12 +3608,12 @@ static void test_verify_ecc_signature(void) BCryptDestroyKey(bkey);
bret = CryptMsgControl(msg, 0, CMSG_CTRL_VERIFY_SIGNATURE, cert->pCertInfo); - todo_wine ok(bret, "failed, error %#lx.\n", GetLastError()); + ok(bret, "failed, error %#lx.\n", GetLastError());
verify_para.dwSignerType = CMSG_VERIFY_SIGNER_CERT; verify_para.pvSigner = (void *)cert; bret = CryptMsgControl(msg, 0, CMSG_CTRL_VERIFY_SIGNATURE_EX, &verify_para); - todo_wine ok(bret, "failed, error %#lx.\n", GetLastError()); + ok(bret, "failed, error %#lx.\n", GetLastError());
free(signer_info); free(cert_info);
I tested this against the failing Age of Empires binaries, both by applying it and using the 'trustchk' program linked in bug #55165, and by running AoE2 against the latest 'bleeding-edge' Proton Experimental build (which includes this patch).
Everything works as expected! Thanks!
This merge request was approved by Hans Leidekker.