Index: net/ssl/openssl_platform_key_mac.cc |
diff --git a/net/ssl/openssl_platform_key_mac.cc b/net/ssl/openssl_platform_key_mac.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..53f7e995cff3447be5d6a1272b567adb2f6add22 |
--- /dev/null |
+++ b/net/ssl/openssl_platform_key_mac.cc |
@@ -0,0 +1,341 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "net/ssl/openssl_platform_key.h" |
+ |
+#include <openssl/err.h> |
+#include <openssl/evp.h> |
+#include <openssl/rsa.h> |
+ |
+#include <Security/cssm.h> |
+#include <Security/SecBase.h> |
+#include <Security/SecCertificate.h> |
+#include <Security/SecIdentity.h> |
+#include <Security/SecKey.h> |
+ |
+#include "base/lazy_instance.h" |
+#include "base/location.h" |
+#include "base/logging.h" |
+#include "base/mac/mac_logging.h" |
+#include "base/mac/scoped_cftyperef.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/synchronization/lock.h" |
+#include "crypto/mac_security_services_lock.h" |
+#include "net/base/net_errors.h" |
+#include "net/cert/x509_certificate.h" |
+#include "net/ssl/openssl_ssl_util.h" |
+ |
+namespace net { |
+ |
+namespace { |
+ |
+class ScopedCSSM_CC_HANDLE { |
+ public: |
+ ScopedCSSM_CC_HANDLE() : handle_(0) { |
+ } |
+ |
+ ~ScopedCSSM_CC_HANDLE() { |
+ reset(); |
+ } |
+ |
+ CSSM_CC_HANDLE get() const { |
+ return handle_; |
+ } |
+ |
+ void reset() { |
+ if (handle_) |
+ CSSM_DeleteContext(handle_); |
+ handle_ = 0; |
+ } |
+ |
+ CSSM_CC_HANDLE* InitializeInto() { |
+ reset(); |
+ return &handle_; |
+ } |
+ private: |
+ CSSM_CC_HANDLE handle_; |
+}; |
+ |
+// Looks up the private key for |certificate| in KeyChain and returns |
+// a SecKeyRef or NULL on failure. The caller takes ownership of the |
+// result. |
+SecKeyRef FetchSecKeyRefForCertificate(const X509Certificate* certificate) { |
+ OSStatus status; |
+ base::ScopedCFTypeRef<SecIdentityRef> identity; |
+ { |
+ base::AutoLock lock(crypto::GetMacSecurityServicesLock()); |
+ status = SecIdentityCreateWithCertificate( |
+ NULL, certificate->os_cert_handle(), identity.InitializeInto()); |
+ } |
+ if (status != noErr) { |
+ OSSTATUS_LOG(WARNING, status); |
+ return NULL; |
+ } |
+ |
+ base::ScopedCFTypeRef<SecKeyRef> private_key; |
+ status = SecIdentityCopyPrivateKey(identity, private_key.InitializeInto()); |
+ if (status != noErr) { |
+ OSSTATUS_LOG(WARNING, status); |
+ return NULL; |
+ } |
+ |
+ return private_key.release(); |
+} |
+ |
+ |
+void ExDataFree(void* parent, |
+ void* ptr, |
+ CRYPTO_EX_DATA* ex_data, |
+ int idx, |
+ long argl, void* argp) { |
+ SecKeyRef key = reinterpret_cast<SecKeyRef>(ptr); |
+ if (key == NULL) |
+ return; |
+ |
+ CRYPTO_set_ex_data(ex_data, idx, NULL); |
+ CFRelease(key); |
+} |
+ |
+int ExDataDup(CRYPTO_EX_DATA* to, |
+ CRYPTO_EX_DATA* from, |
+ void* from_d, |
+ int idx, |
+ long argl, |
+ void* argp) { |
+ // This should never actually get called. |
+ NOTREACHED(); |
+ SecKeyRef* key = reinterpret_cast<SecKeyRef*>(from_d); |
+ if (*key) |
+ CFRetain(*key); |
+ return 0; |
+} |
+ |
+class OpenSSLExDataIndices { |
+ public: |
+ OpenSSLExDataIndices() |
+ : rsa_index_(RSA_get_ex_new_index(0, NULL, NULL, |
+ ExDataDup, ExDataFree)) { |
+ } |
+ |
+ int rsa_index() const { return rsa_index_; } |
+ |
+ private: |
+ int rsa_index_; |
+}; |
+base::LazyInstance<OpenSSLExDataIndices>::Leaky g_indices = |
+ LAZY_INSTANCE_INITIALIZER; |
+ |
+int RsaIndex() { |
+ return g_indices.Get().rsa_index(); |
+} |
+ |
+int RsaMethodPubEnc(int flen, |
+ const unsigned char* from, |
+ unsigned char* to, |
+ RSA* rsa, |
+ int padding) { |
+ NOTIMPLEMENTED(); |
+ RSAerr(RSA_F_RSA_PUBLIC_ENCRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED); |
+ return -1; |
+} |
+ |
+int RsaMethodPubDec(int flen, |
+ const unsigned char* from, |
+ unsigned char* to, |
+ RSA* rsa, |
+ int padding) { |
+ NOTIMPLEMENTED(); |
+ RSAerr(RSA_F_RSA_PUBLIC_DECRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED); |
+ return -1; |
+} |
+ |
+int RsaMethodPrivEnc(int flen, |
+ const unsigned char *from, |
+ unsigned char *to, |
+ RSA *rsa, |
+ int padding) { |
+ // Only support PKCS#1 padding. |
+ DCHECK_EQ(RSA_PKCS1_PADDING, padding); |
+ if (padding != RSA_PKCS1_PADDING) { |
+ RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); |
+ return -1; |
+ } |
+ |
+ SecKeyRef key = |
+ reinterpret_cast<SecKeyRef>(RSA_get_ex_data(rsa, RsaIndex())); |
+ if (!key) { |
+ LOG(WARNING) << "Null SecKeyRef passed to RsaMethodPrivEnc!"; |
+ RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR); |
+ return -1; |
+ } |
+ |
+ CSSM_CSP_HANDLE csp_handle; |
+ OSStatus status = SecKeyGetCSPHandle(key, &csp_handle); |
+ if (status != noErr) { |
+ RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR); |
+ return -1; |
+ } |
+ |
+ const CSSM_KEY* cssm_key; |
+ status = SecKeyGetCSSMKey(key, &cssm_key); |
+ if (status != noErr) { |
+ RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR); |
+ return -1; |
+ } |
+ DCHECK_EQ(CSSM_ALGID_RSA, cssm_key->KeyHeader.AlgorithmId); |
+ |
+ // TODO(davidben): (Taken from TODO(rsleevi) in sslplatf.c) Should |
+ // it be kSecCredentialTypeNoUI? In Win32, at least, you can prevent |
+ // the UI by setting the provider handle on the certificate to be |
+ // opened with CRYPT_SILENT, but is there an equivalent? |
+ const CSSM_ACCESS_CREDENTIALS * cssm_creds = NULL; |
+ status = SecKeyGetCredentials(key, CSSM_ACL_AUTHORIZATION_SIGN, |
+ kSecCredentialTypeDefault, &cssm_creds); |
+ if (status != noErr) { |
+ OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED); |
+ return -1; |
+ } |
+ |
+ ScopedCSSM_CC_HANDLE cssm_signature; |
+ if (CSSM_CSP_CreateSignatureContext( |
+ csp_handle, CSSM_ALGID_RSA, cssm_creds, |
+ cssm_key, cssm_signature.InitializeInto()) != CSSM_OK) { |
+ OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED); |
+ return -1; |
+ } |
+ |
+ // Set RSA blinding. |
+ CSSM_CONTEXT_ATTRIBUTE blinding_attr; |
+ blinding_attr.AttributeType = CSSM_ATTRIBUTE_RSA_BLINDING; |
+ blinding_attr.AttributeLength = sizeof(uint32); |
+ blinding_attr.Attribute.Uint32 = 1; |
+ if (CSSM_UpdateContextAttributes( |
+ cssm_signature.get(), 1, &blinding_attr) != CSSM_OK) { |
+ OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED); |
+ return -1; |
+ } |
+ |
+ CSSM_DATA hash_data; |
+ hash_data.Length = flen; |
+ hash_data.Data = const_cast<uint8*>(from); |
+ |
+ CSSM_DATA signature_data; |
+ signature_data.Length = RSA_size(rsa); |
+ signature_data.Data = to; |
+ |
+ if (CSSM_SignData(cssm_signature.get(), &hash_data, 1, |
+ CSSM_ALGID_NONE, &signature_data) != CSSM_OK) { |
+ OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED); |
+ return -1; |
+ } |
+ |
+ return signature_data.Length; |
+} |
+ |
+int RsaMethodPrivDec(int flen, |
+ const unsigned char* from, |
+ unsigned char* to, |
+ RSA* rsa, |
+ int padding) { |
+ NOTIMPLEMENTED(); |
+ RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED); |
+ return -1; |
+} |
+ |
+const RSA_METHOD mac_rsa_method = { |
+ /* .name = */ "Mac signing-only RSA method", |
+ /* .rsa_pub_enc = */ RsaMethodPubEnc, |
+ /* .rsa_pub_dec = */ RsaMethodPubDec, |
+ /* .rsa_priv_enc = */ RsaMethodPrivEnc, |
+ /* .rsa_priv_dec = */ RsaMethodPrivDec, |
+ /* .rsa_mod_exp = */ NULL, |
+ /* .bn_mod_exp = */ NULL, |
+ /* .init = */ NULL, |
+ /* .finish = */ NULL, |
+ // This flag is necessary to tell OpenSSL to avoid checking the content |
+ // (i.e. internal fields) of the private key. Otherwise, it will complain |
+ // it's not valid for the certificate. |
+ /* .flags = */ RSA_METHOD_FLAG_NO_CHECK, |
+ /* .app_data = */ NULL, |
+ /* .rsa_sign = */ NULL, |
+ /* .rsa_verify = */ NULL, |
+ /* .rsa_keygen = */ NULL, |
+}; |
+ |
+crypto::ScopedEVP_PKEY CreateRSAWrapper(SecKeyRef key, |
+ const CSSM_KEY* cssm_key) { |
+ crypto::ScopedRSA rsa(RSA_new()); |
+ if (!rsa) |
+ return crypto::ScopedEVP_PKEY(); |
+ RSA_set_method(rsa.get(), &mac_rsa_method); |
+ CFRetain(key); |
+ RSA_set_ex_data(rsa.get(), RsaIndex(), key); |
+ |
+ // HACK: RSA_size() doesn't work with custom RSA_METHODs. To ensure that |
+ // it will return the right value, set the 'n' field of the RSA object |
+ // to match the private key's modulus. |
+ // TODO(davidben): Avoid this after the BoringSSL transition. |
+ size_t rsa_size = (cssm_key->KeyHeader.LogicalKeySizeInBits + 7) / 8; |
+ std::vector<uint8_t> bogus(rsa_size, 0xFF); |
+ crypto::ScopedBIGNUM bn(BN_bin2bn(&bogus[0], bogus.size(), NULL)); |
+ if (!bn) |
+ return crypto::ScopedEVP_PKEY(); |
+ rsa->n = bn.release(); |
+ |
+ DCHECK_EQ(rsa_size, (size_t)RSA_size(rsa.get())); |
+ |
+ crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new()); |
+ if (!pkey) |
+ return crypto::ScopedEVP_PKEY(); |
+ |
+ if (!EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) |
+ return crypto::ScopedEVP_PKEY(); |
+ |
+ return pkey.Pass(); |
+} |
+ |
+crypto::ScopedEVP_PKEY CreateECDSAWrapper(SecKeyRef key) { |
+ // TODO(davidben): Implement ECDSA after BoringSSL transition. Most |
+ // of the signing implementation can be shared with RsaMethodPrivEnc |
+ // and pulled into a common function. |
+ NOTIMPLEMENTED(); |
+ return crypto::ScopedEVP_PKEY(); |
+} |
+ |
+crypto::ScopedEVP_PKEY CreatePkeyWrapper(SecKeyRef key) { |
+ const CSSM_KEY* cssm_key; |
+ OSStatus status = SecKeyGetCSSMKey(key, &cssm_key); |
+ if (status != noErr) |
+ return crypto::ScopedEVP_PKEY(); |
+ |
+ switch (cssm_key->KeyHeader.AlgorithmId) { |
+ case CSSM_ALGID_RSA: |
+ return CreateRSAWrapper(key, cssm_key); |
+ case CSSM_ALGID_ECDSA: |
+ return CreateECDSAWrapper(key); |
+ default: |
+ // TODO(davidben): Filter out anything other than ECDSA and RSA |
+ // elsewhere. We don't support other key types. |
+ NOTREACHED(); |
+ LOG(ERROR) << "Unknown key type"; |
+ return crypto::ScopedEVP_PKEY(); |
+ } |
+} |
+ |
+} // namespace |
+ |
+// Default missing implementation. |
wtc
2014/07/16 00:09:58
What does "missing implementation" mean?
davidben
2014/07/16 16:25:35
Oops, removed.
|
+crypto::ScopedEVP_PKEY FetchClientCertPrivateKey( |
+ const X509Certificate* certificate) { |
+ // Look up the private key. |
+ base::ScopedCFTypeRef<SecKeyRef> private_key( |
+ FetchSecKeyRefForCertificate(certificate)); |
+ if (!private_key) |
+ return crypto::ScopedEVP_PKEY(); |
+ |
+ // Create an EVP_PKEY wrapper. |
+ return CreatePkeyWrapper(private_key.get()); |
+} |
+ |
+} // namespace net |