Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / ssl / ssl_platform_key_mac.cc
blob2f2974c47582208b96caf0e4c1554165268dc19b
1 // Copyright 2015 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/ssl_platform_key.h"
7 #include <openssl/ecdsa.h>
8 #include <openssl/obj.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/sequenced_task_runner.h"
25 #include "base/stl_util.h"
26 #include "base/synchronization/lock.h"
27 #include "crypto/mac_security_services_lock.h"
28 #include "crypto/openssl_util.h"
29 #include "crypto/scoped_openssl_types.h"
30 #include "net/base/net_errors.h"
31 #include "net/cert/x509_certificate.h"
32 #include "net/ssl/ssl_private_key.h"
33 #include "net/ssl/threaded_ssl_private_key.h"
35 namespace net {
37 namespace {
39 class ScopedCSSM_CC_HANDLE {
40 public:
41 ScopedCSSM_CC_HANDLE() : handle_(0) {}
42 explicit ScopedCSSM_CC_HANDLE(CSSM_CC_HANDLE handle) : handle_(handle) {}
44 ~ScopedCSSM_CC_HANDLE() { reset(); }
46 CSSM_CC_HANDLE get() const { return handle_; }
48 void reset() {
49 if (handle_)
50 CSSM_DeleteContext(handle_);
51 handle_ = 0;
54 private:
55 CSSM_CC_HANDLE handle_;
57 DISALLOW_COPY_AND_ASSIGN(ScopedCSSM_CC_HANDLE);
60 // Looks up the private key for |certificate| in KeyChain and returns
61 // a SecKeyRef or nullptr on failure. The caller takes ownership of the
62 // result.
63 SecKeyRef FetchSecKeyRefForCertificate(const X509Certificate* certificate) {
64 OSStatus status;
65 base::ScopedCFTypeRef<SecIdentityRef> identity;
67 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
68 status = SecIdentityCreateWithCertificate(
69 nullptr, certificate->os_cert_handle(), identity.InitializeInto());
71 if (status != noErr) {
72 OSSTATUS_LOG(WARNING, status);
73 return nullptr;
76 base::ScopedCFTypeRef<SecKeyRef> private_key;
77 status = SecIdentityCopyPrivateKey(identity, private_key.InitializeInto());
78 if (status != noErr) {
79 OSSTATUS_LOG(WARNING, status);
80 return nullptr;
83 return private_key.release();
86 class SSLPlatformKeyMac : public ThreadedSSLPrivateKey::Delegate {
87 public:
88 SSLPlatformKeyMac(SecKeyRef key, const CSSM_KEY* cssm_key)
89 : key_(key, base::scoped_policy::RETAIN), cssm_key_(cssm_key) {
90 DCHECK(cssm_key_->KeyHeader.AlgorithmId == CSSM_ALGID_RSA ||
91 cssm_key_->KeyHeader.AlgorithmId == CSSM_ALGID_ECDSA);
94 ~SSLPlatformKeyMac() override {}
96 SSLPrivateKey::Type GetType() override {
97 if (cssm_key_->KeyHeader.AlgorithmId == CSSM_ALGID_RSA) {
98 return SSLPrivateKey::Type::RSA;
99 } else {
100 DCHECK_EQ(CSSM_ALGID_ECDSA, cssm_key_->KeyHeader.AlgorithmId);
101 return SSLPrivateKey::Type::ECDSA;
105 bool SupportsHash(SSLPrivateKey::Hash hash) override { return true; }
107 size_t GetMaxSignatureLengthInBytes() override {
108 if (cssm_key_->KeyHeader.AlgorithmId == CSSM_ALGID_RSA) {
109 return (cssm_key_->KeyHeader.LogicalKeySizeInBits + 7) / 8;
110 } else {
111 // LogicalKeySizeInBits is the size of an EC public key. But an
112 // ECDSA signature length depends on the size of the base point's
113 // order. For P-256, P-384, and P-521, these two sizes are the same.
114 return ECDSA_SIG_max_len((cssm_key_->KeyHeader.LogicalKeySizeInBits + 7) /
119 Error SignDigest(SSLPrivateKey::Hash hash,
120 const base::StringPiece& input,
121 std::vector<uint8_t>* signature) override {
122 crypto::OpenSSLErrStackTracer tracer(FROM_HERE);
124 CSSM_CSP_HANDLE csp_handle;
125 OSStatus status = SecKeyGetCSPHandle(key_.get(), &csp_handle);
126 if (status != noErr) {
127 OSSTATUS_LOG(WARNING, status);
128 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
131 const CSSM_ACCESS_CREDENTIALS* cssm_creds = nullptr;
132 status = SecKeyGetCredentials(key_.get(), CSSM_ACL_AUTHORIZATION_SIGN,
133 kSecCredentialTypeDefault, &cssm_creds);
134 if (status != noErr) {
135 OSSTATUS_LOG(WARNING, status);
136 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
139 CSSM_CC_HANDLE cssm_signature_raw = 0;
140 if (CSSM_CSP_CreateSignatureContext(
141 csp_handle, cssm_key_->KeyHeader.AlgorithmId, cssm_creds, cssm_key_,
142 &cssm_signature_raw) != CSSM_OK) {
143 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
145 ScopedCSSM_CC_HANDLE cssm_signature(cssm_signature_raw);
147 CSSM_DATA hash_data;
148 hash_data.Length = input.size();
149 hash_data.Data =
150 const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(input.data()));
152 crypto::ScopedOpenSSLBytes free_digest_info;
153 if (cssm_key_->KeyHeader.AlgorithmId == CSSM_ALGID_RSA) {
154 // CSSM expects the caller to prepend the DigestInfo.
155 int hash_nid = NID_undef;
156 switch (hash) {
157 case SSLPrivateKey::Hash::MD5_SHA1:
158 hash_nid = NID_md5_sha1;
159 break;
160 case SSLPrivateKey::Hash::SHA1:
161 hash_nid = NID_sha1;
162 break;
163 case SSLPrivateKey::Hash::SHA256:
164 hash_nid = NID_sha256;
165 break;
166 case SSLPrivateKey::Hash::SHA384:
167 hash_nid = NID_sha384;
168 break;
169 case SSLPrivateKey::Hash::SHA512:
170 hash_nid = NID_sha512;
171 break;
173 DCHECK_NE(NID_undef, hash_nid);
174 int is_alloced;
175 if (!RSA_add_pkcs1_prefix(&hash_data.Data, &hash_data.Length, &is_alloced,
176 hash_nid, hash_data.Data, hash_data.Length)) {
177 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
179 if (is_alloced)
180 free_digest_info.reset(hash_data.Data);
182 // Set RSA blinding.
183 CSSM_CONTEXT_ATTRIBUTE blinding_attr;
184 blinding_attr.AttributeType = CSSM_ATTRIBUTE_RSA_BLINDING;
185 blinding_attr.AttributeLength = sizeof(uint32_t);
186 blinding_attr.Attribute.Uint32 = 1;
187 if (CSSM_UpdateContextAttributes(cssm_signature.get(), 1,
188 &blinding_attr) != CSSM_OK) {
189 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
193 signature->resize(GetMaxSignatureLengthInBytes());
194 CSSM_DATA signature_data;
195 signature_data.Length = signature->size();
196 signature_data.Data = vector_as_array(signature);
198 if (CSSM_SignData(cssm_signature.get(), &hash_data, 1, CSSM_ALGID_NONE,
199 &signature_data) != CSSM_OK) {
200 return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
202 signature->resize(signature_data.Length);
203 return OK;
206 private:
207 base::ScopedCFTypeRef<SecKeyRef> key_;
208 const CSSM_KEY* cssm_key_;
210 DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyMac);
213 } // namespace
215 scoped_ptr<SSLPrivateKey> FetchClientCertPrivateKey(
216 X509Certificate* certificate,
217 scoped_refptr<base::SequencedTaskRunner> task_runner) {
218 // Look up the private key.
219 base::ScopedCFTypeRef<SecKeyRef> private_key(
220 FetchSecKeyRefForCertificate(certificate));
221 if (!private_key)
222 return nullptr;
224 const CSSM_KEY* cssm_key;
225 OSStatus status = SecKeyGetCSSMKey(private_key.get(), &cssm_key);
226 if (status != noErr)
227 return nullptr;
229 if (cssm_key->KeyHeader.AlgorithmId != CSSM_ALGID_RSA &&
230 cssm_key->KeyHeader.AlgorithmId != CSSM_ALGID_ECDSA) {
231 LOG(ERROR) << "Unknown key type: " << cssm_key->KeyHeader.AlgorithmId;
232 return nullptr;
234 return make_scoped_ptr(new ThreadedSSLPrivateKey(
235 make_scoped_ptr(new SSLPlatformKeyMac(private_key.get(), cssm_key)),
236 task_runner.Pass()));
239 } // namespace net