diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c index 90dd827ce75..4ffa088b20f 100644 --- a/dlls/crypt32/cert.c +++ b/dlls/crypt32/cert.c @@ -29,6 +29,94 @@ WINE_DEFAULT_DEBUG_CHANNEL(crypt); +BOOL WINAPI CertCompareCertificate(DWORD dwCertEncodingType, + PCERT_INFO pCertId1, PCERT_INFO pCertId2) +{ + TRACE("(%08lx, %p, %p)\n", dwCertEncodingType, pCertId1, pCertId2); + + return CertCompareCertificateName(dwCertEncodingType, &pCertId1->Issuer, + &pCertId2->Issuer) && CertCompareIntegerBlob(&pCertId1->SerialNumber, + &pCertId2->SerialNumber); +} + +BOOL WINAPI CertCompareCertificateName(DWORD dwCertEncodingType, + PCERT_NAME_BLOB pCertName1, PCERT_NAME_BLOB pCertName2) +{ + BOOL ret; + + TRACE("(%08lx, %p, %p)\n", dwCertEncodingType, pCertName1, pCertName2); + + if (pCertName1->cbData == pCertName2->cbData) + { + if (pCertName1->cbData) + ret = !memcmp(pCertName1->pbData, pCertName2->pbData, + pCertName1->cbData); + else + ret = TRUE; + } + else + ret = FALSE; + return ret; +} + +/* Returns the number of significant bytes in pInt, where a byte is + * insignificant if it's a leading 0 for positive numbers or a leading 0xff + * for negative numbers. pInt is assumed to be little-endian. + */ +static DWORD CRYPT_significantBytes(PCRYPT_INTEGER_BLOB pInt) +{ + DWORD ret = pInt->cbData; + + while (ret > 1) + { + if (pInt->pbData[ret - 2] <= 0x7f && pInt->pbData[ret - 1] == 0) + ret--; + else if (pInt->pbData[ret - 2] >= 0x80 && pInt->pbData[ret - 1] == 0xff) + ret--; + else + break; + } + return ret; +} + +BOOL WINAPI CertCompareIntegerBlob(PCRYPT_INTEGER_BLOB pInt1, + PCRYPT_INTEGER_BLOB pInt2) +{ + BOOL ret; + DWORD cb1, cb2; + + TRACE("(%p, %p)\n", pInt1, pInt2); + + cb1 = CRYPT_significantBytes(pInt1); + cb2 = CRYPT_significantBytes(pInt2); + if (cb1 == cb2) + ret = !memcmp(pInt1->pbData, pInt1->pbData, cb1); + else + ret = FALSE; + return ret; +} + +BOOL WINAPI CertComparePublicKeyInfo(DWORD dwCertEncodingType, + PCERT_PUBLIC_KEY_INFO pPublicKey1, PCERT_PUBLIC_KEY_INFO pPublicKey2) +{ + BOOL ret; + + TRACE("(%08lx, %p, %p)\n", dwCertEncodingType, pPublicKey1, pPublicKey2); + + if (pPublicKey1->PublicKey.cbData == pPublicKey2->PublicKey.cbData && + pPublicKey1->PublicKey.cUnusedBits == pPublicKey2->PublicKey.cUnusedBits) + { + if (pPublicKey2->PublicKey.cbData) + ret = !memcmp(pPublicKey1->PublicKey.pbData, + pPublicKey2->PublicKey.pbData, pPublicKey1->PublicKey.cbData); + else + ret = TRUE; + } + else + ret = FALSE; + return ret; +} + PCRYPT_ATTRIBUTE WINAPI CertFindAttribute(LPCSTR pszObjId, DWORD cAttr, CRYPT_ATTRIBUTE rgAttr[]) { diff --git a/dlls/crypt32/crypt32.spec b/dlls/crypt32/crypt32.spec index 836c6b9722f..73744ed4e23 100644 --- a/dlls/crypt32/crypt32.spec +++ b/dlls/crypt32/crypt32.spec @@ -11,10 +11,10 @@ @ stdcall CertAddStoreToCollection(ptr ptr long long) @ stdcall CertAlgIdToOID(long) @ stdcall CertCloseStore(ptr long) -@ stub CertCompareCertificate -@ stub CertCompareCertificateName -@ stub CertCompareIntegerBlob -@ stub CertComparePublicKeyInfo +@ stdcall CertCompareCertificate(long ptr ptr) +@ stdcall CertCompareCertificateName(long ptr ptr) +@ stdcall CertCompareIntegerBlob(ptr ptr) +@ stdcall CertComparePublicKeyInfo(long ptr ptr) @ stdcall CertControlStore(long long long ptr) @ stdcall CertCreateCRLContext(long ptr long) @ stdcall CertCreateCTLContext(long ptr long) diff --git a/dlls/crypt32/tests/cert.c b/dlls/crypt32/tests/cert.c index 6c862b7cad6..88ea1d743ab 100644 --- a/dlls/crypt32/tests/cert.c +++ b/dlls/crypt32/tests/cert.c @@ -682,6 +682,160 @@ static void testKeyUsage(void) } } +static void testCompareCertName(void) +{ + static const BYTE bogus[] = { 1, 2, 3, 4 }; + static const BYTE bogusPrime[] = { 0, 1, 2, 3, 4 }; + static const BYTE emptyPrime[] = { 0x30, 0x00, 0x01 }; + BOOL ret; + CERT_NAME_BLOB blob1, blob2; + + /* crashes + ret = CertCompareCertificateName(0, NULL, NULL); + */ + /* An empty name checks against itself.. */ + blob1.pbData = (LPBYTE)emptyCert; + blob1.cbData = sizeof(emptyCert); + ret = CertCompareCertificateName(0, &blob1, &blob1); + ok(ret, "CertCompareCertificateName failed: %08lx\n", GetLastError()); + /* It doesn't have to be a valid encoded name.. */ + blob1.pbData = (LPBYTE)bogus; + blob1.cbData = sizeof(bogus); + ret = CertCompareCertificateName(0, &blob1, &blob1); + ok(ret, "CertCompareCertificateName failed: %08lx\n", GetLastError()); + /* Leading zeroes matter.. */ + blob2.pbData = (LPBYTE)bogusPrime; + blob2.cbData = sizeof(bogusPrime); + ret = CertCompareCertificateName(0, &blob1, &blob2); + ok(!ret, "Expected failure\n"); + /* As do trailing extra bytes. */ + blob2.pbData = (LPBYTE)emptyPrime; + blob2.cbData = sizeof(emptyPrime); + ret = CertCompareCertificateName(0, &blob1, &blob2); + ok(!ret, "Expected failure\n"); +} + +static const BYTE int1[] = { 0x88, 0xff, 0xff, 0xff }; +static const BYTE int2[] = { 0x88, 0xff }; +static const BYTE int3[] = { 0x23, 0xff }; +static const BYTE int4[] = { 0x7f, 0x00 }; +static const BYTE int5[] = { 0x7f }; +static const BYTE int6[] = { 0x80, 0x00, 0x00, 0x00 }; +static const BYTE int7[] = { 0x80, 0x00 }; + +struct IntBlobTest +{ + CRYPT_INTEGER_BLOB blob1; + CRYPT_INTEGER_BLOB blob2; + BOOL areEqual; +} intBlobs[] = { + { { sizeof(int1), (LPBYTE)int1 }, { sizeof(int2), (LPBYTE)int2 }, TRUE }, + { { sizeof(int3), (LPBYTE)int3 }, { sizeof(int3), (LPBYTE)int3 }, TRUE }, + { { sizeof(int4), (LPBYTE)int4 }, { sizeof(int5), (LPBYTE)int5 }, TRUE }, + { { sizeof(int6), (LPBYTE)int6 }, { sizeof(int7), (LPBYTE)int7 }, TRUE }, + { { sizeof(int1), (LPBYTE)int1 }, { sizeof(int7), (LPBYTE)int7 }, FALSE }, +}; + +static void testCompareIntegerBlob(void) +{ + DWORD i; + BOOL ret; + + for (i = 0; i < sizeof(intBlobs) / sizeof(intBlobs[0]); i++) + { + ret = CertCompareIntegerBlob(&intBlobs[i].blob1, &intBlobs[i].blob2); + ok(ret == intBlobs[i].areEqual, + "%ld: expected blobs %s compare\n", i, intBlobs[i].areEqual ? + "to" : "not to"); + } +} + +static void testComparePublicKeyInfo(void) +{ + BOOL ret; + CERT_PUBLIC_KEY_INFO info1 = { { 0 } }, info2 = { { 0 } }; + static const BYTE bits1[] = { 1, 0 }; + static const BYTE bits2[] = { 0 }; + static const BYTE bits3[] = { 1 }; + + /* crashes + ret = CertComparePublicKeyInfo(0, NULL, NULL); + */ + /* Empty public keys compare */ + ret = CertComparePublicKeyInfo(0, &info1, &info2); + ok(ret, "CertComparePublicKeyInfo failed: %08lx\n", GetLastError()); + /* Different OIDs appear to compare */ + info1.Algorithm.pszObjId = szOID_RSA_RSA; + info2.Algorithm.pszObjId = szOID_RSA_SHA1RSA; + ret = CertComparePublicKeyInfo(0, &info1, &info2); + ok(ret, "CertComparePublicKeyInfo failed: %08lx\n", GetLastError()); + info2.Algorithm.pszObjId = szOID_X957_DSA; + ret = CertComparePublicKeyInfo(0, &info1, &info2); + ok(ret, "CertComparePublicKeyInfo failed: %08lx\n", GetLastError()); + info1.PublicKey.cbData = sizeof(bits1); + info1.PublicKey.pbData = (LPBYTE)bits1; + info1.PublicKey.cUnusedBits = 0; + info2.PublicKey.cbData = sizeof(bits1); + info2.PublicKey.pbData = (LPBYTE)bits1; + info2.PublicKey.cUnusedBits = 0; + ret = CertComparePublicKeyInfo(0, &info1, &info2); + ok(ret, "CertComparePublicKeyInfo failed: %08lx\n", GetLastError()); + /* Even though they compare in their used bits, these do not compare */ + info1.PublicKey.cbData = sizeof(bits2); + info1.PublicKey.pbData = (LPBYTE)bits2; + info1.PublicKey.cUnusedBits = 0; + info2.PublicKey.cbData = sizeof(bits3); + info2.PublicKey.pbData = (LPBYTE)bits3; + info2.PublicKey.cUnusedBits = 1; + ret = CertComparePublicKeyInfo(0, &info1, &info2); + /* Simple (non-comparing) case */ + ok(!ret, "Expected keys not to compare\n"); + info2.PublicKey.cbData = sizeof(bits1); + info2.PublicKey.pbData = (LPBYTE)bits1; + info2.PublicKey.cUnusedBits = 0; + ret = CertComparePublicKeyInfo(0, &info1, &info2); + ok(!ret, "Expected keys not to compare\n"); +} + +static const BYTE subjectName2[] = { 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x0a, 0x41, 0x6c, 0x65, 0x78, 0x20, 0x4c, 0x61, + 0x6e, 0x67, 0x00 }; + +static const BYTE serialNum[] = { 1 }; + +void testCompareCert(void) +{ + CERT_INFO info1 = { 0 }, info2 = { 0 }; + BOOL ret; + + /* Crashes + ret = CertCompareCertificate(X509_ASN_ENCODING, NULL, NULL); + */ + + /* Certs with the same issuer and serial number are equal, even if they + * differ in other respects (like subject). + */ + info1.SerialNumber.pbData = (LPBYTE)serialNum; + info1.SerialNumber.cbData = sizeof(serialNum); + info1.Issuer.pbData = (LPBYTE)subjectName; + info1.Issuer.cbData = sizeof(subjectName); + info1.Subject.pbData = (LPBYTE)subjectName2; + info1.Subject.cbData = sizeof(subjectName2); + info2.SerialNumber.pbData = (LPBYTE)serialNum; + info2.SerialNumber.cbData = sizeof(serialNum); + info2.Issuer.pbData = (LPBYTE)subjectName; + info2.Issuer.cbData = sizeof(subjectName); + info2.Subject.pbData = (LPBYTE)subjectName; + info2.Subject.cbData = sizeof(subjectName); + ret = CertCompareCertificate(X509_ASN_ENCODING, &info1, &info2); + ok(ret, "Expected certs to be equal\n"); + + info2.Issuer.pbData = (LPBYTE)subjectName2; + info2.Issuer.cbData = sizeof(subjectName2); + ret = CertCompareCertificate(X509_ASN_ENCODING, &info1, &info2); + ok(!ret, "Expected certs not to be equal\n"); +} + START_TEST(cert) { init_function_pointers(); @@ -689,4 +843,8 @@ START_TEST(cert) testCertSigs(); testCreateSelfSignCert(); testKeyUsage(); + testCompareCertName(); + testCompareIntegerBlob(); + testComparePublicKeyInfo(); + testCompareCert(); } diff --git a/include/wincrypt.h b/include/wincrypt.h index 7017f5ae61c..cfedfb4c089 100644 --- a/include/wincrypt.h +++ b/include/wincrypt.h @@ -2693,6 +2693,15 @@ BOOL WINAPI CertAddSerializedElementToStore(HCERTSTORE hCertStore, const BYTE *pbElement, DWORD cbElement, DWORD dwAddDisposition, DWORD dwFlags, DWORD dwContextTypeFlags, DWORD *pdwContentType, const void **ppvContext); +BOOL WINAPI CertCompareCertificate(DWORD dwCertEncodingType, + PCERT_INFO pCertId1, PCERT_INFO pCertId2); +BOOL WINAPI CertCompareCertificateName(DWORD dwCertEncodingType, + PCERT_NAME_BLOB pCertName1, PCERT_NAME_BLOB pCertName2); +BOOL WINAPI CertCompareIntegerBlob(PCRYPT_INTEGER_BLOB pInt1, + PCRYPT_INTEGER_BLOB pInt2); +BOOL WINAPI CertComparePublicKeyInfo(DWORD dwCertEncodingType, + PCERT_PUBLIC_KEY_INFO pPublicKey1, PCERT_PUBLIC_KEY_INFO pPublicKey2); + const void *CertCreateContext(DWORD dwContextType, DWORD dwEncodingType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, PCERT_CREATE_CONTEXT_PARA pCreatePara);