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/ec_key.h>
8 #include <openssl/err.h>
9 #include <openssl/engine.h>
10 #include <openssl/evp.h>
11 #include <openssl/rsa.h>
13 #include <Security/cssm.h>
14 #include <Security/SecBase.h>
15 #include <Security/SecCertificate.h>
16 #include <Security/SecIdentity.h>
17 #include <Security/SecKey.h>
19 #include "base/lazy_instance.h"
20 #include "base/location.h"
21 #include "base/logging.h"
22 #include "base/mac/mac_logging.h"
23 #include "base/mac/scoped_cftyperef.h"
24 #include "base/memory/scoped_policy.h"
25 #include "base/memory/scoped_ptr.h"
26 #include "base/synchronization/lock.h"
27 #include "crypto/mac_security_services_lock.h"
28 #include "net/base/net_errors.h"
29 #include "net/cert/x509_certificate.h"
30 #include "net/ssl/openssl_ssl_util.h"
36 class ScopedCSSM_CC_HANDLE
{
38 ScopedCSSM_CC_HANDLE() : handle_(0) {
41 ~ScopedCSSM_CC_HANDLE() {
45 CSSM_CC_HANDLE
get() const {
51 CSSM_DeleteContext(handle_
);
55 CSSM_CC_HANDLE
* InitializeInto() {
60 CSSM_CC_HANDLE handle_
;
62 DISALLOW_COPY_AND_ASSIGN(ScopedCSSM_CC_HANDLE
);
65 // Looks up the private key for |certificate| in KeyChain and returns
66 // a SecKeyRef or NULL on failure. The caller takes ownership of the
68 SecKeyRef
FetchSecKeyRefForCertificate(const X509Certificate
* certificate
) {
70 base::ScopedCFTypeRef
<SecIdentityRef
> identity
;
72 base::AutoLock
lock(crypto::GetMacSecurityServicesLock());
73 status
= SecIdentityCreateWithCertificate(
74 NULL
, certificate
->os_cert_handle(), identity
.InitializeInto());
76 if (status
!= noErr
) {
77 OSSTATUS_LOG(WARNING
, status
);
81 base::ScopedCFTypeRef
<SecKeyRef
> private_key
;
82 status
= SecIdentityCopyPrivateKey(identity
, private_key
.InitializeInto());
83 if (status
!= noErr
) {
84 OSSTATUS_LOG(WARNING
, status
);
88 return private_key
.release();
91 extern const RSA_METHOD mac_rsa_method
;
92 extern const ECDSA_METHOD mac_ecdsa_method
;
94 // KeyExData contains the data that is contained in the EX_DATA of the
95 // RSA and ECDSA objects that are created to wrap Mac system keys.
97 KeyExData(SecKeyRef key
, const CSSM_KEY
* cssm_key
)
98 : key(key
, base::scoped_policy::RETAIN
), cssm_key(cssm_key
) {}
100 base::ScopedCFTypeRef
<SecKeyRef
> key
;
101 const CSSM_KEY
* cssm_key
;
104 // ExDataDup is called when one of the RSA or EC_KEY objects is
105 // duplicated. This is not supported and should never happen.
106 int ExDataDup(CRYPTO_EX_DATA
* to
,
107 const CRYPTO_EX_DATA
* from
,
112 CHECK_EQ((void*)NULL
, *from_d
);
116 // ExDataFree is called when one of the RSA or EC_KEY objects is freed.
117 void ExDataFree(void* parent
,
119 CRYPTO_EX_DATA
* ex_data
,
121 long argl
, void* argp
) {
122 KeyExData
* data
= reinterpret_cast<KeyExData
*>(ptr
);
126 // BoringSSLEngine is a BoringSSL ENGINE that implements RSA and ECDSA
127 // by forwarding the requested operations to Apple's CSSM
129 class BoringSSLEngine
{
132 : rsa_index_(RSA_get_ex_new_index(0 /* argl */,
137 ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */,
142 engine_(ENGINE_new()) {
143 ENGINE_set_RSA_method(
144 engine_
, &mac_rsa_method
, sizeof(mac_rsa_method
));
145 ENGINE_set_ECDSA_method(
146 engine_
, &mac_ecdsa_method
, sizeof(mac_ecdsa_method
));
149 int rsa_ex_index() const { return rsa_index_
; }
150 int ec_key_ex_index() const { return ec_key_index_
; }
152 const ENGINE
* engine() const { return engine_
; }
155 const int rsa_index_
;
156 const int ec_key_index_
;
157 ENGINE
* const engine_
;
160 base::LazyInstance
<BoringSSLEngine
>::Leaky global_boringssl_engine
=
161 LAZY_INSTANCE_INITIALIZER
;
163 // Helper function for making a signature.
165 // MakeCSSMSignature uses the key information in |ex_data| to sign the
166 // |in_len| bytes pointed by |in|. It writes up to |max_out| bytes
167 // into the buffer pointed to by |out|, setting |*out_len| to the
168 // number of bytes written. It returns 1 on success and 0 on failure.
169 int MakeCSSMSignature(const KeyExData
* ex_data
,
175 CSSM_CSP_HANDLE csp_handle
;
176 OSStatus status
= SecKeyGetCSPHandle(ex_data
->key
.get(), &csp_handle
);
177 if (status
!= noErr
) {
178 OSSTATUS_LOG(WARNING
, status
);
179 OPENSSL_PUT_ERROR(RSA
, sign_raw
, ERR_R_INTERNAL_ERROR
);
183 const CSSM_ACCESS_CREDENTIALS
* cssm_creds
= NULL
;
184 status
= SecKeyGetCredentials(ex_data
->key
.get(), CSSM_ACL_AUTHORIZATION_SIGN
,
185 kSecCredentialTypeDefault
, &cssm_creds
);
186 if (status
!= noErr
) {
187 OSSTATUS_LOG(WARNING
, status
);
188 OpenSSLPutNetError(FROM_HERE
, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
);
192 ScopedCSSM_CC_HANDLE cssm_signature
;
193 if (CSSM_CSP_CreateSignatureContext(
194 csp_handle
, ex_data
->cssm_key
->KeyHeader
.AlgorithmId
, cssm_creds
,
195 ex_data
->cssm_key
, cssm_signature
.InitializeInto()) != CSSM_OK
) {
196 OpenSSLPutNetError(FROM_HERE
, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
);
200 if (ex_data
->cssm_key
->KeyHeader
.AlgorithmId
== CSSM_ALGID_RSA
) {
202 CSSM_CONTEXT_ATTRIBUTE blinding_attr
;
203 blinding_attr
.AttributeType
= CSSM_ATTRIBUTE_RSA_BLINDING
;
204 blinding_attr
.AttributeLength
= sizeof(uint32
);
205 blinding_attr
.Attribute
.Uint32
= 1;
206 if (CSSM_UpdateContextAttributes(
207 cssm_signature
.get(), 1, &blinding_attr
) != CSSM_OK
) {
208 OpenSSLPutNetError(FROM_HERE
, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
);
214 hash_data
.Length
= in_len
;
215 hash_data
.Data
= const_cast<uint8
*>(in
);
217 CSSM_DATA signature_data
;
218 signature_data
.Length
= max_out
;
219 signature_data
.Data
= out
;
221 if (CSSM_SignData(cssm_signature
.get(), &hash_data
, 1,
222 CSSM_ALGID_NONE
, &signature_data
) != CSSM_OK
) {
223 OpenSSLPutNetError(FROM_HERE
, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
);
227 *out_len
= signature_data
.Length
;
231 // Custom RSA_METHOD that uses the platform APIs for signing.
233 const KeyExData
* RsaGetExData(const RSA
* rsa
) {
234 return reinterpret_cast<const KeyExData
*>(
235 RSA_get_ex_data(rsa
, global_boringssl_engine
.Get().rsa_ex_index()));
238 size_t RsaMethodSize(const RSA
*rsa
) {
239 const KeyExData
*ex_data
= RsaGetExData(rsa
);
240 return (ex_data
->cssm_key
->KeyHeader
.LogicalKeySizeInBits
+ 7) / 8;
243 int RsaMethodEncrypt(RSA
* rsa
,
251 OPENSSL_PUT_ERROR(RSA
, encrypt
, RSA_R_UNKNOWN_ALGORITHM_TYPE
);
255 int RsaMethodSignRaw(RSA
* rsa
,
262 // Only support PKCS#1 padding.
263 DCHECK_EQ(RSA_PKCS1_PADDING
, padding
);
264 if (padding
!= RSA_PKCS1_PADDING
) {
265 OPENSSL_PUT_ERROR(RSA
, sign_raw
, RSA_R_UNKNOWN_PADDING_TYPE
);
269 const KeyExData
*ex_data
= RsaGetExData(rsa
);
271 OPENSSL_PUT_ERROR(RSA
, sign_raw
, ERR_R_INTERNAL_ERROR
);
274 DCHECK_EQ(CSSM_ALGID_RSA
, ex_data
->cssm_key
->KeyHeader
.AlgorithmId
);
276 return MakeCSSMSignature(ex_data
, out_len
, out
, max_out
, in
, in_len
);
279 int RsaMethodDecrypt(RSA
* rsa
,
287 OPENSSL_PUT_ERROR(RSA
, decrypt
, RSA_R_UNKNOWN_ALGORITHM_TYPE
);
291 int RsaMethodVerifyRaw(RSA
* rsa
,
299 OPENSSL_PUT_ERROR(RSA
, verify_raw
, RSA_R_UNKNOWN_ALGORITHM_TYPE
);
303 const RSA_METHOD mac_rsa_method
= {
319 NULL
/* private_transform */,
321 NULL
/* bn_mod_exp */,
326 crypto::ScopedEVP_PKEY
CreateRSAWrapper(SecKeyRef key
,
327 const CSSM_KEY
* cssm_key
) {
328 crypto::ScopedRSA
rsa(
329 RSA_new_method(global_boringssl_engine
.Get().engine()));
331 return crypto::ScopedEVP_PKEY();
334 rsa
.get(), global_boringssl_engine
.Get().rsa_ex_index(),
335 new KeyExData(key
, cssm_key
));
337 crypto::ScopedEVP_PKEY
pkey(EVP_PKEY_new());
339 return crypto::ScopedEVP_PKEY();
341 if (!EVP_PKEY_set1_RSA(pkey
.get(), rsa
.get()))
342 return crypto::ScopedEVP_PKEY();
347 // Custom ECDSA_METHOD that uses the platform APIs.
348 // Note that for now, only signing through ECDSA_sign() is really supported.
349 // all other method pointers are either stubs returning errors, or no-ops.
351 const KeyExData
* EcKeyGetExData(const EC_KEY
* ec_key
) {
352 return reinterpret_cast<const KeyExData
*>(EC_KEY_get_ex_data(
353 ec_key
, global_boringssl_engine
.Get().ec_key_ex_index()));
356 size_t EcdsaMethodGroupOrderSize(const EC_KEY
* ec_key
) {
357 const KeyExData
* ex_data
= EcKeyGetExData(ec_key
);
358 // LogicalKeySizeInBits is the size of an EC public key. But an
359 // ECDSA signature length depends on the size of the base point's
360 // order. For P-256, P-384, and P-521, these two sizes are the same.
361 return (ex_data
->cssm_key
->KeyHeader
.LogicalKeySizeInBits
+ 7) / 8;
364 int EcdsaMethodSign(const uint8_t* digest
,
367 unsigned int* sig_len
,
369 const KeyExData
*ex_data
= EcKeyGetExData(ec_key
);
371 OPENSSL_PUT_ERROR(RSA
, sign_raw
, ERR_R_INTERNAL_ERROR
);
374 DCHECK_EQ(CSSM_ALGID_ECDSA
, ex_data
->cssm_key
->KeyHeader
.AlgorithmId
);
376 // TODO(davidben): Fix BoringSSL to make sig_len a size_t*.
378 int ret
= MakeCSSMSignature(
379 ex_data
, &out_len
, sig
, ECDSA_size(ec_key
), digest
, digest_len
);
386 int EcdsaMethodVerify(const uint8_t* digest
,
392 OPENSSL_PUT_ERROR(ECDSA
, ECDSA_do_verify
, ECDSA_R_NOT_IMPLEMENTED
);
396 const ECDSA_METHOD mac_ecdsa_method
= {
405 EcdsaMethodGroupOrderSize
,
411 crypto::ScopedEVP_PKEY
CreateECDSAWrapper(SecKeyRef key
,
412 const CSSM_KEY
* cssm_key
) {
413 crypto::ScopedEC_KEY
ec_key(
414 EC_KEY_new_method(global_boringssl_engine
.Get().engine()));
416 return crypto::ScopedEVP_PKEY();
419 ec_key
.get(), global_boringssl_engine
.Get().ec_key_ex_index(),
420 new KeyExData(key
, cssm_key
));
422 crypto::ScopedEVP_PKEY
pkey(EVP_PKEY_new());
424 return crypto::ScopedEVP_PKEY();
426 if (!EVP_PKEY_set1_EC_KEY(pkey
.get(), ec_key
.get()))
427 return crypto::ScopedEVP_PKEY();
432 crypto::ScopedEVP_PKEY
CreatePkeyWrapper(SecKeyRef key
) {
433 const CSSM_KEY
* cssm_key
;
434 OSStatus status
= SecKeyGetCSSMKey(key
, &cssm_key
);
436 return crypto::ScopedEVP_PKEY();
438 switch (cssm_key
->KeyHeader
.AlgorithmId
) {
440 return CreateRSAWrapper(key
, cssm_key
);
441 case CSSM_ALGID_ECDSA
:
442 return CreateECDSAWrapper(key
, cssm_key
);
444 // TODO(davidben): Filter out anything other than ECDSA and RSA
445 // elsewhere. We don't support other key types.
447 LOG(ERROR
) << "Unknown key type";
448 return crypto::ScopedEVP_PKEY();
454 crypto::ScopedEVP_PKEY
FetchClientCertPrivateKey(
455 const X509Certificate
* certificate
) {
456 // Look up the private key.
457 base::ScopedCFTypeRef
<SecKeyRef
> private_key(
458 FetchSecKeyRefForCertificate(certificate
));
460 return crypto::ScopedEVP_PKEY();
462 // Create an EVP_PKEY wrapper.
463 return CreatePkeyWrapper(private_key
.get());