123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- // Copyright 2015 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "net/ssl/ssl_platform_key_win.h"
- #include <algorithm>
- #include <memory>
- #include <string>
- #include <utility>
- #include <vector>
- #include "base/logging.h"
- #include "base/strings/utf_string_conversions.h"
- #include "crypto/openssl_util.h"
- #include "crypto/scoped_capi_types.h"
- #include "net/base/net_errors.h"
- #include "net/cert/x509_certificate.h"
- #include "net/ssl/ssl_platform_key_util.h"
- #include "net/ssl/ssl_private_key.h"
- #include "net/ssl/threaded_ssl_private_key.h"
- #include "third_party/boringssl/src/include/openssl/bn.h"
- #include "third_party/boringssl/src/include/openssl/ecdsa.h"
- #include "third_party/boringssl/src/include/openssl/evp.h"
- #include "third_party/boringssl/src/include/openssl/ssl.h"
- namespace net {
- namespace {
- std::string GetCAPIProviderName(HCRYPTPROV provider) {
- DWORD name_len;
- if (!CryptGetProvParam(provider, PP_NAME, nullptr, &name_len, 0)) {
- return "(error getting name)";
- }
- std::vector<BYTE> name(name_len);
- if (!CryptGetProvParam(provider, PP_NAME, name.data(), &name_len, 0)) {
- return "(error getting name)";
- }
- // Per Microsoft's documentation, PP_NAME is NUL-terminated. However,
- // smartcard drivers are notoriously buggy, so check this.
- auto nul = std::find(name.begin(), name.end(), 0);
- if (nul != name.end()) {
- name_len = nul - name.begin();
- }
- return std::string(reinterpret_cast<const char*>(name.data()), name_len);
- }
- class SSLPlatformKeyCAPI : public ThreadedSSLPrivateKey::Delegate {
- public:
- // Takes ownership of |provider|.
- SSLPlatformKeyCAPI(crypto::ScopedHCRYPTPROV provider, DWORD key_spec)
- : provider_name_(GetCAPIProviderName(provider.get())),
- provider_(std::move(provider)),
- key_spec_(key_spec) {}
- SSLPlatformKeyCAPI(const SSLPlatformKeyCAPI&) = delete;
- SSLPlatformKeyCAPI& operator=(const SSLPlatformKeyCAPI&) = delete;
- ~SSLPlatformKeyCAPI() override = default;
- std::string GetProviderName() override { return "CAPI: " + provider_name_; }
- std::vector<uint16_t> GetAlgorithmPreferences() override {
- // If the key is in CAPI, assume conservatively that the CAPI service
- // provider may only be able to sign pre-TLS-1.2 and SHA-1 hashes.
- // Prioritize SHA-1, but if the server doesn't advertise it, leave the other
- // algorithms enabled to try.
- return {
- SSL_SIGN_RSA_PKCS1_SHA1,
- SSL_SIGN_RSA_PKCS1_SHA256,
- SSL_SIGN_RSA_PKCS1_SHA384,
- SSL_SIGN_RSA_PKCS1_SHA512,
- };
- }
- Error Sign(uint16_t algorithm,
- base::span<const uint8_t> input,
- std::vector<uint8_t>* signature) override {
- const EVP_MD* md = SSL_get_signature_algorithm_digest(algorithm);
- uint8_t digest[EVP_MAX_MD_SIZE];
- unsigned digest_len;
- if (!md || !EVP_Digest(input.data(), input.size(), digest, &digest_len, md,
- nullptr)) {
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- }
- ALG_ID hash_alg;
- switch (EVP_MD_type(md)) {
- case NID_md5_sha1:
- hash_alg = CALG_SSL3_SHAMD5;
- break;
- case NID_sha1:
- hash_alg = CALG_SHA1;
- break;
- case NID_sha256:
- hash_alg = CALG_SHA_256;
- break;
- case NID_sha384:
- hash_alg = CALG_SHA_384;
- break;
- case NID_sha512:
- hash_alg = CALG_SHA_512;
- break;
- default:
- NOTREACHED();
- return ERR_FAILED;
- }
- crypto::ScopedHCRYPTHASH hash_handle;
- if (!CryptCreateHash(
- provider_.get(), hash_alg, 0, 0,
- crypto::ScopedHCRYPTHASH::Receiver(hash_handle).get())) {
- PLOG(ERROR) << "CreateCreateHash failed";
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- }
- DWORD hash_len;
- DWORD arg_len = sizeof(hash_len);
- if (!CryptGetHashParam(hash_handle.get(), HP_HASHSIZE,
- reinterpret_cast<BYTE*>(&hash_len), &arg_len, 0)) {
- PLOG(ERROR) << "CryptGetHashParam HP_HASHSIZE failed";
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- }
- if (hash_len != digest_len)
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- if (!CryptSetHashParam(hash_handle.get(), HP_HASHVAL,
- const_cast<BYTE*>(digest), 0)) {
- PLOG(ERROR) << "CryptSetHashParam HP_HASHVAL failed";
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- }
- DWORD signature_len = 0;
- if (!CryptSignHash(hash_handle.get(), key_spec_, nullptr, 0, nullptr,
- &signature_len)) {
- PLOG(ERROR) << "CryptSignHash failed";
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- }
- signature->resize(signature_len);
- if (!CryptSignHash(hash_handle.get(), key_spec_, nullptr, 0,
- signature->data(), &signature_len)) {
- PLOG(ERROR) << "CryptSignHash failed";
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- }
- signature->resize(signature_len);
- // CryptoAPI signs in little-endian, so reverse it.
- std::reverse(signature->begin(), signature->end());
- return OK;
- }
- private:
- std::string provider_name_;
- crypto::ScopedHCRYPTPROV provider_;
- DWORD key_spec_;
- };
- class ScopedNCRYPT_PROV_HANDLE {
- public:
- ScopedNCRYPT_PROV_HANDLE() {}
- ScopedNCRYPT_PROV_HANDLE(const ScopedNCRYPT_PROV_HANDLE&) = delete;
- ScopedNCRYPT_PROV_HANDLE& operator=(const ScopedNCRYPT_PROV_HANDLE&) = delete;
- ~ScopedNCRYPT_PROV_HANDLE() {
- if (prov_) {
- NCryptFreeObject(prov_);
- }
- }
- NCRYPT_PROV_HANDLE get() const { return prov_; }
- NCRYPT_PROV_HANDLE* InitializeInto() { return &prov_; }
- private:
- NCRYPT_PROV_HANDLE prov_ = 0;
- };
- std::wstring GetCNGProviderName(NCRYPT_KEY_HANDLE key) {
- ScopedNCRYPT_PROV_HANDLE prov;
- DWORD prov_len = 0;
- SECURITY_STATUS status = NCryptGetProperty(
- key, NCRYPT_PROVIDER_HANDLE_PROPERTY,
- reinterpret_cast<BYTE*>(prov.InitializeInto()),
- sizeof(*prov.InitializeInto()), &prov_len, NCRYPT_SILENT_FLAG);
- if (FAILED(status)) {
- return L"(error getting provider)";
- }
- DCHECK_EQ(sizeof(NCRYPT_PROV_HANDLE), prov_len);
- // NCRYPT_NAME_PROPERTY is a NUL-terminated Unicode string, which means an
- // array of wchar_t, however NCryptGetProperty works in bytes, so lengths must
- // be converted.
- DWORD name_len = 0;
- status = NCryptGetProperty(prov.get(), NCRYPT_NAME_PROPERTY, nullptr, 0,
- &name_len, NCRYPT_SILENT_FLAG);
- if (FAILED(status) || name_len % sizeof(wchar_t) != 0) {
- return L"(error getting provider name)";
- }
- std::vector<wchar_t> name;
- name.reserve(name_len / sizeof(wchar_t));
- status = NCryptGetProperty(
- prov.get(), NCRYPT_NAME_PROPERTY, reinterpret_cast<BYTE*>(name.data()),
- name.size() * sizeof(wchar_t), &name_len, NCRYPT_SILENT_FLAG);
- if (FAILED(status)) {
- return L"(error getting provider name)";
- }
- name.resize(name_len / sizeof(wchar_t));
- // Per Microsoft's documentation, the name is NUL-terminated. However,
- // smartcard drivers are notoriously buggy, so check this.
- auto nul = std::find(name.begin(), name.end(), 0);
- if (nul != name.end()) {
- name.erase(nul, name.end());
- }
- return std::wstring(name.begin(), name.end());
- }
- class SSLPlatformKeyCNG : public ThreadedSSLPrivateKey::Delegate {
- public:
- // Takes ownership of |key|.
- SSLPlatformKeyCNG(NCRYPT_KEY_HANDLE key, int type, size_t max_length)
- : provider_name_(GetCNGProviderName(key)),
- key_(key),
- type_(type),
- max_length_(max_length) {}
- SSLPlatformKeyCNG(const SSLPlatformKeyCNG&) = delete;
- SSLPlatformKeyCNG& operator=(const SSLPlatformKeyCNG&) = delete;
- ~SSLPlatformKeyCNG() override { NCryptFreeObject(key_); }
- std::string GetProviderName() override {
- return "CNG: " + base::WideToUTF8(provider_name_);
- }
- std::vector<uint16_t> GetAlgorithmPreferences() override {
- // Per TLS 1.3 (RFC 8446), the RSA-PSS code points in TLS correspond to
- // RSA-PSS with salt length equal to the digest length. TPM 2.0's
- // TPM_ALG_RSAPSS algorithm, however, uses the maximum possible salt length.
- // The TPM provider will fail signing requests for other salt lengths and
- // thus cannot generate TLS-compatible PSS signatures.
- //
- // However, as of TPM revision 1.16, TPMs which follow FIPS 186-4 will
- // instead interpret TPM_ALG_RSAPSS using salt length equal to the digest
- // length. Those TPMs can generate TLS-compatible PSS signatures. As a
- // result, if this is a TPM-based key, we only report PSS as supported if
- // the salt length will match the digest length.
- bool supports_pss = true;
- if (provider_name_ == MS_PLATFORM_KEY_STORAGE_PROVIDER) {
- DWORD salt_size = 0;
- DWORD size_of_salt_size = sizeof(salt_size);
- HRESULT status =
- NCryptGetProperty(key_, NCRYPT_PCP_PSS_SALT_SIZE_PROPERTY,
- reinterpret_cast<PBYTE>(&salt_size),
- size_of_salt_size, &size_of_salt_size, 0);
- if (FAILED(status) || salt_size != NCRYPT_TPM_PSS_SALT_SIZE_HASHSIZE) {
- supports_pss = false;
- }
- }
- // If this is an under 1024-bit RSA key, conservatively prefer to sign SHA-1
- // hashes. Older Estonian ID cards can only sign SHA-1 hashes. Prioritize
- // SHA-1, but if the server doesn't advertise it, leave the other algorithms
- // enabled to try.
- if (type_ == EVP_PKEY_RSA && max_length_ <= 1024 / 8) {
- std::vector<uint16_t> ret = {
- SSL_SIGN_RSA_PKCS1_SHA1,
- SSL_SIGN_RSA_PKCS1_SHA256,
- SSL_SIGN_RSA_PKCS1_SHA384,
- SSL_SIGN_RSA_PKCS1_SHA512,
- };
- if (supports_pss) {
- // 1024-bit keys are too small for SSL_SIGN_RSA_PSS_SHA512.
- ret.push_back(SSL_SIGN_RSA_PSS_SHA256);
- ret.push_back(SSL_SIGN_RSA_PSS_SHA384);
- }
- return ret;
- }
- return SSLPrivateKey::DefaultAlgorithmPreferences(type_, supports_pss);
- }
- Error Sign(uint16_t algorithm,
- base::span<const uint8_t> input,
- std::vector<uint8_t>* signature) override {
- crypto::OpenSSLErrStackTracer tracer(FROM_HERE);
- const EVP_MD* md = SSL_get_signature_algorithm_digest(algorithm);
- uint8_t digest[EVP_MAX_MD_SIZE];
- unsigned digest_len;
- if (!md || !EVP_Digest(input.data(), input.size(), digest, &digest_len, md,
- nullptr)) {
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- }
- BCRYPT_PKCS1_PADDING_INFO pkcs1_padding_info = {nullptr};
- BCRYPT_PSS_PADDING_INFO pss_padding_info = {nullptr};
- void* padding_info = nullptr;
- DWORD flags = 0;
- if (SSL_get_signature_algorithm_key_type(algorithm) == EVP_PKEY_RSA) {
- const WCHAR* hash_alg;
- switch (EVP_MD_type(md)) {
- case NID_md5_sha1:
- hash_alg = nullptr;
- break;
- case NID_sha1:
- hash_alg = BCRYPT_SHA1_ALGORITHM;
- break;
- case NID_sha256:
- hash_alg = BCRYPT_SHA256_ALGORITHM;
- break;
- case NID_sha384:
- hash_alg = BCRYPT_SHA384_ALGORITHM;
- break;
- case NID_sha512:
- hash_alg = BCRYPT_SHA512_ALGORITHM;
- break;
- default:
- NOTREACHED();
- return ERR_FAILED;
- }
- if (SSL_is_signature_algorithm_rsa_pss(algorithm)) {
- pss_padding_info.pszAlgId = hash_alg;
- pss_padding_info.cbSalt = EVP_MD_size(md);
- padding_info = &pss_padding_info;
- flags |= BCRYPT_PAD_PSS;
- } else {
- pkcs1_padding_info.pszAlgId = hash_alg;
- padding_info = &pkcs1_padding_info;
- flags |= BCRYPT_PAD_PKCS1;
- }
- }
- DWORD signature_len;
- SECURITY_STATUS status =
- NCryptSignHash(key_, padding_info, const_cast<BYTE*>(digest),
- digest_len, nullptr, 0, &signature_len, flags);
- if (FAILED(status)) {
- LOG(ERROR) << "NCryptSignHash failed: " << status;
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- }
- signature->resize(signature_len);
- status = NCryptSignHash(key_, padding_info, const_cast<BYTE*>(digest),
- digest_len, signature->data(), signature_len,
- &signature_len, flags);
- if (FAILED(status)) {
- LOG(ERROR) << "NCryptSignHash failed: " << status;
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- }
- signature->resize(signature_len);
- // CNG emits raw ECDSA signatures, but BoringSSL expects a DER-encoded
- // ECDSA-Sig-Value.
- if (type_ == EVP_PKEY_EC) {
- if (signature->size() % 2 != 0) {
- LOG(ERROR) << "Bad signature length";
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- }
- size_t order_len = signature->size() / 2;
- // Convert the RAW ECDSA signature to a DER-encoded ECDSA-Sig-Value.
- bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new());
- if (!sig || !BN_bin2bn(signature->data(), order_len, sig->r) ||
- !BN_bin2bn(signature->data() + order_len, order_len, sig->s)) {
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- }
- int len = i2d_ECDSA_SIG(sig.get(), nullptr);
- if (len <= 0)
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- signature->resize(len);
- uint8_t* ptr = signature->data();
- len = i2d_ECDSA_SIG(sig.get(), &ptr);
- if (len <= 0)
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- signature->resize(len);
- }
- return OK;
- }
- private:
- std::wstring provider_name_;
- NCRYPT_KEY_HANDLE key_;
- int type_;
- size_t max_length_;
- };
- } // namespace
- scoped_refptr<SSLPrivateKey> WrapCAPIPrivateKey(
- const X509Certificate* certificate,
- crypto::ScopedHCRYPTPROV prov,
- DWORD key_spec) {
- return base::MakeRefCounted<ThreadedSSLPrivateKey>(
- std::make_unique<SSLPlatformKeyCAPI>(std::move(prov), key_spec),
- GetSSLPlatformKeyTaskRunner());
- }
- scoped_refptr<SSLPrivateKey> WrapCNGPrivateKey(
- const X509Certificate* certificate,
- NCRYPT_KEY_HANDLE key) {
- // Rather than query the private key for metadata, extract the public key from
- // the certificate without using Windows APIs. CNG does not consistently work
- // depending on the system. See https://crbug.com/468345.
- int key_type;
- size_t max_length;
- if (!GetClientCertInfo(certificate, &key_type, &max_length)) {
- NCryptFreeObject(key);
- return nullptr;
- }
- return base::MakeRefCounted<ThreadedSSLPrivateKey>(
- std::make_unique<SSLPlatformKeyCNG>(key, key_type, max_length),
- GetSSLPlatformKeyTaskRunner());
- }
- scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKey(
- const X509Certificate* certificate,
- PCCERT_CONTEXT cert_context) {
- HCRYPTPROV_OR_NCRYPT_KEY_HANDLE prov_or_key = 0;
- DWORD key_spec = 0;
- BOOL must_free = FALSE;
- DWORD flags = CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG;
- if (!CryptAcquireCertificatePrivateKey(cert_context, flags, nullptr,
- &prov_or_key, &key_spec, &must_free)) {
- PLOG(WARNING) << "Could not acquire private key";
- return nullptr;
- }
- // Should never get a cached handle back - ownership must always be
- // transferred.
- CHECK_EQ(must_free, TRUE);
- if (key_spec == CERT_NCRYPT_KEY_SPEC) {
- return WrapCNGPrivateKey(certificate, prov_or_key);
- } else {
- return WrapCAPIPrivateKey(certificate,
- crypto::ScopedHCRYPTPROV(prov_or_key), key_spec);
- }
- }
- } // namespace net
|