Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(42)

Side by Side Diff: net/ssl/openssl_platform_key_mac.cc

Issue 396803002: Implement TLS client auth in the OS X OpenSSL port. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: EVP_PKEY_set1_RSA has a saner ownership story. Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 #include "net/ssl/openssl_platform_key.h"
6
7 #include <openssl/err.h>
8 #include <openssl/evp.h>
9 #include <openssl/rsa.h>
10
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>
16
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_ptr.h"
23 #include "base/synchronization/lock.h"
24 #include "crypto/mac_security_services_lock.h"
25 #include "net/base/net_errors.h"
26 #include "net/cert/x509_certificate.h"
27 #include "net/ssl/openssl_ssl_util.h"
28
29 namespace net {
30
31 namespace {
32
33 class ScopedCSSM_CC_HANDLE {
34 public:
35 ScopedCSSM_CC_HANDLE() : handle_(0) {
36 }
37
38 ~ScopedCSSM_CC_HANDLE() {
39 reset();
40 }
41
42 CSSM_CC_HANDLE get() const {
43 return handle_;
44 }
45
46 void reset() {
47 if (handle_)
48 CSSM_DeleteContext(handle_);
49 handle_ = 0;
50 }
51
52 CSSM_CC_HANDLE* InitializeInto() {
53 reset();
54 return &handle_;
55 }
56 private:
57 CSSM_CC_HANDLE handle_;
58 };
59
60 // Looks up the private key for |certificate| in KeyChain and returns
61 // a SecKeyRef or NULL on failure. The caller takes ownership of the
62 // result.
63 SecKeyRef FetchSecKeyRefForCertificate(const X509Certificate* certificate) {
64 OSStatus status;
65 base::ScopedCFTypeRef<SecIdentityRef> identity;
66 {
67 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
68 status = SecIdentityCreateWithCertificate(
69 NULL, certificate->os_cert_handle(), identity.InitializeInto());
70 }
71 if (status != noErr) {
72 OSSTATUS_LOG(WARNING, status);
73 return NULL;
74 }
75
76 base::ScopedCFTypeRef<SecKeyRef> private_key;
77 status = SecIdentityCopyPrivateKey(identity, private_key.InitializeInto());
78 if (status != noErr) {
79 OSSTATUS_LOG(WARNING, status);
80 return NULL;
81 }
82
83 return private_key.release();
84 }
85
86
87 void ExDataFree(void* parent,
88 void* ptr,
89 CRYPTO_EX_DATA* ex_data,
90 int idx,
91 long argl, void* argp) {
92 SecKeyRef key = reinterpret_cast<SecKeyRef>(ptr);
93 if (key == NULL)
94 return;
95
96 CRYPTO_set_ex_data(ex_data, idx, NULL);
97 CFRelease(key);
98 }
99
100 int ExDataDup(CRYPTO_EX_DATA* to,
101 CRYPTO_EX_DATA* from,
102 void* from_d,
103 int idx,
104 long argl,
105 void* argp) {
106 // This should never actually get called.
107 NOTREACHED();
108 SecKeyRef* key = reinterpret_cast<SecKeyRef*>(from_d);
109 if (*key)
110 CFRetain(*key);
111 return 0;
112 }
113
114 class OpenSSLExDataIndices {
115 public:
116 OpenSSLExDataIndices()
117 : rsa_index_(RSA_get_ex_new_index(0, NULL, NULL,
118 ExDataDup, ExDataFree)) {
119 }
120
121 int rsa_index() const { return rsa_index_; }
122
123 private:
124 int rsa_index_;
125 };
126 base::LazyInstance<OpenSSLExDataIndices>::Leaky g_indices =
127 LAZY_INSTANCE_INITIALIZER;
128
129 int RsaIndex() {
130 return g_indices.Get().rsa_index();
131 }
132
133 int RsaMethodPubEnc(int flen,
134 const unsigned char* from,
135 unsigned char* to,
136 RSA* rsa,
137 int padding) {
138 NOTIMPLEMENTED();
139 RSAerr(RSA_F_RSA_PUBLIC_ENCRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
140 return -1;
141 }
142
143 int RsaMethodPubDec(int flen,
144 const unsigned char* from,
145 unsigned char* to,
146 RSA* rsa,
147 int padding) {
148 NOTIMPLEMENTED();
149 RSAerr(RSA_F_RSA_PUBLIC_DECRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
150 return -1;
151 }
152
153 int RsaMethodPrivEnc(int flen,
154 const unsigned char *from,
155 unsigned char *to,
156 RSA *rsa,
157 int padding) {
158 // Only support PKCS#1 padding.
159 DCHECK_EQ(RSA_PKCS1_PADDING, padding);
160 if (padding != RSA_PKCS1_PADDING) {
161 RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
162 return -1;
163 }
164
165 SecKeyRef key =
166 reinterpret_cast<SecKeyRef>(RSA_get_ex_data(rsa, RsaIndex()));
167 if (!key) {
168 LOG(WARNING) << "Null SecKeyRef passed to RsaMethodPrivEnc!";
169 RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
170 return -1;
171 }
172
173 CSSM_CSP_HANDLE csp_handle;
174 OSStatus status = SecKeyGetCSPHandle(key, &csp_handle);
175 if (status != noErr) {
176 RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
177 return -1;
178 }
179
180 const CSSM_KEY* cssm_key;
181 status = SecKeyGetCSSMKey(key, &cssm_key);
182 if (status != noErr) {
183 RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
184 return -1;
185 }
186 DCHECK_EQ(CSSM_ALGID_RSA, cssm_key->KeyHeader.AlgorithmId);
187
188 // TODO(davidben): (Taken from TODO(rsleevi) in sslplatf.c) Should
189 // it be kSecCredentialTypeNoUI? In Win32, at least, you can prevent
190 // the UI by setting the provider handle on the certificate to be
191 // opened with CRYPT_SILENT, but is there an equivalent?
192 const CSSM_ACCESS_CREDENTIALS * cssm_creds = NULL;
193 status = SecKeyGetCredentials(key, CSSM_ACL_AUTHORIZATION_SIGN,
194 kSecCredentialTypeDefault, &cssm_creds);
195 if (status != noErr) {
196 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
197 return -1;
198 }
199
200 ScopedCSSM_CC_HANDLE cssm_signature;
201 if (CSSM_CSP_CreateSignatureContext(
202 csp_handle, CSSM_ALGID_RSA, cssm_creds,
203 cssm_key, cssm_signature.InitializeInto()) != CSSM_OK) {
204 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
205 return -1;
206 }
207
208 // Set RSA blinding.
209 CSSM_CONTEXT_ATTRIBUTE blinding_attr;
210 blinding_attr.AttributeType = CSSM_ATTRIBUTE_RSA_BLINDING;
211 blinding_attr.AttributeLength = sizeof(uint32);
212 blinding_attr.Attribute.Uint32 = 1;
213 if (CSSM_UpdateContextAttributes(
214 cssm_signature.get(), 1, &blinding_attr) != CSSM_OK) {
215 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
216 return -1;
217 }
218
219 CSSM_DATA hash_data;
220 hash_data.Length = flen;
221 hash_data.Data = const_cast<uint8*>(from);
222
223 CSSM_DATA signature_data;
224 signature_data.Length = RSA_size(rsa);
225 signature_data.Data = to;
226
227 if (CSSM_SignData(cssm_signature.get(), &hash_data, 1,
228 CSSM_ALGID_NONE, &signature_data) != CSSM_OK) {
229 OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
230 return -1;
231 }
232
233 return signature_data.Length;
234 }
235
236 int RsaMethodPrivDec(int flen,
237 const unsigned char* from,
238 unsigned char* to,
239 RSA* rsa,
240 int padding) {
241 NOTIMPLEMENTED();
242 RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
243 return -1;
244 }
245
246 const RSA_METHOD mac_rsa_method = {
247 /* .name = */ "Mac signing-only RSA method",
248 /* .rsa_pub_enc = */ RsaMethodPubEnc,
249 /* .rsa_pub_dec = */ RsaMethodPubDec,
250 /* .rsa_priv_enc = */ RsaMethodPrivEnc,
251 /* .rsa_priv_dec = */ RsaMethodPrivDec,
252 /* .rsa_mod_exp = */ NULL,
253 /* .bn_mod_exp = */ NULL,
254 /* .init = */ NULL,
255 /* .finish = */ NULL,
256 // This flag is necessary to tell OpenSSL to avoid checking the content
257 // (i.e. internal fields) of the private key. Otherwise, it will complain
258 // it's not valid for the certificate.
259 /* .flags = */ RSA_METHOD_FLAG_NO_CHECK,
260 /* .app_data = */ NULL,
261 /* .rsa_sign = */ NULL,
262 /* .rsa_verify = */ NULL,
263 /* .rsa_keygen = */ NULL,
264 };
265
266 crypto::ScopedEVP_PKEY CreateRSAWrapper(SecKeyRef key,
267 const CSSM_KEY* cssm_key) {
268 crypto::ScopedRSA rsa(RSA_new());
269 if (!rsa)
270 return crypto::ScopedEVP_PKEY();
271 RSA_set_method(rsa.get(), &mac_rsa_method);
272 CFRetain(key);
273 RSA_set_ex_data(rsa.get(), RsaIndex(), key);
274
275 // HACK: RSA_size() doesn't work with custom RSA_METHODs. To ensure that
276 // it will return the right value, set the 'n' field of the RSA object
277 // to match the private key's modulus.
278 // TODO(davidben): Avoid this after the BoringSSL transition.
279 size_t rsa_size = (cssm_key->KeyHeader.LogicalKeySizeInBits + 7) / 8;
280 std::vector<uint8_t> bogus(rsa_size, 0xFF);
281 crypto::ScopedBIGNUM bn(BN_bin2bn(&bogus[0], bogus.size(), NULL));
282 if (!bn)
283 return crypto::ScopedEVP_PKEY();
284 rsa->n = bn.release();
285
286 DCHECK_EQ(rsa_size, (size_t)RSA_size(rsa.get()));
287
288 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
289 if (!pkey)
290 return crypto::ScopedEVP_PKEY();
291
292 if (!EVP_PKEY_set1_RSA(pkey.get(), rsa.get()))
293 return crypto::ScopedEVP_PKEY();
294
295 return pkey.Pass();
296 }
297
298 crypto::ScopedEVP_PKEY CreateECDSAWrapper(SecKeyRef key) {
299 // TODO(davidben): Implement ECDSA after BoringSSL transition. Most
300 // of the signing implementation can be shared with RsaMethodPrivEnc
301 // and pulled into a common function.
302 NOTIMPLEMENTED();
303 return crypto::ScopedEVP_PKEY();
304 }
305
306 crypto::ScopedEVP_PKEY CreatePkeyWrapper(SecKeyRef key) {
307 const CSSM_KEY* cssm_key;
308 OSStatus status = SecKeyGetCSSMKey(key, &cssm_key);
309 if (status != noErr)
310 return crypto::ScopedEVP_PKEY();
311
312 switch (cssm_key->KeyHeader.AlgorithmId) {
313 case CSSM_ALGID_RSA:
314 return CreateRSAWrapper(key, cssm_key);
315 case CSSM_ALGID_ECDSA:
316 return CreateECDSAWrapper(key);
317 default:
318 // TODO(davidben): Filter out anything other than ECDSA and RSA
319 // elsewhere. We don't support other key types.
320 NOTREACHED();
321 LOG(ERROR) << "Unknown key type";
322 return crypto::ScopedEVP_PKEY();
323 }
324 }
325
326 } // namespace
327
328 // Default missing implementation.
wtc 2014/07/16 00:09:58 What does "missing implementation" mean?
davidben 2014/07/16 16:25:35 Oops, removed.
329 crypto::ScopedEVP_PKEY FetchClientCertPrivateKey(
330 const X509Certificate* certificate) {
331 // Look up the private key.
332 base::ScopedCFTypeRef<SecKeyRef> private_key(
333 FetchSecKeyRefForCertificate(certificate));
334 if (!private_key)
335 return crypto::ScopedEVP_PKEY();
336
337 // Create an EVP_PKEY wrapper.
338 return CreatePkeyWrapper(private_key.get());
339 }
340
341 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698