Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(189)

Side by Side Diff: net/ssl/ssl_platform_key_win.cc

Issue 1178193002: Sign CertificateVerify messages on a background thread. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix net_nacl Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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.
4
5 #include "net/ssl/ssl_platform_key.h"
6
7 #include <windows.h>
8 #include <NCrypt.h>
9
10 #include <algorithm>
11 #include <string>
12 #include <vector>
13
14 #include <openssl/bn.h>
15 #include <openssl/ecdsa.h>
16 #include <openssl/evp.h>
17 #include <openssl/x509.h>
18
19 #include "base/lazy_instance.h"
20 #include "base/logging.h"
21 #include "base/macros.h"
22 #include "base/stl_util.h"
23 #include "base/win/windows_version.h"
24 #include "crypto/openssl_util.h"
25 #include "crypto/scoped_capi_types.h"
26 #include "crypto/wincrypt_shim.h"
27 #include "net/base/net_errors.h"
28 #include "net/cert/x509_certificate.h"
29 #include "net/ssl/scoped_openssl_types.h"
30 #include "net/ssl/ssl_private_key.h"
31 #include "net/ssl/threaded_ssl_private_key.h"
32
33 namespace net {
34
35 namespace {
36
37 using NCryptFreeObjectFunc = SECURITY_STATUS(WINAPI*)(NCRYPT_HANDLE);
38 using NCryptSignHashFunc = SECURITY_STATUS(WINAPI*)(NCRYPT_KEY_HANDLE, // hKey
39 VOID*, // pPaddingInfo
40 BYTE*, // pbHashValue
41 DWORD, // cbHashValue
42 BYTE*, // pbSignature
43 DWORD, // cbSignature
44 DWORD*, // pcbResult
45 DWORD); // dwFlags
46
47 class CNGFunctions {
48 public:
49 CNGFunctions() : ncrypt_free_object_(nullptr), ncrypt_sign_hash_(nullptr) {
50 HMODULE ncrypt = GetModuleHandle(L"ncrypt.dll");
51 if (ncrypt != nullptr) {
52 ncrypt_free_object_ = reinterpret_cast<NCryptFreeObjectFunc>(
53 GetProcAddress(ncrypt, "NCryptFreeObject"));
54 ncrypt_sign_hash_ = reinterpret_cast<NCryptSignHashFunc>(
55 GetProcAddress(ncrypt, "NCryptSignHash"));
56 }
57 }
58
59 NCryptFreeObjectFunc ncrypt_free_object() const {
60 return ncrypt_free_object_;
61 }
62
63 NCryptSignHashFunc ncrypt_sign_hash() const { return ncrypt_sign_hash_; }
64
65 private:
66 NCryptFreeObjectFunc ncrypt_free_object_;
67 NCryptSignHashFunc ncrypt_sign_hash_;
68 };
69
70 base::LazyInstance<CNGFunctions>::Leaky g_cng_functions =
71 LAZY_INSTANCE_INITIALIZER;
72
73 class SSLPlatformKeyCAPI : public ThreadedSSLPrivateKey::Delegate {
74 public:
75 // Takes ownership of |provider|.
76 SSLPlatformKeyCAPI(HCRYPTPROV provider, DWORD key_spec, size_t max_length)
77 : provider_(provider), key_spec_(key_spec), max_length_(max_length) {}
78
79 ~SSLPlatformKeyCAPI() override {}
80
81 SSLPrivateKey::Type GetType() override { return SSLPrivateKey::Type::RSA; }
82
83 bool SupportsHash(SSLPrivateKey::Hash hash) override {
84 // If the key is in CAPI, assume conservatively that the CAPI service
85 // provider may only be able to sign pre-TLS-1.2 and SHA-1 hashes.
86 return hash == SSLPrivateKey::Hash::MD5_SHA1 ||
87 hash == SSLPrivateKey::Hash::SHA1;
88 }
89
90 size_t GetMaxSignatureLength() override { return max_length_; }
91
92 Error SignDigest(SSLPrivateKey::Hash hash,
93 const base::StringPiece& input,
94 std::vector<uint8_t>* signature) override {
95 ALG_ID hash_alg = 0;
96 switch (hash) {
97 case SSLPrivateKey::Hash::MD5_SHA1:
98 hash_alg = CALG_SSL3_SHAMD5;
99 break;
100 case SSLPrivateKey::Hash::SHA1:
101 hash_alg = CALG_SHA1;
102 break;
103 case SSLPrivateKey::Hash::SHA256:
104 hash_alg = CALG_SHA_256;
105 break;
106 case SSLPrivateKey::Hash::SHA384:
107 hash_alg = CALG_SHA_384;
108 break;
109 case SSLPrivateKey::Hash::SHA512:
110 hash_alg = CALG_SHA_512;
111 break;
112 }
113
114 crypto::ScopedHCRYPTHASH hash_handle;
115 if (!CryptCreateHash(provider_, hash_alg, 0, 0, hash_handle.receive())) {
116 PLOG(ERROR) << "CreateCreateHash failed";
117 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
118 }
119 DWORD hash_len;
120 DWORD arg_len = sizeof(hash_len);
121 if (!CryptGetHashParam(hash_handle.get(), HP_HASHSIZE,
122 reinterpret_cast<BYTE*>(&hash_len), &arg_len, 0)) {
123 PLOG(ERROR) << "CryptGetHashParam HP_HASHSIZE failed";
124 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
125 }
126 if (hash_len != input.size())
127 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
128 if (!CryptSetHashParam(
129 hash_handle.get(), HP_HASHVAL,
130 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(input.data())),
131 0)) {
132 PLOG(ERROR) << "CryptSetHashParam HP_HASHVAL failed";
133 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
134 }
135 DWORD signature_len = 0;
136 if (!CryptSignHash(hash_handle.get(), key_spec_, nullptr, 0, nullptr,
137 &signature_len)) {
138 PLOG(ERROR) << "CryptSignHash failed";
139 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
140 }
141 signature->resize(signature_len);
142 if (!CryptSignHash(hash_handle.get(), key_spec_, nullptr, 0,
143 vector_as_array(signature), &signature_len)) {
144 PLOG(ERROR) << "CryptSignHash failed";
145 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
146 }
147 signature->resize(signature_len);
148
149 // CryptoAPI signs in little-endian, so reverse it.
150 std::reverse(signature->begin(), signature->end());
151 return OK;
152 }
153
154 private:
155 crypto::ScopedHCRYPTPROV provider_;
156 DWORD key_spec_;
157 size_t max_length_;
158
159 DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyCAPI);
160 };
161
162 class SSLPlatformKeyCNG : public ThreadedSSLPrivateKey::Delegate {
163 public:
164 // Takes ownership of |key|.
165 SSLPlatformKeyCNG(NCRYPT_KEY_HANDLE key,
166 SSLPrivateKey::Type type,
167 size_t max_length)
168 : key_(key), type_(type), max_length_(max_length) {}
169
170 ~SSLPlatformKeyCNG() override {
171 g_cng_functions.Get().ncrypt_free_object()(key_);
172 }
173
174 SSLPrivateKey::Type GetType() override { return type_; }
175
176 bool SupportsHash(SSLPrivateKey::Hash hash) override {
177 // If the key is a 1024-bit RSA, assume conservatively that it may only be
178 // able to sign SHA-1 hashes. This is the case for older Estonian ID cards
179 // that have 1024-bit RSA keys. (For an RSA key, the maximum signature
180 // length is the size of the modulus in bytes.)
181 //
182 // CNG does provide NCryptIsAlgSupported and NCryptEnumAlgorithms functions,
183 // however they seem to both return NTE_NOT_SUPPORTED when querying the
184 // NCRYPT_PROV_HANDLE at the key's NCRYPT_PROVIDER_HANDLE_PROPERTY.
185 if (type_ == SSLPrivateKey::Type::RSA && max_length_ <= 1024 / 8) {
186 return hash == SSLPrivateKey::Hash::MD5_SHA1 ||
187 hash == SSLPrivateKey::Hash::SHA1;
188 }
189 return true;
190 }
191
192 size_t GetMaxSignatureLength() override { return max_length_; }
193
194 Error SignDigest(SSLPrivateKey::Hash hash,
195 const base::StringPiece& input,
196 std::vector<uint8_t>* signature) override {
197 crypto::OpenSSLErrStackTracer tracer(FROM_HERE);
198
199 BCRYPT_PKCS1_PADDING_INFO rsa_padding_info;
Ryan Sleevi 2015/06/23 15:27:29 = {0}; ?
davidben 2015/06/24 21:43:12 Done.
200 void* padding_info = nullptr;
201 DWORD flags = 0;
202 if (type_ == SSLPrivateKey::Type::RSA) {
203 switch (hash) {
204 case SSLPrivateKey::Hash::MD5_SHA1:
205 rsa_padding_info.pszAlgId = nullptr;
206 break;
207 case SSLPrivateKey::Hash::SHA1:
208 rsa_padding_info.pszAlgId = BCRYPT_SHA1_ALGORITHM;
209 break;
210 case SSLPrivateKey::Hash::SHA256:
211 rsa_padding_info.pszAlgId = BCRYPT_SHA256_ALGORITHM;
212 break;
213 case SSLPrivateKey::Hash::SHA384:
214 rsa_padding_info.pszAlgId = BCRYPT_SHA384_ALGORITHM;
215 break;
216 case SSLPrivateKey::Hash::SHA512:
217 rsa_padding_info.pszAlgId = BCRYPT_SHA512_ALGORITHM;
218 break;
219 }
220 padding_info = &rsa_padding_info;
221 flags |= BCRYPT_PAD_PKCS1;
222 }
223
224 DWORD signature_len;
225 SECURITY_STATUS status = g_cng_functions.Get().ncrypt_sign_hash()(
226 key_, padding_info,
227 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(input.data())),
228 input.size(), nullptr, 0, &signature_len, flags);
229 if (FAILED(status)) {
230 LOG(ERROR) << "NCryptSignHash failed: " << status;
231 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
232 }
233 signature->resize(signature_len);
234 status = g_cng_functions.Get().ncrypt_sign_hash()(
235 key_, padding_info,
236 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(input.data())),
237 input.size(), vector_as_array(signature), signature_len, &signature_len,
238 flags);
239 if (FAILED(status)) {
240 LOG(ERROR) << "NCryptSignHash failed: " << status;
241 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
242 }
243 signature->resize(signature_len);
244
245 // CNG emits raw ECDSA signatures, but BoringSSL expects a DER-encoded
246 // ECDSA-Sig-Value.
247 if (type_ == SSLPrivateKey::Type::ECDSA) {
248 if (signature->size() % 2 != 0) {
249 LOG(ERROR) << "Bad signature length";
250 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
251 }
252 size_t order_len = signature->size() / 2;
253
254 // Convert the RAW ECDSA signature to a DER-encoded ECDSA-Sig-Value.
255 crypto::ScopedECDSA_SIG sig(ECDSA_SIG_new());
256 if (!sig)
257 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
258 sig->r = BN_bin2bn(vector_as_array(signature), order_len, nullptr);
259 sig->s =
260 BN_bin2bn(vector_as_array(signature) + order_len, order_len, nullptr);
261 if (!sig->r || !sig->s)
Ryan Sleevi 2015/06/23 15:27:29 Confirmed that ECDSA_sig_free does the right thing
davidben 2015/06/24 21:43:12 Yeah, BoringSSL free functions should all check fo
262 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
263
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);
273 }
274
275 return OK;
276 }
277
278 private:
279 NCRYPT_KEY_HANDLE key_;
280 SSLPrivateKey::Type type_;
281 size_t max_length_;
282
283 DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyCNG);
284 };
285
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);
292
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(NULL, &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;
313 }
314 *out_max_length = EVP_PKEY_size(key.get());
315 return true;
316 }
317
318 } // namespace
319
320 scoped_ptr<SSLPrivateKey> FetchClientCertPrivateKey(
321 X509Certificate* certificate,
322 const scoped_refptr<base::TaskRunner>& 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;
330
331 PCCERT_CONTEXT cert_context = certificate->os_cert_handle();
332
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;
339
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;
344 }
345
346 // Should never get a cached handle back - ownership must always be
347 // transferred.
348 CHECK_EQ(must_free, TRUE);
349
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));
356 }
357 return make_scoped_ptr(
358 new ThreadedSSLPrivateKey(delegate.Pass(), task_runner));
359 }
360
361 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698