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
= {
308 nullptr /* app_data */,
311 nullptr /* finish */,
314 nullptr /* verify */,
319 nullptr /* private_transform */,
320 nullptr /* mod_exp */,
321 nullptr /* bn_mod_exp */,
323 nullptr /* keygen */,
324 nullptr /* multi_prime_keygen */,
325 nullptr /* supports_digest */,
328 crypto::ScopedEVP_PKEY
CreateRSAWrapper(SecKeyRef key
,
329 const CSSM_KEY
* cssm_key
) {
330 crypto::ScopedRSA
rsa(
331 RSA_new_method(global_boringssl_engine
.Get().engine()));
333 return crypto::ScopedEVP_PKEY();
336 rsa
.get(), global_boringssl_engine
.Get().rsa_ex_index(),
337 new KeyExData(key
, cssm_key
));
339 crypto::ScopedEVP_PKEY
pkey(EVP_PKEY_new());
341 return crypto::ScopedEVP_PKEY();
343 if (!EVP_PKEY_set1_RSA(pkey
.get(), rsa
.get()))
344 return crypto::ScopedEVP_PKEY();
349 // Custom ECDSA_METHOD that uses the platform APIs.
350 // Note that for now, only signing through ECDSA_sign() is really supported.
351 // all other method pointers are either stubs returning errors, or no-ops.
353 const KeyExData
* EcKeyGetExData(const EC_KEY
* ec_key
) {
354 return reinterpret_cast<const KeyExData
*>(EC_KEY_get_ex_data(
355 ec_key
, global_boringssl_engine
.Get().ec_key_ex_index()));
358 size_t EcdsaMethodGroupOrderSize(const EC_KEY
* ec_key
) {
359 const KeyExData
* ex_data
= EcKeyGetExData(ec_key
);
360 // LogicalKeySizeInBits is the size of an EC public key. But an
361 // ECDSA signature length depends on the size of the base point's
362 // order. For P-256, P-384, and P-521, these two sizes are the same.
363 return (ex_data
->cssm_key
->KeyHeader
.LogicalKeySizeInBits
+ 7) / 8;
366 int EcdsaMethodSign(const uint8_t* digest
,
369 unsigned int* sig_len
,
371 const KeyExData
*ex_data
= EcKeyGetExData(ec_key
);
373 OPENSSL_PUT_ERROR(RSA
, sign_raw
, ERR_R_INTERNAL_ERROR
);
376 DCHECK_EQ(CSSM_ALGID_ECDSA
, ex_data
->cssm_key
->KeyHeader
.AlgorithmId
);
378 // TODO(davidben): Fix BoringSSL to make sig_len a size_t*.
380 int ret
= MakeCSSMSignature(
381 ex_data
, &out_len
, sig
, ECDSA_size(ec_key
), digest
, digest_len
);
388 int EcdsaMethodVerify(const uint8_t* digest
,
394 OPENSSL_PUT_ERROR(ECDSA
, ECDSA_do_verify
, ECDSA_R_NOT_IMPLEMENTED
);
398 const ECDSA_METHOD mac_ecdsa_method
= {
407 EcdsaMethodGroupOrderSize
,
413 crypto::ScopedEVP_PKEY
CreateECDSAWrapper(SecKeyRef key
,
414 const CSSM_KEY
* cssm_key
) {
415 crypto::ScopedEC_KEY
ec_key(
416 EC_KEY_new_method(global_boringssl_engine
.Get().engine()));
418 return crypto::ScopedEVP_PKEY();
421 ec_key
.get(), global_boringssl_engine
.Get().ec_key_ex_index(),
422 new KeyExData(key
, cssm_key
));
424 crypto::ScopedEVP_PKEY
pkey(EVP_PKEY_new());
426 return crypto::ScopedEVP_PKEY();
428 if (!EVP_PKEY_set1_EC_KEY(pkey
.get(), ec_key
.get()))
429 return crypto::ScopedEVP_PKEY();
434 crypto::ScopedEVP_PKEY
CreatePkeyWrapper(SecKeyRef key
) {
435 const CSSM_KEY
* cssm_key
;
436 OSStatus status
= SecKeyGetCSSMKey(key
, &cssm_key
);
438 return crypto::ScopedEVP_PKEY();
440 switch (cssm_key
->KeyHeader
.AlgorithmId
) {
442 return CreateRSAWrapper(key
, cssm_key
);
443 case CSSM_ALGID_ECDSA
:
444 return CreateECDSAWrapper(key
, cssm_key
);
446 // TODO(davidben): Filter out anything other than ECDSA and RSA
447 // elsewhere. We don't support other key types.
449 LOG(ERROR
) << "Unknown key type";
450 return crypto::ScopedEVP_PKEY();
456 crypto::ScopedEVP_PKEY
FetchClientCertPrivateKey(
457 const X509Certificate
* certificate
) {
458 // Look up the private key.
459 base::ScopedCFTypeRef
<SecKeyRef
> private_key(
460 FetchSecKeyRefForCertificate(certificate
));
462 return crypto::ScopedEVP_PKEY();
464 // Create an EVP_PKEY wrapper.
465 return CreatePkeyWrapper(private_key
.get());