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

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: Mark keys as opaque. 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_policy.h"
23 #include "base/memory/scoped_ptr.h"
24 #include "base/synchronization/lock.h"
25 #include "crypto/mac_security_services_lock.h"
26 #include "net/base/net_errors.h"
27 #include "net/cert/x509_certificate.h"
28 #include "net/ssl/openssl_ssl_util.h"
29
30 namespace net {
31
32 namespace {
33
34 class ScopedCSSM_CC_HANDLE {
35 public:
36 ScopedCSSM_CC_HANDLE() : handle_(0) {
37 }
38
39 ~ScopedCSSM_CC_HANDLE() {
40 reset();
41 }
42
43 CSSM_CC_HANDLE get() const {
44 return handle_;
45 }
46
47 void reset() {
48 if (handle_)
49 CSSM_DeleteContext(handle_);
50 handle_ = 0;
51 }
52
53 CSSM_CC_HANDLE* InitializeInto() {
54 reset();
55 return &handle_;
56 }
57 private:
58 CSSM_CC_HANDLE handle_;
Ryan Sleevi 2014/07/23 22:38:47 DISALLOW_COPY_AND_ASSIGN
davidben 2014/07/23 23:13:09 Done.
59 };
60
61 // Looks up the private key for |certificate| in KeyChain and returns
62 // a SecKeyRef or NULL on failure. The caller takes ownership of the
63 // result.
64 SecKeyRef FetchSecKeyRefForCertificate(const X509Certificate* certificate) {
65 OSStatus status;
66 base::ScopedCFTypeRef<SecIdentityRef> identity;
67 {
68 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
69 status = SecIdentityCreateWithCertificate(
70 NULL, certificate->os_cert_handle(), identity.InitializeInto());
71 }
72 if (status != noErr) {
73 OSSTATUS_LOG(WARNING, status);
74 return NULL;
75 }
76
77 base::ScopedCFTypeRef<SecKeyRef> private_key;
78 status = SecIdentityCopyPrivateKey(identity, private_key.InitializeInto());
79 if (status != noErr) {
80 OSSTATUS_LOG(WARNING, status);
81 return NULL;
82 }
83
84 return private_key.release();
85 }
86
87 extern const RSA_METHOD mac_rsa_method;
88 extern const ECDSA_METHOD mac_ecdsa_method;
89
90 // KeyExData contains the data that is contained in the EX_DATA of the
91 // RSA and ECDSA objects that are created to wrap Mac system keys.
92 struct KeyExData {
93 KeyExData(SecKeyRef key, const CSSM_KEY* cssm_key)
94 : key(key, base::scoped_policy::RETAIN), cssm_key(cssm_key) {}
95
96 base::ScopedCFTypeRef<SecKeyRef> key;
97 const CSSM_KEY* cssm_key;
98 };
99
100 // ExDataDup is called when one of the RSA or EC_KEY objects is
101 // duplicated. This is not supported and should never happen.
102 int ExDataDup(CRYPTO_EX_DATA* to,
103 const CRYPTO_EX_DATA* from,
104 void** from_d,
105 int idx,
106 long argl,
107 void* argp) {
108 CHECK(false);
Ryan Sleevi 2014/07/23 22:38:47 NOTREACHED/NOTIMPLEMENTED, one of which should be
davidben 2014/07/23 23:13:09 (I just copied this from keystore_openssl.cc.) I
davidben 2014/07/23 23:18:49 (Hrmf, okay, no. EC_KEY_dup is probably not immedi
109 return 0;
110 }
111
112 // ExDataFree is called when one of the RSA or EC_KEY objects is freed.
113 void ExDataFree(void* parent,
114 void* ptr,
115 CRYPTO_EX_DATA* ex_data,
116 int idx,
117 long argl, void* argp) {
118 KeyExData* data = reinterpret_cast<KeyExData*>(ptr);
119 delete data;
120 }
121
122 // BoringSSLEngine is a BoringSSL ENGINE that implements RSA and ECDSA
123 // by forwarding the requested operations to Apple's CSSM
124 // implementation.
125 class BoringSSLEngine {
126 public:
127 BoringSSLEngine()
128 : rsa_index_(RSA_get_ex_new_index(0 /* argl */,
129 NULL /* argp */,
130 NULL /* new_func */,
131 ExDataDup,
132 ExDataFree)),
133 ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */,
134 NULL /* argp */,
135 NULL /* new_func */,
136 ExDataDup,
137 ExDataFree)),
138 engine_(ENGINE_new()) {
139 ENGINE_set_RSA_method(
140 engine_, &mac_rsa_method, sizeof(mac_rsa_method));
141 ENGINE_set_ECDSA_method(
142 engine_, &mac_ecdsa_method, sizeof(mac_ecdsa_method));
143 }
144
145 int rsa_ex_index() const { return rsa_index_; }
146 int ec_key_ex_index() const { return ec_key_index_; }
147
148 const ENGINE* engine() const { return engine_; }
149
150 private:
151 const int rsa_index_;
152 const int ec_key_index_;
153 ENGINE* const engine_;
154 };
155
156 base::LazyInstance<BoringSSLEngine>::Leaky global_boringssl_engine =
157 LAZY_INSTANCE_INITIALIZER;
158
159 // Helper function for making a signature.
160
161 // MakeCSSMSignature uses the key information in |ex_data| to sign the
162 // |in_len| bytes pointed by |in|. It writes up to |max_out| bytes
163 // into the buffer pointed to by |out|, setting |*out_len| to the
164 // number of bytes written. It returns 1 on success and 0 on failure.
165 int MakeCSSMSignature(const KeyExData* ex_data,
166 size_t* out_len,
167 uint8_t* out,
168 size_t max_out,
169 const uint8_t* in,
170 size_t in_len) {
171 CSSM_CSP_HANDLE csp_handle;
172 OSStatus status = SecKeyGetCSPHandle(ex_data->key.get(), &csp_handle);
173 if (status != noErr) {
174 OSSTATUS_LOG(WARNING, status);
175 OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR);
176 return 0;
177 }
178
179 // TODO(davidben): (Taken from TODO(rsleevi) in sslplatf.c) Should
180 // it be kSecCredentialTypeNoUI? In Win32, at least, you can prevent
181 // the UI by setting the provider handle on the certificate to be
182 // opened with CRYPT_SILENT, but is there an equivalent?
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);
189 return 0;
190 }
191
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);
197 return 0;
198 }
199
200 if (ex_data->cssm_key->KeyHeader.AlgorithmId == CSSM_ALGID_RSA) {
201 // Set RSA blinding.
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);
209 return 0;
210 }
211 }
212
213 CSSM_DATA hash_data;
214 hash_data.Length = in_len;
215 hash_data.Data = const_cast<uint8*>(in);
216
217 CSSM_DATA signature_data;
218 signature_data.Length = max_out;
219 signature_data.Data = out;
220
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);
224 return 0;
225 }
226
227 *out_len = signature_data.Length;
228 return 1;
229 }
230
231 // Custom RSA_METHOD that uses the platform APIs for signing.
232
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()));
236 }
237
238 size_t RsaMethodSize(const RSA *rsa) {
239 const KeyExData *ex_data = RsaGetExData(rsa);
240 return (ex_data->cssm_key->KeyHeader.LogicalKeySizeInBits + 7) / 8;
241 }
242
243 int RsaMethodEncrypt(RSA* rsa,
244 size_t* out_len,
245 uint8_t* out,
246 size_t max_out,
247 const uint8_t* in,
248 size_t in_len,
249 int padding) {
250 NOTIMPLEMENTED();
251 OPENSSL_PUT_ERROR(RSA, encrypt, RSA_R_UNKNOWN_ALGORITHM_TYPE);
252 return 0;
253 }
254
255 int RsaMethodSignRaw(RSA* rsa,
256 size_t* out_len,
257 uint8_t* out,
258 size_t max_out,
259 const uint8_t* in,
260 size_t in_len,
261 int padding) {
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);
266 return 0;
267 }
268
269 const KeyExData *ex_data = RsaGetExData(rsa);
270 if (!ex_data) {
271 OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR);
272 return 0;
273 }
274 DCHECK_EQ(CSSM_ALGID_RSA, ex_data->cssm_key->KeyHeader.AlgorithmId);
275
276 return MakeCSSMSignature(ex_data, out_len, out, max_out, in, in_len);
277 }
278
279 int RsaMethodDecrypt(RSA* rsa,
280 size_t* out_len,
281 uint8_t* out,
282 size_t max_out,
283 const uint8_t* in,
284 size_t in_len,
285 int padding) {
286 NOTIMPLEMENTED();
287 OPENSSL_PUT_ERROR(RSA, decrypt, RSA_R_UNKNOWN_ALGORITHM_TYPE);
288 return 0;
289 }
290
291 int RsaMethodVerifyRaw(RSA* rsa,
292 size_t* out_len,
293 uint8_t* out,
294 size_t max_out,
295 const uint8_t* in,
296 size_t in_len,
297 int padding) {
298 NOTIMPLEMENTED();
299 OPENSSL_PUT_ERROR(RSA, verify_raw, RSA_R_UNKNOWN_ALGORITHM_TYPE);
300 return 0;
301 }
302
303 const RSA_METHOD mac_rsa_method = {
304 {
305 0 /* references */,
306 1 /* is_static */
307 } /* common */,
308 NULL /* app_data */,
309
310 NULL /* init */,
311 NULL /* finish */,
312 RsaMethodSize,
313 NULL /* sign */,
314 NULL /* verify */,
315 RsaMethodEncrypt,
316 RsaMethodSignRaw,
317 RsaMethodDecrypt,
318 RsaMethodVerifyRaw,
319 NULL /* mod_exp */,
320 NULL /* bn_mod_exp */,
321 RSA_FLAG_OPAQUE,
322 NULL /* keygen */,
323 };
324
325 crypto::ScopedEVP_PKEY CreateRSAWrapper(SecKeyRef key,
326 const CSSM_KEY* cssm_key) {
327 crypto::ScopedRSA rsa(
328 RSA_new_method(global_boringssl_engine.Get().engine()));
329 if (!rsa)
330 return crypto::ScopedEVP_PKEY();
331
332 RSA_set_ex_data(
333 rsa.get(), global_boringssl_engine.Get().rsa_ex_index(),
334 new KeyExData(key, cssm_key));
335
336 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
337 if (!pkey)
338 return crypto::ScopedEVP_PKEY();
339
340 if (!EVP_PKEY_set1_RSA(pkey.get(), rsa.get()))
341 return crypto::ScopedEVP_PKEY();
342
343 return pkey.Pass();
344 }
345
346 // Custom ECDSA_METHOD that uses the platform APIs.
347 // Note that for now, only signing through ECDSA_sign() is really supported.
348 // all other method pointers are either stubs returning errors, or no-ops.
349
350 const KeyExData* EcKeyGetExData(const EC_KEY* ec_key) {
351 return reinterpret_cast<const KeyExData*>(EC_KEY_get_ex_data(
352 ec_key, global_boringssl_engine.Get().ec_key_ex_index()));
353 }
354
355 size_t EcdsaMethodGroupOrderSize(const EC_KEY* ec_key) {
356 const KeyExData* ex_data = EcKeyGetExData(ec_key);
357 // LogicalKeySizeInBits is the size of an EC public key. But an
358 // ECDSA signature length depends on the size of the base point's
359 // order. For P-256, P-384, and P-521, these two sizes are the same.
360 return (ex_data->cssm_key->KeyHeader.LogicalKeySizeInBits + 7) / 8;
361 }
362
363 int EcdsaMethodSign(const uint8_t* digest,
364 size_t digest_len,
365 uint8_t* sig,
366 unsigned int* sig_len,
367 EC_KEY* ec_key) {
368 const KeyExData *ex_data = EcKeyGetExData(ec_key);
369 if (!ex_data) {
370 OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR);
371 return 0;
372 }
373 DCHECK_EQ(CSSM_ALGID_ECDSA, ex_data->cssm_key->KeyHeader.AlgorithmId);
374
375 // TODO(davidben): Fix BoringSSL to make sig_len a size_t*.
376 size_t out_len;
377 int ret = MakeCSSMSignature(
378 ex_data, &out_len, sig, ECDSA_size(ec_key), digest, digest_len);
379 if (!ret)
380 return 0;
381 *sig_len = out_len;
382 return 1;
383 }
384
385 int EcdsaMethodVerify(const uint8_t* digest,
386 size_t digest_len,
387 const uint8_t* sig,
388 size_t sig_len,
389 EC_KEY* eckey) {
390 NOTIMPLEMENTED();
391 OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ECDSA_R_NOT_IMPLEMENTED);
392 return 0;
393 }
394
395 const ECDSA_METHOD mac_ecdsa_method = {
396 {
397 0 /* references */,
398 1 /* is_static */
399 } /* common */,
400 NULL /* app_data */,
401
402 NULL /* init */,
403 NULL /* finish */,
404 EcdsaMethodGroupOrderSize,
405 EcdsaMethodSign,
406 EcdsaMethodVerify,
407 ECDSA_FLAG_OPAQUE,
408 };
409
410 crypto::ScopedEVP_PKEY CreateECDSAWrapper(SecKeyRef key,
411 const CSSM_KEY* cssm_key) {
412 crypto::ScopedEC_KEY ec_key(
413 EC_KEY_new_method(global_boringssl_engine.Get().engine()));
414 if (!ec_key)
415 return crypto::ScopedEVP_PKEY();
416
417 EC_KEY_set_ex_data(
418 ec_key.get(), global_boringssl_engine.Get().ec_key_ex_index(),
419 new KeyExData(key, cssm_key));
420
421 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
422 if (!pkey)
423 return crypto::ScopedEVP_PKEY();
424
425 if (!EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()))
426 return crypto::ScopedEVP_PKEY();
427
428 return pkey.Pass();
429 }
430
431 crypto::ScopedEVP_PKEY CreatePkeyWrapper(SecKeyRef key) {
432 const CSSM_KEY* cssm_key;
433 OSStatus status = SecKeyGetCSSMKey(key, &cssm_key);
434 if (status != noErr)
435 return crypto::ScopedEVP_PKEY();
436
437 switch (cssm_key->KeyHeader.AlgorithmId) {
438 case CSSM_ALGID_RSA:
439 return CreateRSAWrapper(key, cssm_key);
440 case CSSM_ALGID_ECDSA:
441 return CreateECDSAWrapper(key, cssm_key);
442 default:
443 // TODO(davidben): Filter out anything other than ECDSA and RSA
444 // elsewhere. We don't support other key types.
445 NOTREACHED();
446 LOG(ERROR) << "Unknown key type";
447 return crypto::ScopedEVP_PKEY();
448 }
449 }
450
451 } // namespace
452
453 crypto::ScopedEVP_PKEY FetchClientCertPrivateKey(
454 const X509Certificate* certificate) {
455 // Look up the private key.
456 base::ScopedCFTypeRef<SecKeyRef> private_key(
457 FetchSecKeyRefForCertificate(certificate));
458 if (!private_key)
459 return crypto::ScopedEVP_PKEY();
460
461 // Create an EVP_PKEY wrapper.
462 return CreatePkeyWrapper(private_key.get());
463 }
464
465 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698