Remove IsHighDPIEnabled, move EnableHighDPISupport to only place it's used
[chromium-blink-merge.git] / net / ssl / openssl_platform_key_mac.cc
blobd36924455df70ca05854bc5dfa83f71fede8178b
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.
5 #include "net/ssl/openssl_platform_key.h"
7 #include <openssl/err.h>
8 #include <openssl/evp.h>
9 #include <openssl/rsa.h>
11 #include <Security/cssm.h>
12 #include <Security/SecBase.h>
13 #include <Security/SecCertificate.h>
14 #include <Security/SecIdentity.h>
15 #include <Security/SecKey.h>
17 #include "base/lazy_instance.h"
18 #include "base/location.h"
19 #include "base/logging.h"
20 #include "base/mac/mac_logging.h"
21 #include "base/mac/scoped_cftyperef.h"
22 #include "base/memory/scoped_policy.h"
23 #include "base/memory/scoped_ptr.h"
24 #include "base/synchronization/lock.h"
25 #include "crypto/mac_security_services_lock.h"
26 #include "net/base/net_errors.h"
27 #include "net/cert/x509_certificate.h"
28 #include "net/ssl/openssl_ssl_util.h"
30 namespace net {
32 namespace {
34 class ScopedCSSM_CC_HANDLE {
35 public:
36 ScopedCSSM_CC_HANDLE() : handle_(0) {
39 ~ScopedCSSM_CC_HANDLE() {
40 reset();
43 CSSM_CC_HANDLE get() const {
44 return handle_;
47 void reset() {
48 if (handle_)
49 CSSM_DeleteContext(handle_);
50 handle_ = 0;
53 CSSM_CC_HANDLE* InitializeInto() {
54 reset();
55 return &handle_;
57 private:
58 CSSM_CC_HANDLE handle_;
60 DISALLOW_COPY_AND_ASSIGN(ScopedCSSM_CC_HANDLE);
63 // Looks up the private key for |certificate| in KeyChain and returns
64 // a SecKeyRef or NULL on failure. The caller takes ownership of the
65 // result.
66 SecKeyRef FetchSecKeyRefForCertificate(const X509Certificate* certificate) {
67 OSStatus status;
68 base::ScopedCFTypeRef<SecIdentityRef> identity;
70 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
71 status = SecIdentityCreateWithCertificate(
72 NULL, certificate->os_cert_handle(), identity.InitializeInto());
74 if (status != noErr) {
75 OSSTATUS_LOG(WARNING, status);
76 return NULL;
79 base::ScopedCFTypeRef<SecKeyRef> private_key;
80 status = SecIdentityCopyPrivateKey(identity, private_key.InitializeInto());
81 if (status != noErr) {
82 OSSTATUS_LOG(WARNING, status);
83 return NULL;
86 return private_key.release();
89 extern const RSA_METHOD mac_rsa_method;
90 extern const ECDSA_METHOD mac_ecdsa_method;
92 // KeyExData contains the data that is contained in the EX_DATA of the
93 // RSA and ECDSA objects that are created to wrap Mac system keys.
94 struct KeyExData {
95 KeyExData(SecKeyRef key, const CSSM_KEY* cssm_key)
96 : key(key, base::scoped_policy::RETAIN), cssm_key(cssm_key) {}
98 base::ScopedCFTypeRef<SecKeyRef> key;
99 const CSSM_KEY* cssm_key;
102 // ExDataDup is called when one of the RSA or EC_KEY objects is
103 // duplicated. This is not supported and should never happen.
104 int ExDataDup(CRYPTO_EX_DATA* to,
105 const CRYPTO_EX_DATA* from,
106 void** from_d,
107 int idx,
108 long argl,
109 void* argp) {
110 CHECK_EQ((void*)NULL, *from_d);
111 return 0;
114 // ExDataFree is called when one of the RSA or EC_KEY objects is freed.
115 void ExDataFree(void* parent,
116 void* ptr,
117 CRYPTO_EX_DATA* ex_data,
118 int idx,
119 long argl, void* argp) {
120 KeyExData* data = reinterpret_cast<KeyExData*>(ptr);
121 delete data;
124 // BoringSSLEngine is a BoringSSL ENGINE that implements RSA and ECDSA
125 // by forwarding the requested operations to Apple's CSSM
126 // implementation.
127 class BoringSSLEngine {
128 public:
129 BoringSSLEngine()
130 : rsa_index_(RSA_get_ex_new_index(0 /* argl */,
131 NULL /* argp */,
132 NULL /* new_func */,
133 ExDataDup,
134 ExDataFree)),
135 ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */,
136 NULL /* argp */,
137 NULL /* new_func */,
138 ExDataDup,
139 ExDataFree)),
140 engine_(ENGINE_new()) {
141 ENGINE_set_RSA_method(
142 engine_, &mac_rsa_method, sizeof(mac_rsa_method));
143 ENGINE_set_ECDSA_method(
144 engine_, &mac_ecdsa_method, sizeof(mac_ecdsa_method));
147 int rsa_ex_index() const { return rsa_index_; }
148 int ec_key_ex_index() const { return ec_key_index_; }
150 const ENGINE* engine() const { return engine_; }
152 private:
153 const int rsa_index_;
154 const int ec_key_index_;
155 ENGINE* const engine_;
158 base::LazyInstance<BoringSSLEngine>::Leaky global_boringssl_engine =
159 LAZY_INSTANCE_INITIALIZER;
161 // Helper function for making a signature.
163 // MakeCSSMSignature uses the key information in |ex_data| to sign the
164 // |in_len| bytes pointed by |in|. It writes up to |max_out| bytes
165 // into the buffer pointed to by |out|, setting |*out_len| to the
166 // number of bytes written. It returns 1 on success and 0 on failure.
167 int MakeCSSMSignature(const KeyExData* ex_data,
168 size_t* out_len,
169 uint8_t* out,
170 size_t max_out,
171 const uint8_t* in,
172 size_t in_len) {
173 CSSM_CSP_HANDLE csp_handle;
174 OSStatus status = SecKeyGetCSPHandle(ex_data->key.get(), &csp_handle);
175 if (status != noErr) {
176 OSSTATUS_LOG(WARNING, status);
177 OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR);
178 return 0;
181 const CSSM_ACCESS_CREDENTIALS* cssm_creds = NULL;
182 status = SecKeyGetCredentials(ex_data->key.get(), CSSM_ACL_AUTHORIZATION_SIGN,
183 kSecCredentialTypeDefault, &cssm_creds);
184 if (status != noErr) {
185 OSSTATUS_LOG(WARNING, status);
186 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
187 return 0;
190 ScopedCSSM_CC_HANDLE cssm_signature;
191 if (CSSM_CSP_CreateSignatureContext(
192 csp_handle, ex_data->cssm_key->KeyHeader.AlgorithmId, cssm_creds,
193 ex_data->cssm_key, cssm_signature.InitializeInto()) != CSSM_OK) {
194 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
195 return 0;
198 if (ex_data->cssm_key->KeyHeader.AlgorithmId == CSSM_ALGID_RSA) {
199 // Set RSA blinding.
200 CSSM_CONTEXT_ATTRIBUTE blinding_attr;
201 blinding_attr.AttributeType = CSSM_ATTRIBUTE_RSA_BLINDING;
202 blinding_attr.AttributeLength = sizeof(uint32);
203 blinding_attr.Attribute.Uint32 = 1;
204 if (CSSM_UpdateContextAttributes(
205 cssm_signature.get(), 1, &blinding_attr) != CSSM_OK) {
206 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
207 return 0;
211 CSSM_DATA hash_data;
212 hash_data.Length = in_len;
213 hash_data.Data = const_cast<uint8*>(in);
215 CSSM_DATA signature_data;
216 signature_data.Length = max_out;
217 signature_data.Data = out;
219 if (CSSM_SignData(cssm_signature.get(), &hash_data, 1,
220 CSSM_ALGID_NONE, &signature_data) != CSSM_OK) {
221 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
222 return 0;
225 *out_len = signature_data.Length;
226 return 1;
229 // Custom RSA_METHOD that uses the platform APIs for signing.
231 const KeyExData* RsaGetExData(const RSA* rsa) {
232 return reinterpret_cast<const KeyExData*>(
233 RSA_get_ex_data(rsa, global_boringssl_engine.Get().rsa_ex_index()));
236 size_t RsaMethodSize(const RSA *rsa) {
237 const KeyExData *ex_data = RsaGetExData(rsa);
238 return (ex_data->cssm_key->KeyHeader.LogicalKeySizeInBits + 7) / 8;
241 int RsaMethodEncrypt(RSA* rsa,
242 size_t* out_len,
243 uint8_t* out,
244 size_t max_out,
245 const uint8_t* in,
246 size_t in_len,
247 int padding) {
248 NOTIMPLEMENTED();
249 OPENSSL_PUT_ERROR(RSA, encrypt, RSA_R_UNKNOWN_ALGORITHM_TYPE);
250 return 0;
253 int RsaMethodSignRaw(RSA* rsa,
254 size_t* out_len,
255 uint8_t* out,
256 size_t max_out,
257 const uint8_t* in,
258 size_t in_len,
259 int padding) {
260 // Only support PKCS#1 padding.
261 DCHECK_EQ(RSA_PKCS1_PADDING, padding);
262 if (padding != RSA_PKCS1_PADDING) {
263 OPENSSL_PUT_ERROR(RSA, sign_raw, RSA_R_UNKNOWN_PADDING_TYPE);
264 return 0;
267 const KeyExData *ex_data = RsaGetExData(rsa);
268 if (!ex_data) {
269 OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR);
270 return 0;
272 DCHECK_EQ(CSSM_ALGID_RSA, ex_data->cssm_key->KeyHeader.AlgorithmId);
274 return MakeCSSMSignature(ex_data, out_len, out, max_out, in, in_len);
277 int RsaMethodDecrypt(RSA* rsa,
278 size_t* out_len,
279 uint8_t* out,
280 size_t max_out,
281 const uint8_t* in,
282 size_t in_len,
283 int padding) {
284 NOTIMPLEMENTED();
285 OPENSSL_PUT_ERROR(RSA, decrypt, RSA_R_UNKNOWN_ALGORITHM_TYPE);
286 return 0;
289 int RsaMethodVerifyRaw(RSA* rsa,
290 size_t* out_len,
291 uint8_t* out,
292 size_t max_out,
293 const uint8_t* in,
294 size_t in_len,
295 int padding) {
296 NOTIMPLEMENTED();
297 OPENSSL_PUT_ERROR(RSA, verify_raw, RSA_R_UNKNOWN_ALGORITHM_TYPE);
298 return 0;
301 const RSA_METHOD mac_rsa_method = {
303 0 /* references */,
304 1 /* is_static */
305 } /* common */,
306 NULL /* app_data */,
308 NULL /* init */,
309 NULL /* finish */,
310 RsaMethodSize,
311 NULL /* sign */,
312 NULL /* verify */,
313 RsaMethodEncrypt,
314 RsaMethodSignRaw,
315 RsaMethodDecrypt,
316 RsaMethodVerifyRaw,
317 NULL /* private_transform */,
318 NULL /* mod_exp */,
319 NULL /* bn_mod_exp */,
320 RSA_FLAG_OPAQUE,
321 NULL /* keygen */,
324 crypto::ScopedEVP_PKEY CreateRSAWrapper(SecKeyRef key,
325 const CSSM_KEY* cssm_key) {
326 crypto::ScopedRSA rsa(
327 RSA_new_method(global_boringssl_engine.Get().engine()));
328 if (!rsa)
329 return crypto::ScopedEVP_PKEY();
331 RSA_set_ex_data(
332 rsa.get(), global_boringssl_engine.Get().rsa_ex_index(),
333 new KeyExData(key, cssm_key));
335 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
336 if (!pkey)
337 return crypto::ScopedEVP_PKEY();
339 if (!EVP_PKEY_set1_RSA(pkey.get(), rsa.get()))
340 return crypto::ScopedEVP_PKEY();
342 return pkey.Pass();
345 // Custom ECDSA_METHOD that uses the platform APIs.
346 // Note that for now, only signing through ECDSA_sign() is really supported.
347 // all other method pointers are either stubs returning errors, or no-ops.
349 const KeyExData* EcKeyGetExData(const EC_KEY* ec_key) {
350 return reinterpret_cast<const KeyExData*>(EC_KEY_get_ex_data(
351 ec_key, global_boringssl_engine.Get().ec_key_ex_index()));
354 size_t EcdsaMethodGroupOrderSize(const EC_KEY* ec_key) {
355 const KeyExData* ex_data = EcKeyGetExData(ec_key);
356 // LogicalKeySizeInBits is the size of an EC public key. But an
357 // ECDSA signature length depends on the size of the base point's
358 // order. For P-256, P-384, and P-521, these two sizes are the same.
359 return (ex_data->cssm_key->KeyHeader.LogicalKeySizeInBits + 7) / 8;
362 int EcdsaMethodSign(const uint8_t* digest,
363 size_t digest_len,
364 uint8_t* sig,
365 unsigned int* sig_len,
366 EC_KEY* ec_key) {
367 const KeyExData *ex_data = EcKeyGetExData(ec_key);
368 if (!ex_data) {
369 OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR);
370 return 0;
372 DCHECK_EQ(CSSM_ALGID_ECDSA, ex_data->cssm_key->KeyHeader.AlgorithmId);
374 // TODO(davidben): Fix BoringSSL to make sig_len a size_t*.
375 size_t out_len;
376 int ret = MakeCSSMSignature(
377 ex_data, &out_len, sig, ECDSA_size(ec_key), digest, digest_len);
378 if (!ret)
379 return 0;
380 *sig_len = out_len;
381 return 1;
384 int EcdsaMethodVerify(const uint8_t* digest,
385 size_t digest_len,
386 const uint8_t* sig,
387 size_t sig_len,
388 EC_KEY* eckey) {
389 NOTIMPLEMENTED();
390 OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ECDSA_R_NOT_IMPLEMENTED);
391 return 0;
394 const ECDSA_METHOD mac_ecdsa_method = {
396 0 /* references */,
397 1 /* is_static */
398 } /* common */,
399 NULL /* app_data */,
401 NULL /* init */,
402 NULL /* finish */,
403 EcdsaMethodGroupOrderSize,
404 EcdsaMethodSign,
405 EcdsaMethodVerify,
406 ECDSA_FLAG_OPAQUE,
409 crypto::ScopedEVP_PKEY CreateECDSAWrapper(SecKeyRef key,
410 const CSSM_KEY* cssm_key) {
411 crypto::ScopedEC_KEY ec_key(
412 EC_KEY_new_method(global_boringssl_engine.Get().engine()));
413 if (!ec_key)
414 return crypto::ScopedEVP_PKEY();
416 EC_KEY_set_ex_data(
417 ec_key.get(), global_boringssl_engine.Get().ec_key_ex_index(),
418 new KeyExData(key, cssm_key));
420 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
421 if (!pkey)
422 return crypto::ScopedEVP_PKEY();
424 if (!EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()))
425 return crypto::ScopedEVP_PKEY();
427 return pkey.Pass();
430 crypto::ScopedEVP_PKEY CreatePkeyWrapper(SecKeyRef key) {
431 const CSSM_KEY* cssm_key;
432 OSStatus status = SecKeyGetCSSMKey(key, &cssm_key);
433 if (status != noErr)
434 return crypto::ScopedEVP_PKEY();
436 switch (cssm_key->KeyHeader.AlgorithmId) {
437 case CSSM_ALGID_RSA:
438 return CreateRSAWrapper(key, cssm_key);
439 case CSSM_ALGID_ECDSA:
440 return CreateECDSAWrapper(key, cssm_key);
441 default:
442 // TODO(davidben): Filter out anything other than ECDSA and RSA
443 // elsewhere. We don't support other key types.
444 NOTREACHED();
445 LOG(ERROR) << "Unknown key type";
446 return crypto::ScopedEVP_PKEY();
450 } // namespace
452 crypto::ScopedEVP_PKEY FetchClientCertPrivateKey(
453 const X509Certificate* certificate) {
454 // Look up the private key.
455 base::ScopedCFTypeRef<SecKeyRef> private_key(
456 FetchSecKeyRefForCertificate(certificate));
457 if (!private_key)
458 return crypto::ScopedEVP_PKEY();
460 // Create an EVP_PKEY wrapper.
461 return CreatePkeyWrapper(private_key.get());
464 } // namespace net