Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / net / ssl / ssl_platform_key_win.cc
blob5bd99554446139a757666ff56078838ba6002cdb
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/ssl/ssl_platform_key.h"
7 #include <windows.h>
8 #include <NCrypt.h>
10 #include <algorithm>
11 #include <string>
12 #include <vector>
14 #include <openssl/bn.h>
15 #include <openssl/ecdsa.h>
16 #include <openssl/evp.h>
17 #include <openssl/x509.h>
19 #include "base/lazy_instance.h"
20 #include "base/logging.h"
21 #include "base/macros.h"
22 #include "base/sequenced_task_runner.h"
23 #include "base/stl_util.h"
24 #include "base/win/windows_version.h"
25 #include "crypto/openssl_util.h"
26 #include "crypto/scoped_capi_types.h"
27 #include "crypto/wincrypt_shim.h"
28 #include "net/base/net_errors.h"
29 #include "net/cert/x509_certificate.h"
30 #include "net/ssl/scoped_openssl_types.h"
31 #include "net/ssl/ssl_private_key.h"
32 #include "net/ssl/threaded_ssl_private_key.h"
34 namespace net {
36 namespace {
38 using NCryptFreeObjectFunc = SECURITY_STATUS(WINAPI*)(NCRYPT_HANDLE);
39 using NCryptSignHashFunc = SECURITY_STATUS(WINAPI*)(NCRYPT_KEY_HANDLE, // hKey
40 VOID*, // pPaddingInfo
41 BYTE*, // pbHashValue
42 DWORD, // cbHashValue
43 BYTE*, // pbSignature
44 DWORD, // cbSignature
45 DWORD*, // pcbResult
46 DWORD); // dwFlags
48 class CNGFunctions {
49 public:
50 CNGFunctions() : ncrypt_free_object_(nullptr), ncrypt_sign_hash_(nullptr) {
51 HMODULE ncrypt = GetModuleHandle(L"ncrypt.dll");
52 if (ncrypt != nullptr) {
53 ncrypt_free_object_ = reinterpret_cast<NCryptFreeObjectFunc>(
54 GetProcAddress(ncrypt, "NCryptFreeObject"));
55 ncrypt_sign_hash_ = reinterpret_cast<NCryptSignHashFunc>(
56 GetProcAddress(ncrypt, "NCryptSignHash"));
60 NCryptFreeObjectFunc ncrypt_free_object() const {
61 return ncrypt_free_object_;
64 NCryptSignHashFunc ncrypt_sign_hash() const { return ncrypt_sign_hash_; }
66 private:
67 NCryptFreeObjectFunc ncrypt_free_object_;
68 NCryptSignHashFunc ncrypt_sign_hash_;
71 base::LazyInstance<CNGFunctions>::Leaky g_cng_functions =
72 LAZY_INSTANCE_INITIALIZER;
74 class SSLPlatformKeyCAPI : public ThreadedSSLPrivateKey::Delegate {
75 public:
76 // Takes ownership of |provider|.
77 SSLPlatformKeyCAPI(HCRYPTPROV provider, DWORD key_spec, size_t max_length)
78 : provider_(provider), key_spec_(key_spec), max_length_(max_length) {}
80 ~SSLPlatformKeyCAPI() override {}
82 SSLPrivateKey::Type GetType() override { return SSLPrivateKey::Type::RSA; }
84 bool SupportsHash(SSLPrivateKey::Hash hash) override {
85 // If the key is in CAPI, assume conservatively that the CAPI service
86 // provider may only be able to sign pre-TLS-1.2 and SHA-1 hashes.
87 return hash == SSLPrivateKey::Hash::MD5_SHA1 ||
88 hash == SSLPrivateKey::Hash::SHA1;
91 size_t GetMaxSignatureLengthInBytes() override { return max_length_; }
93 Error SignDigest(SSLPrivateKey::Hash hash,
94 const base::StringPiece& input,
95 std::vector<uint8_t>* signature) override {
96 ALG_ID hash_alg = 0;
97 switch (hash) {
98 case SSLPrivateKey::Hash::MD5_SHA1:
99 hash_alg = CALG_SSL3_SHAMD5;
100 break;
101 case SSLPrivateKey::Hash::SHA1:
102 hash_alg = CALG_SHA1;
103 break;
104 case SSLPrivateKey::Hash::SHA256:
105 hash_alg = CALG_SHA_256;
106 break;
107 case SSLPrivateKey::Hash::SHA384:
108 hash_alg = CALG_SHA_384;
109 break;
110 case SSLPrivateKey::Hash::SHA512:
111 hash_alg = CALG_SHA_512;
112 break;
114 DCHECK_NE(static_cast<ALG_ID>(0), hash_alg);
116 crypto::ScopedHCRYPTHASH hash_handle;
117 if (!CryptCreateHash(provider_, hash_alg, 0, 0, hash_handle.receive())) {
118 PLOG(ERROR) << "CreateCreateHash failed";
119 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
121 DWORD hash_len;
122 DWORD arg_len = sizeof(hash_len);
123 if (!CryptGetHashParam(hash_handle.get(), HP_HASHSIZE,
124 reinterpret_cast<BYTE*>(&hash_len), &arg_len, 0)) {
125 PLOG(ERROR) << "CryptGetHashParam HP_HASHSIZE failed";
126 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
128 if (hash_len != input.size())
129 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
130 if (!CryptSetHashParam(
131 hash_handle.get(), HP_HASHVAL,
132 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(input.data())),
133 0)) {
134 PLOG(ERROR) << "CryptSetHashParam HP_HASHVAL failed";
135 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
137 DWORD signature_len = 0;
138 if (!CryptSignHash(hash_handle.get(), key_spec_, nullptr, 0, nullptr,
139 &signature_len)) {
140 PLOG(ERROR) << "CryptSignHash failed";
141 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
143 signature->resize(signature_len);
144 if (!CryptSignHash(hash_handle.get(), key_spec_, nullptr, 0,
145 vector_as_array(signature), &signature_len)) {
146 PLOG(ERROR) << "CryptSignHash failed";
147 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
149 signature->resize(signature_len);
151 // CryptoAPI signs in little-endian, so reverse it.
152 std::reverse(signature->begin(), signature->end());
153 return OK;
156 private:
157 crypto::ScopedHCRYPTPROV provider_;
158 DWORD key_spec_;
159 size_t max_length_;
161 DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyCAPI);
164 class SSLPlatformKeyCNG : public ThreadedSSLPrivateKey::Delegate {
165 public:
166 // Takes ownership of |key|.
167 SSLPlatformKeyCNG(NCRYPT_KEY_HANDLE key,
168 SSLPrivateKey::Type type,
169 size_t max_length)
170 : key_(key), type_(type), max_length_(max_length) {}
172 ~SSLPlatformKeyCNG() override {
173 g_cng_functions.Get().ncrypt_free_object()(key_);
176 SSLPrivateKey::Type GetType() override { return type_; }
178 bool SupportsHash(SSLPrivateKey::Hash hash) override {
179 // If the key is a 1024-bit RSA, assume conservatively that it may only be
180 // able to sign SHA-1 hashes. This is the case for older Estonian ID cards
181 // that have 1024-bit RSA keys. (For an RSA key, the maximum signature
182 // length is the size of the modulus in bytes.)
184 // CNG does provide NCryptIsAlgSupported and NCryptEnumAlgorithms functions,
185 // however they seem to both return NTE_NOT_SUPPORTED when querying the
186 // NCRYPT_PROV_HANDLE at the key's NCRYPT_PROVIDER_HANDLE_PROPERTY.
187 if (type_ == SSLPrivateKey::Type::RSA && max_length_ <= 1024 / 8) {
188 return hash == SSLPrivateKey::Hash::MD5_SHA1 ||
189 hash == SSLPrivateKey::Hash::SHA1;
191 return true;
194 size_t GetMaxSignatureLengthInBytes() override { return max_length_; }
196 Error SignDigest(SSLPrivateKey::Hash hash,
197 const base::StringPiece& input,
198 std::vector<uint8_t>* signature) override {
199 crypto::OpenSSLErrStackTracer tracer(FROM_HERE);
201 BCRYPT_PKCS1_PADDING_INFO rsa_padding_info = {0};
202 void* padding_info = nullptr;
203 DWORD flags = 0;
204 if (type_ == SSLPrivateKey::Type::RSA) {
205 switch (hash) {
206 case SSLPrivateKey::Hash::MD5_SHA1:
207 rsa_padding_info.pszAlgId = nullptr;
208 break;
209 case SSLPrivateKey::Hash::SHA1:
210 rsa_padding_info.pszAlgId = BCRYPT_SHA1_ALGORITHM;
211 break;
212 case SSLPrivateKey::Hash::SHA256:
213 rsa_padding_info.pszAlgId = BCRYPT_SHA256_ALGORITHM;
214 break;
215 case SSLPrivateKey::Hash::SHA384:
216 rsa_padding_info.pszAlgId = BCRYPT_SHA384_ALGORITHM;
217 break;
218 case SSLPrivateKey::Hash::SHA512:
219 rsa_padding_info.pszAlgId = BCRYPT_SHA512_ALGORITHM;
220 break;
222 padding_info = &rsa_padding_info;
223 flags |= BCRYPT_PAD_PKCS1;
226 DWORD signature_len;
227 SECURITY_STATUS status = g_cng_functions.Get().ncrypt_sign_hash()(
228 key_, padding_info,
229 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(input.data())),
230 input.size(), nullptr, 0, &signature_len, flags);
231 if (FAILED(status)) {
232 LOG(ERROR) << "NCryptSignHash failed: " << status;
233 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
235 signature->resize(signature_len);
236 status = g_cng_functions.Get().ncrypt_sign_hash()(
237 key_, padding_info,
238 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(input.data())),
239 input.size(), vector_as_array(signature), signature_len, &signature_len,
240 flags);
241 if (FAILED(status)) {
242 LOG(ERROR) << "NCryptSignHash failed: " << status;
243 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
245 signature->resize(signature_len);
247 // CNG emits raw ECDSA signatures, but BoringSSL expects a DER-encoded
248 // ECDSA-Sig-Value.
249 if (type_ == SSLPrivateKey::Type::ECDSA) {
250 if (signature->size() % 2 != 0) {
251 LOG(ERROR) << "Bad signature length";
252 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
254 size_t order_len = signature->size() / 2;
256 // Convert the RAW ECDSA signature to a DER-encoded ECDSA-Sig-Value.
257 crypto::ScopedECDSA_SIG sig(ECDSA_SIG_new());
258 if (!sig || !BN_bin2bn(vector_as_array(signature), order_len, sig->r) ||
259 !BN_bin2bn(vector_as_array(signature) + order_len, order_len,
260 sig->s)) {
261 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
264 int len = i2d_ECDSA_SIG(sig.get(), nullptr);
265 if (len <= 0)
266 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
267 signature->resize(len);
268 uint8_t* ptr = vector_as_array(signature);
269 len = i2d_ECDSA_SIG(sig.get(), &ptr);
270 if (len <= 0)
271 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
272 signature->resize(len);
275 return OK;
278 private:
279 NCRYPT_KEY_HANDLE key_;
280 SSLPrivateKey::Type type_;
281 size_t max_length_;
283 DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyCNG);
286 // Determines the key type and maximum signature length of |certificate|'s
287 // public key.
288 bool GetKeyInfo(const X509Certificate* certificate,
289 SSLPrivateKey::Type* out_type,
290 size_t* out_max_length) {
291 crypto::OpenSSLErrStackTracer tracker(FROM_HERE);
293 std::string der_encoded;
294 if (!X509Certificate::GetDEREncoded(certificate->os_cert_handle(),
295 &der_encoded))
296 return false;
297 const uint8_t* bytes = reinterpret_cast<const uint8_t*>(der_encoded.data());
298 ScopedX509 x509(d2i_X509(nullptr, &bytes, der_encoded.size()));
299 if (!x509)
300 return false;
301 crypto::ScopedEVP_PKEY key(X509_get_pubkey(x509.get()));
302 if (!key)
303 return false;
304 switch (EVP_PKEY_id(key.get())) {
305 case EVP_PKEY_RSA:
306 *out_type = SSLPrivateKey::Type::RSA;
307 break;
308 case EVP_PKEY_EC:
309 *out_type = SSLPrivateKey::Type::ECDSA;
310 break;
311 default:
312 return false;
314 *out_max_length = EVP_PKEY_size(key.get());
315 return true;
318 } // namespace
320 scoped_ptr<SSLPrivateKey> FetchClientCertPrivateKey(
321 X509Certificate* certificate,
322 scoped_refptr<base::SequencedTaskRunner> task_runner) {
323 // Rather than query the private key for metadata, extract the public key from
324 // the certificate without using Windows APIs. CAPI and CNG do not
325 // consistently work depending on the system. See https://crbug.com/468345.
326 SSLPrivateKey::Type key_type;
327 size_t max_length;
328 if (!GetKeyInfo(certificate, &key_type, &max_length))
329 return nullptr;
331 PCCERT_CONTEXT cert_context = certificate->os_cert_handle();
333 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE prov_or_key = 0;
334 DWORD key_spec = 0;
335 BOOL must_free = FALSE;
336 DWORD flags = 0;
337 if (base::win::GetVersion() >= base::win::VERSION_VISTA)
338 flags |= CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG;
340 if (!CryptAcquireCertificatePrivateKey(cert_context, flags, nullptr,
341 &prov_or_key, &key_spec, &must_free)) {
342 PLOG(WARNING) << "Could not acquire private key";
343 return nullptr;
346 // Should never get a cached handle back - ownership must always be
347 // transferred.
348 CHECK_EQ(must_free, TRUE);
350 scoped_ptr<ThreadedSSLPrivateKey::Delegate> delegate;
351 if (key_spec == CERT_NCRYPT_KEY_SPEC) {
352 delegate.reset(new SSLPlatformKeyCNG(prov_or_key, key_type, max_length));
353 } else {
354 DCHECK(SSLPrivateKey::Type::RSA == key_type);
355 delegate.reset(new SSLPlatformKeyCAPI(prov_or_key, key_spec, max_length));
357 return make_scoped_ptr(
358 new ThreadedSSLPrivateKey(delegate.Pass(), task_runner.Pass()));
361 } // namespace net