diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c index f921b18ec3c..c969edb12a1 100644 --- a/dlls/crypt32/cert.c +++ b/dlls/crypt32/cert.c @@ -323,3 +323,262 @@ BOOL WINAPI CryptVerifyMessageSignature(/*PCRYPT_VERIFY_MESSAGE_PARA*/ void* pVe pbDecoded, pcbDecoded, ppSignerCert); return FALSE; } + +BOOL WINAPI CertGetEnhancedKeyUsage(PCCERT_CONTEXT pCertContext, DWORD dwFlags, + PCERT_ENHKEY_USAGE pUsage, DWORD *pcbUsage) +{ + PCERT_ENHKEY_USAGE usage = NULL; + DWORD bytesNeeded; + BOOL ret = TRUE; + + TRACE("(%p, %08lx, %p, %ld)\n", pCertContext, dwFlags, pUsage, *pcbUsage); + + if (!pCertContext || !pcbUsage) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (!(dwFlags & CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG)) + { + DWORD propSize = 0; + + if (CertGetCertificateContextProperty(pCertContext, + CERT_ENHKEY_USAGE_PROP_ID, NULL, &propSize)) + { + LPBYTE buf = CryptMemAlloc(propSize); + + if (buf) + { + if (CertGetCertificateContextProperty(pCertContext, + CERT_ENHKEY_USAGE_PROP_ID, buf, &propSize)) + { + ret = CryptDecodeObjectEx(pCertContext->dwCertEncodingType, + X509_ENHANCED_KEY_USAGE, buf, propSize, + CRYPT_ENCODE_ALLOC_FLAG, NULL, &usage, &bytesNeeded); + } + CryptMemFree(buf); + } + } + } + if (!usage && !(dwFlags & CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG)) + { + PCERT_EXTENSION ext = CertFindExtension(szOID_ENHANCED_KEY_USAGE, + pCertContext->pCertInfo->cExtension, + pCertContext->pCertInfo->rgExtension); + + if (ext) + { + ret = CryptDecodeObjectEx(pCertContext->dwCertEncodingType, + X509_ENHANCED_KEY_USAGE, ext->Value.pbData, ext->Value.cbData, + CRYPT_ENCODE_ALLOC_FLAG, NULL, &usage, &bytesNeeded); + } + } + if (!usage) + { + /* If a particular location is specified, this should fail. Otherwise + * it should succeed with an empty usage. (This is true on Win2k and + * later, which we emulate.) + */ + if (dwFlags) + { + SetLastError(CRYPT_E_NOT_FOUND); + ret = FALSE; + } + else + bytesNeeded = sizeof(CERT_ENHKEY_USAGE); + } + + if (ret) + { + if (!pUsage) + *pcbUsage = bytesNeeded; + else if (*pcbUsage < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + *pcbUsage = bytesNeeded; + ret = FALSE; + } + else + { + *pcbUsage = bytesNeeded; + if (usage) + { + DWORD i; + LPSTR nextOID = (LPSTR)((LPBYTE)pUsage + + sizeof(CERT_ENHKEY_USAGE) + + usage->cUsageIdentifier * sizeof(LPSTR)); + + pUsage->cUsageIdentifier = usage->cUsageIdentifier; + pUsage->rgpszUsageIdentifier = (LPSTR *)((LPBYTE)pUsage + + sizeof(CERT_ENHKEY_USAGE)); + for (i = 0; i < usage->cUsageIdentifier; i++) + { + pUsage->rgpszUsageIdentifier[i] = nextOID; + strcpy(nextOID, usage->rgpszUsageIdentifier[i]); + nextOID += strlen(nextOID) + 1; + } + } + else + pUsage->cUsageIdentifier = 0; + } + } + if (usage) + LocalFree(usage); + TRACE("returning %d\n", ret); + return ret; +} + +BOOL WINAPI CertSetEnhancedKeyUsage(PCCERT_CONTEXT pCertContext, + PCERT_ENHKEY_USAGE pUsage) +{ + BOOL ret; + + TRACE("(%p, %p)\n", pCertContext, pUsage); + + if (pUsage) + { + CRYPT_DATA_BLOB blob = { 0, NULL }; + + ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, + pUsage, CRYPT_ENCODE_ALLOC_FLAG, NULL, &blob.pbData, &blob.cbData); + if (ret) + { + ret = CertSetCertificateContextProperty(pCertContext, + CERT_ENHKEY_USAGE_PROP_ID, 0, &blob); + LocalFree(blob.pbData); + } + } + else + ret = CertSetCertificateContextProperty(pCertContext, + CERT_ENHKEY_USAGE_PROP_ID, 0, NULL); + return ret; +} + +BOOL WINAPI CertAddEnhancedKeyUsageIdentifier(PCCERT_CONTEXT pCertContext, + LPCSTR pszUsageIdentifier) +{ + BOOL ret; + DWORD size; + + TRACE("(%p, %s)\n", pCertContext, debugstr_a(pszUsageIdentifier)); + + if (CertGetEnhancedKeyUsage(pCertContext, + CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, NULL, &size)) + { + PCERT_ENHKEY_USAGE usage = CryptMemAlloc(size); + + if (usage) + { + ret = CertGetEnhancedKeyUsage(pCertContext, + CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, usage, &size); + if (ret) + { + PCERT_ENHKEY_USAGE newUsage = CryptMemAlloc(size + + sizeof(LPSTR) + strlen(pszUsageIdentifier) + 1); + + if (newUsage) + { + LPSTR nextOID; + DWORD i; + + newUsage->rgpszUsageIdentifier = + (LPSTR *)((LPBYTE)newUsage + sizeof(CERT_ENHKEY_USAGE)); + nextOID = (LPSTR)((LPBYTE)newUsage->rgpszUsageIdentifier + + (usage->cUsageIdentifier + 1) * sizeof(LPSTR)); + for (i = 0; i < usage->cUsageIdentifier; i++) + { + newUsage->rgpszUsageIdentifier[i] = nextOID; + strcpy(nextOID, usage->rgpszUsageIdentifier[i]); + nextOID += strlen(nextOID) + 1; + } + newUsage->rgpszUsageIdentifier[i] = nextOID; + strcpy(nextOID, pszUsageIdentifier); + newUsage->cUsageIdentifier = i + 1; + ret = CertSetEnhancedKeyUsage(pCertContext, newUsage); + CryptMemFree(newUsage); + } + } + CryptMemFree(usage); + } + else + ret = FALSE; + } + else + { + PCERT_ENHKEY_USAGE usage = CryptMemAlloc(sizeof(CERT_ENHKEY_USAGE) + + sizeof(LPSTR) + strlen(pszUsageIdentifier) + 1); + + if (usage) + { + usage->rgpszUsageIdentifier = + (LPSTR *)((LPBYTE)usage + sizeof(CERT_ENHKEY_USAGE)); + usage->rgpszUsageIdentifier[0] = (LPSTR)((LPBYTE)usage + + sizeof(CERT_ENHKEY_USAGE) + sizeof(LPSTR)); + strcpy(usage->rgpszUsageIdentifier[0], pszUsageIdentifier); + usage->cUsageIdentifier = 1; + ret = CertSetEnhancedKeyUsage(pCertContext, usage); + CryptMemFree(usage); + } + else + ret = FALSE; + } + return ret; +} + +BOOL WINAPI CertRemoveEnhancedKeyUsageIdentifier(PCCERT_CONTEXT pCertContext, + LPCSTR pszUsageIdentifier) +{ + BOOL ret; + DWORD size; + CERT_ENHKEY_USAGE usage; + + TRACE("(%p, %s)\n", pCertContext, debugstr_a(pszUsageIdentifier)); + + size = sizeof(usage); + ret = CertGetEnhancedKeyUsage(pCertContext, + CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, &usage, &size); + if (!ret && GetLastError() == ERROR_MORE_DATA) + { + PCERT_ENHKEY_USAGE pUsage = CryptMemAlloc(size); + + if (pUsage) + { + ret = CertGetEnhancedKeyUsage(pCertContext, + CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, pUsage, &size); + if (ret) + { + if (pUsage->cUsageIdentifier) + { + DWORD i; + BOOL found = FALSE; + + for (i = 0; i < pUsage->cUsageIdentifier; i++) + { + if (!strcmp(pUsage->rgpszUsageIdentifier[i], + pszUsageIdentifier)) + found = TRUE; + if (found && i < pUsage->cUsageIdentifier - 1) + pUsage->rgpszUsageIdentifier[i] = + pUsage->rgpszUsageIdentifier[i + 1]; + } + pUsage->cUsageIdentifier--; + /* Remove the usage if it's empty */ + if (pUsage->cUsageIdentifier) + ret = CertSetEnhancedKeyUsage(pCertContext, pUsage); + else + ret = CertSetEnhancedKeyUsage(pCertContext, NULL); + } + } + CryptMemFree(pUsage); + } + else + ret = FALSE; + } + else + { + /* it fit in an empty usage, therefore there's nothing to remove */ + ret = TRUE; + } + return ret; +} diff --git a/dlls/crypt32/crypt32.spec b/dlls/crypt32/crypt32.spec index e13c9e62411..1ba868bd837 100644 --- a/dlls/crypt32/crypt32.spec +++ b/dlls/crypt32/crypt32.spec @@ -6,7 +6,7 @@ @ stdcall CertAddEncodedCertificateToStore(long long ptr long long ptr) @ stub CertAddEncodedCertificateToSystemStoreA @ stub CertAddEncodedCertificateToSystemStoreW -@ stub CertAddEnhancedKeyUsageIdentifier +@ stdcall CertAddEnhancedKeyUsageIdentifier(ptr str) @ stdcall CertAddSerializedElementToStore(ptr ptr long long long long ptr ptr) @ stdcall CertAddStoreToCollection(ptr ptr long long) @ stdcall CertAlgIdToOID(long) @@ -49,7 +49,7 @@ @ stdcall CertGetCTLContextProperty(ptr long ptr ptr) @ stub CertGetCertificateChain @ stdcall CertGetCertificateContextProperty(ptr long ptr ptr) -@ stub CertGetEnhancedKeyUsage +@ stdcall CertGetEnhancedKeyUsage(ptr long ptr ptr) @ stub CertGetIntendedKeyUsage @ stub CertGetIssuerCertificateFromStore @ stdcall CertGetNameStringA(ptr long long ptr ptr long) @@ -65,7 +65,7 @@ @ stdcall CertOpenSystemStoreW(long wstr) @ stdcall CertRDNValueToStrA(long ptr ptr long) @ stdcall CertRDNValueToStrW(long ptr ptr long) -@ stub CertRemoveEnhancedKeyUsageIdentifier +@ stdcall CertRemoveEnhancedKeyUsageIdentifier(ptr str) @ stdcall CertRemoveStoreFromCollection(long long) @ stdcall CertSaveStore(long long long long ptr long) @ stdcall CertSerializeCRLStoreElement(ptr long ptr ptr) @@ -74,7 +74,7 @@ @ stdcall CertSetCRLContextProperty(ptr long long ptr) @ stdcall CertSetCTLContextProperty(ptr long long ptr) @ stdcall CertSetCertificateContextProperty(ptr long long ptr) -@ stub CertSetEnhancedKeyUsage +@ stdcall CertSetEnhancedKeyUsage(ptr ptr) @ stub CertStrToNameA @ stub CertStrToNameW @ stub CertVerifyCRLRevocation diff --git a/dlls/crypt32/tests/cert.c b/dlls/crypt32/tests/cert.c index 2649e15a180..826e9be55ff 100644 --- a/dlls/crypt32/tests/cert.c +++ b/dlls/crypt32/tests/cert.c @@ -299,9 +299,315 @@ static void testCertSigs(void) CRYPT_DELETEKEYSET); } +static const BYTE bigCert[] = { 0x30, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x02, 0x06, + 0x00, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x0a, 0x4a, 0x75, 0x61, 0x6e, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x00, 0x30, 0x22, + 0x18, 0x0f, 0x31, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x31, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, + 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x15, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0a, 0x4a, 0x75, 0x61, 0x6e, 0x20, + 0x4c, 0x61, 0x6e, 0x67, 0x00, 0x30, 0x07, 0x30, 0x02, 0x06, 0x00, 0x03, 0x01, + 0x00, 0xa3, 0x16, 0x30, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01 }; + +static const LPCSTR keyUsages[] = { szOID_PKIX_KP_CODE_SIGNING, + szOID_PKIX_KP_CLIENT_AUTH, szOID_RSA_RSA }; + +static const BYTE certWithUsage[] = { 0x30, 0x81, 0x93, 0x02, 0x01, 0x01, 0x30, + 0x02, 0x06, 0x00, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x0a, 0x4a, 0x75, 0x61, 0x6e, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x00, + 0x30, 0x22, 0x18, 0x0f, 0x31, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x31, 0x36, 0x30, 0x31, 0x30, + 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x15, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0a, 0x4a, 0x75, 0x61, + 0x6e, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x00, 0x30, 0x07, 0x30, 0x02, 0x06, 0x00, + 0x03, 0x01, 0x00, 0xa3, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x1d, + 0x25, 0x01, 0x01, 0xff, 0x04, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x03, 0x03, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x03, 0x02, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 }; + +static void testKeyUsage(void) +{ + BOOL ret; + PCCERT_CONTEXT context; + DWORD size; + + /* Test base cases */ + ret = CertGetEnhancedKeyUsage(NULL, 0, NULL, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %08lx\n", GetLastError()); + size = 1; + ret = CertGetEnhancedKeyUsage(NULL, 0, NULL, &size); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %08lx\n", GetLastError()); + size = 0; + ret = CertGetEnhancedKeyUsage(NULL, 0, NULL, &size); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %08lx\n", GetLastError()); + /* These crash + ret = CertSetEnhancedKeyUsage(NULL, NULL); + usage.cUsageIdentifier = 0; + ret = CertSetEnhancedKeyUsage(NULL, &usage); + */ + /* Test with a cert with no enhanced key usage extension */ + context = CertCreateCertificateContext(X509_ASN_ENCODING, bigCert, + sizeof(bigCert)); + ok(context != NULL, "CertCreateCertificateContext failed: %08lx\n", + GetLastError()); + if (context) + { + static const char oid[] = "1.2.3.4"; + BYTE buf[sizeof(CERT_ENHKEY_USAGE) + 2 * (sizeof(LPSTR) + sizeof(oid))]; + PCERT_ENHKEY_USAGE pUsage = (PCERT_ENHKEY_USAGE)buf; + + ret = CertGetEnhancedKeyUsage(context, 0, NULL, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %08lx\n", GetLastError()); + size = 1; + ret = CertGetEnhancedKeyUsage(context, 0, NULL, &size); + if (ret) + { + /* Windows 2000, ME, or later: even though it succeeded, we expect + * CRYPT_E_NOT_FOUND, which indicates there is no enhanced key + * usage set for this cert (which implies it's valid for all uses.) + */ + ok(GetLastError() == CRYPT_E_NOT_FOUND, + "Expected CRYPT_E_NOT_FOUND, got %08lx\n", GetLastError()); + ok(size == sizeof(CERT_ENHKEY_USAGE), "Expected size %d, got %ld\n", + sizeof(CERT_ENHKEY_USAGE), size); + ret = CertGetEnhancedKeyUsage(context, 0, pUsage, &size); + ok(ret, "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + ok(pUsage->cUsageIdentifier == 0, "Expected 0 usages, got %ld\n", + pUsage->cUsageIdentifier); + } + else + { + /* Windows NT, 95, or 98: it fails, and the last error is + * CRYPT_E_NOT_FOUND. + */ + ok(GetLastError() == CRYPT_E_NOT_FOUND, + "Expected CRYPT_E_NOT_FOUND, got %08lx\n", GetLastError()); + } + /* I can add a usage identifier when no key usage has been set */ + ret = CertAddEnhancedKeyUsageIdentifier(context, oid); + ok(ret, "CertAddEnhancedKeyUsageIdentifier failed: %08lx\n", + GetLastError()); + size = sizeof(buf); + ret = CertGetEnhancedKeyUsage(context, + CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, pUsage, &size); + ok(ret && GetLastError() == 0, + "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + ok(pUsage->cUsageIdentifier == 1, "Expected 1 usage, got %ld\n", + pUsage->cUsageIdentifier); + if (pUsage->cUsageIdentifier) + ok(!strcmp(pUsage->rgpszUsageIdentifier[0], oid), + "Expected %s, got %s\n", oid, pUsage->rgpszUsageIdentifier[0]); + /* Now set an empty key usage */ + pUsage->cUsageIdentifier = 0; + ret = CertSetEnhancedKeyUsage(context, pUsage); + ok(ret, "CertSetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + /* Shouldn't find it in the cert */ + size = sizeof(buf); + ret = CertGetEnhancedKeyUsage(context, + CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, pUsage, &size); + ok(!ret && GetLastError() == CRYPT_E_NOT_FOUND, + "Expected CRYPT_E_NOT_FOUND, got %08lx\n", GetLastError()); + /* Should find it as an extended property */ + ret = CertGetEnhancedKeyUsage(context, + CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, pUsage, &size); + ok(ret && GetLastError() == 0, + "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + ok(pUsage->cUsageIdentifier == 0, "Expected 0 usages, got %ld\n", + pUsage->cUsageIdentifier); + /* Should find it as either */ + ret = CertGetEnhancedKeyUsage(context, 0, pUsage, &size); + ok(ret && GetLastError() == 0, + "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + ok(pUsage->cUsageIdentifier == 0, "Expected 0 usages, got %ld\n", + pUsage->cUsageIdentifier); + /* Add a usage identifier */ + ret = CertAddEnhancedKeyUsageIdentifier(context, oid); + ok(ret, "CertAddEnhancedKeyUsageIdentifier failed: %08lx\n", + GetLastError()); + size = sizeof(buf); + ret = CertGetEnhancedKeyUsage(context, 0, pUsage, &size); + ok(ret && GetLastError() == 0, + "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + ok(pUsage->cUsageIdentifier == 1, "Expected 1 identifier, got %ld\n", + pUsage->cUsageIdentifier); + if (pUsage->cUsageIdentifier) + ok(!strcmp(pUsage->rgpszUsageIdentifier[0], oid), + "Expected %s, got %s\n", oid, pUsage->rgpszUsageIdentifier[0]); + /* Yep, I can re-add the same usage identifier */ + ret = CertAddEnhancedKeyUsageIdentifier(context, oid); + ok(ret, "CertAddEnhancedKeyUsageIdentifier failed: %08lx\n", + GetLastError()); + size = sizeof(buf); + ret = CertGetEnhancedKeyUsage(context, 0, pUsage, &size); + ok(ret && GetLastError() == 0, + "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + ok(pUsage->cUsageIdentifier == 2, "Expected 2 identifiers, got %ld\n", + pUsage->cUsageIdentifier); + if (pUsage->cUsageIdentifier) + ok(!strcmp(pUsage->rgpszUsageIdentifier[0], oid), + "Expected %s, got %s\n", oid, pUsage->rgpszUsageIdentifier[0]); + if (pUsage->cUsageIdentifier >= 2) + ok(!strcmp(pUsage->rgpszUsageIdentifier[1], oid), + "Expected %s, got %s\n", oid, pUsage->rgpszUsageIdentifier[1]); + /* Now set a NULL extended property--this deletes the property. */ + ret = CertSetEnhancedKeyUsage(context, NULL); + ok(ret, "CertSetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + SetLastError(0xbaadcafe); + size = sizeof(buf); + ret = CertGetEnhancedKeyUsage(context, 0, pUsage, &size); + ok(GetLastError() == CRYPT_E_NOT_FOUND, + "Expected CRYPT_E_NOT_FOUND, got %08lx\n", GetLastError()); + + CertFreeCertificateContext(context); + } + /* Now test with a cert with an enhanced key usage extension */ + context = CertCreateCertificateContext(X509_ASN_ENCODING, certWithUsage, + sizeof(certWithUsage)); + ok(context != NULL, "CertCreateCertificateContext failed: %08lx\n", + GetLastError()); + if (context) + { + LPBYTE buf = NULL; + DWORD bufSize = 0, i; + + /* The size may depend on what flags are used to query it, so I + * realloc the buffer for each test. + */ + ret = CertGetEnhancedKeyUsage(context, + CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, NULL, &bufSize); + ok(ret, "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + buf = HeapAlloc(GetProcessHeap(), 0, bufSize); + if (buf) + { + PCERT_ENHKEY_USAGE pUsage = (PCERT_ENHKEY_USAGE)buf; + + /* Should find it in the cert */ + size = bufSize; + ret = CertGetEnhancedKeyUsage(context, + CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, pUsage, &size); + ok(ret && GetLastError() == 0, + "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + ok(pUsage->cUsageIdentifier == 3, "Expected 3 usages, got %ld\n", + pUsage->cUsageIdentifier); + for (i = 0; i < pUsage->cUsageIdentifier; i++) + ok(!strcmp(pUsage->rgpszUsageIdentifier[i], keyUsages[i]), + "Expected %s, got %s\n", keyUsages[i], + pUsage->rgpszUsageIdentifier[i]); + HeapFree(GetProcessHeap(), 0, buf); + } + ret = CertGetEnhancedKeyUsage(context, 0, NULL, &bufSize); + ok(ret, "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + buf = HeapAlloc(GetProcessHeap(), 0, bufSize); + if (buf) + { + PCERT_ENHKEY_USAGE pUsage = (PCERT_ENHKEY_USAGE)buf; + + /* Should find it as either */ + size = bufSize; + ret = CertGetEnhancedKeyUsage(context, 0, pUsage, &size); + /* In Windows, GetLastError returns CRYPT_E_NOT_FOUND not found + * here, even though the return is successful and the usage id + * count is positive. I don't enforce that here. + */ + ok(ret, + "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + ok(pUsage->cUsageIdentifier == 3, "Expected 3 usages, got %ld\n", + pUsage->cUsageIdentifier); + for (i = 0; i < pUsage->cUsageIdentifier; i++) + ok(!strcmp(pUsage->rgpszUsageIdentifier[i], keyUsages[i]), + "Expected %s, got %s\n", keyUsages[i], + pUsage->rgpszUsageIdentifier[i]); + HeapFree(GetProcessHeap(), 0, buf); + } + /* Shouldn't find it as an extended property */ + ret = CertGetEnhancedKeyUsage(context, + CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, NULL, &size); + ok(!ret && GetLastError() == CRYPT_E_NOT_FOUND, + "Expected CRYPT_E_NOT_FOUND, got %08lx\n", GetLastError()); + /* Adding a usage identifier overrides the cert's usage!? */ + ret = CertAddEnhancedKeyUsageIdentifier(context, szOID_RSA_RSA); + ok(ret, "CertAddEnhancedKeyUsageIdentifier failed: %08lx\n", + GetLastError()); + ret = CertGetEnhancedKeyUsage(context, 0, NULL, &bufSize); + ok(ret, "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + buf = HeapAlloc(GetProcessHeap(), 0, bufSize); + if (buf) + { + PCERT_ENHKEY_USAGE pUsage = (PCERT_ENHKEY_USAGE)buf; + + /* Should find it as either */ + size = bufSize; + ret = CertGetEnhancedKeyUsage(context, 0, pUsage, &size); + ok(ret, + "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + ok(pUsage->cUsageIdentifier == 1, "Expected 1 usage, got %ld\n", + pUsage->cUsageIdentifier); + ok(!strcmp(pUsage->rgpszUsageIdentifier[0], szOID_RSA_RSA), + "Expected %s, got %s\n", szOID_RSA_RSA, + pUsage->rgpszUsageIdentifier[0]); + HeapFree(GetProcessHeap(), 0, buf); + } + /* But querying the cert directly returns its usage */ + ret = CertGetEnhancedKeyUsage(context, + CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, NULL, &bufSize); + ok(ret, "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + buf = HeapAlloc(GetProcessHeap(), 0, bufSize); + if (buf) + { + PCERT_ENHKEY_USAGE pUsage = (PCERT_ENHKEY_USAGE)buf; + + size = bufSize; + ret = CertGetEnhancedKeyUsage(context, + CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, pUsage, &size); + ok(ret, + "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + ok(pUsage->cUsageIdentifier == 3, "Expected 3 usages, got %ld\n", + pUsage->cUsageIdentifier); + for (i = 0; i < pUsage->cUsageIdentifier; i++) + ok(!strcmp(pUsage->rgpszUsageIdentifier[i], keyUsages[i]), + "Expected %s, got %s\n", keyUsages[i], + pUsage->rgpszUsageIdentifier[i]); + HeapFree(GetProcessHeap(), 0, buf); + } + /* And removing the only usage identifier in the extended property + * results in the cert's key usage being found. + */ + ret = CertRemoveEnhancedKeyUsageIdentifier(context, szOID_RSA_RSA); + ok(ret, "CertRemoveEnhancedKeyUsage failed: %08lx\n", GetLastError()); + ret = CertGetEnhancedKeyUsage(context, 0, NULL, &bufSize); + ok(ret, "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + buf = HeapAlloc(GetProcessHeap(), 0, bufSize); + if (buf) + { + PCERT_ENHKEY_USAGE pUsage = (PCERT_ENHKEY_USAGE)buf; + + /* Should find it as either */ + size = bufSize; + ret = CertGetEnhancedKeyUsage(context, 0, pUsage, &size); + ok(ret, + "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); + ok(pUsage->cUsageIdentifier == 3, "Expected 3 usages, got %ld\n", + pUsage->cUsageIdentifier); + for (i = 0; i < pUsage->cUsageIdentifier; i++) + ok(!strcmp(pUsage->rgpszUsageIdentifier[i], keyUsages[i]), + "Expected %s, got %s\n", keyUsages[i], + pUsage->rgpszUsageIdentifier[i]); + HeapFree(GetProcessHeap(), 0, buf); + } + + CertFreeCertificateContext(context); + } +} + START_TEST(cert) { init_function_pointers(); testCryptHashCert(); testCertSigs(); + testKeyUsage(); } diff --git a/include/wincrypt.h b/include/wincrypt.h index aa3240b5f6d..e19f6b16687 100644 --- a/include/wincrypt.h +++ b/include/wincrypt.h @@ -681,6 +681,27 @@ typedef struct _CERT_CHAIN_POLICY_STATUS { void *pvExtraPolicyStatus; } CERT_CHAIN_POLICY_STATUS, *PCERT_CHAIN_POLICY_STATUS; +typedef struct _CERT_USAGE_MATCH { + DWORD dwType; + CERT_ENHKEY_USAGE Usage; +} CERT_USAGE_MATCH, *PCERT_USAGE_MATCH; + +typedef struct _CTL_USAGE_MATCH { + DWORD dwType; + CTL_USAGE Usage; +} CTL_USAGE_MATCH, *PCTL_USAGE_MATCH; + +typedef struct _CERT_CHAIN_PARA { + DWORD cbSize; + CERT_USAGE_MATCH RequestedUsage; +#ifdef CERT_CHAIN_PARA_HAS_EXTRA_FIELDS + CERT_USAGE_MATCH RequestedIssuancePolicy; + DWORD dwUrlRetrievalTimeout; + BOOL fCheckRevocationFreshnessTime; + DWORD dwRevocationFreshnessTime; +#endif +} CERT_CHAIN_PARA, *PCERT_CHAIN_PARA; + typedef struct _CERT_SYSTEM_STORE_INFO { DWORD cbSize; } CERT_SYSTEM_STORE_INFO, *PCERT_SYSTEM_STORE_INFO; @@ -1881,6 +1902,19 @@ static const WCHAR CERT_PHYSICAL_STORE_AUTH_ROOT_NAME[] = #define CERT_FIND_PUBKEY_MD5_HASH \ (CERT_COMPARE_PUBKEY_MD5_HASH << CERT_COMPARE_SHIFT) +#define CERT_FIND_OPTIONAL_ENHKEY_USAGE_FLAG 0x1 +#define CERT_FIND_OPTIONAL_CTL_USAGE_FLAG 0x1 +#define CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG 0x2 +#define CERT_FIND_EXT_ONLY_CTL_USAGE_FLAG 0x2 +#define CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG 0x4 +#define CERT_FIND_PROP_ONLY_CTL_USAGE_FLAG 0x4 +#define CERT_FIND_NO_ENHKEY_USAGE_FLAG 0x8 +#define CERT_FIND_NO_CTL_USAGE_FLAG 0x8 +#define CERT_FIND_OR_ENHKEY_USAGE_FLAG 0x10 +#define CERT_FIND_OR_CTL_USAGE_FLAG 0x10 +#define CERT_FIND_VALID_ENHKEY_USAGE_FLAG 0x20 +#define CERT_FIND_VALID_CTL_USAGE_FLAG 0x20 + /* PFN_CERT_STORE_PROV_WRITE_CERT dwFlags values */ #define CERT_STORE_PROV_WRITE_ADD_FLAG 0x1 @@ -2587,6 +2621,17 @@ BOOL WINAPI CertSerializeCRLStoreElement(PCCRL_CONTEXT pCrlContext, BOOL WINAPI CertSerializeCTLStoreElement(PCCTL_CONTEXT pCtlContext, DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement); +BOOL WINAPI CertGetEnhancedKeyUsage(PCCERT_CONTEXT pCertContext, DWORD dwFlags, + PCERT_ENHKEY_USAGE pUsage, DWORD *pcbUsage); +BOOL WINAPI CertSetEnhancedKeyUsage(PCCERT_CONTEXT pCertContext, + PCERT_ENHKEY_USAGE pUsage); +BOOL WINAPI CertAddEnhancedKeyUsageIdentifier(PCCERT_CONTEXT pCertContext, + LPCSTR pszUsageIdentifer); +BOOL WINAPI CertRemoveEnhancedKeyUsageIdentifier(PCCERT_CONTEXT pCertContext, + LPCSTR pszUsageIdentifer); +BOOL WINAPI CertGetValidUsages(DWORD cCerts, PCCERT_CONTEXT *rghCerts, + int *cNumOIDs, LPSTR *rghOIDs, DWORD *pcbOIDs); + BOOL WINAPI CryptEncodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType, const void *pvStructInfo, BYTE *pbEncoded, DWORD *pcbEncoded); BOOL WINAPI CryptEncodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,