Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / net / ssl / openssl_platform_key_mac.cc
blob34857afda35f85c578e2ec31dbe9b6137941b7a5
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"
32 namespace net {
34 namespace {
36 class ScopedCSSM_CC_HANDLE {
37 public:
38 ScopedCSSM_CC_HANDLE() : handle_(0) {
41 ~ScopedCSSM_CC_HANDLE() {
42 reset();
45 CSSM_CC_HANDLE get() const {
46 return handle_;
49 void reset() {
50 if (handle_)
51 CSSM_DeleteContext(handle_);
52 handle_ = 0;
55 CSSM_CC_HANDLE* InitializeInto() {
56 reset();
57 return &handle_;
59 private:
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
67 // result.
68 SecKeyRef FetchSecKeyRefForCertificate(const X509Certificate* certificate) {
69 OSStatus status;
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);
78 return NULL;
81 base::ScopedCFTypeRef<SecKeyRef> private_key;
82 status = SecIdentityCopyPrivateKey(identity, private_key.InitializeInto());
83 if (status != noErr) {
84 OSSTATUS_LOG(WARNING, status);
85 return NULL;
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.
96 struct KeyExData {
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,
108 void** from_d,
109 int idx,
110 long argl,
111 void* argp) {
112 CHECK_EQ((void*)NULL, *from_d);
113 return 0;
116 // ExDataFree is called when one of the RSA or EC_KEY objects is freed.
117 void ExDataFree(void* parent,
118 void* ptr,
119 CRYPTO_EX_DATA* ex_data,
120 int idx,
121 long argl, void* argp) {
122 KeyExData* data = reinterpret_cast<KeyExData*>(ptr);
123 delete data;
126 // BoringSSLEngine is a BoringSSL ENGINE that implements RSA and ECDSA
127 // by forwarding the requested operations to Apple's CSSM
128 // implementation.
129 class BoringSSLEngine {
130 public:
131 BoringSSLEngine()
132 : rsa_index_(RSA_get_ex_new_index(0 /* argl */,
133 NULL /* argp */,
134 NULL /* new_func */,
135 ExDataDup,
136 ExDataFree)),
137 ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */,
138 NULL /* argp */,
139 NULL /* new_func */,
140 ExDataDup,
141 ExDataFree)),
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_; }
154 private:
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,
170 size_t* out_len,
171 uint8_t* out,
172 size_t max_out,
173 const uint8_t* in,
174 size_t in_len) {
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);
180 return 0;
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;
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;
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;
213 CSSM_DATA hash_data;
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);
224 return 0;
227 *out_len = signature_data.Length;
228 return 1;
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,
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;
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;
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;
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,
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;
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;
303 const RSA_METHOD mac_rsa_method = {
305 0 /* references */,
306 1 /* is_static */
307 } /* common */,
308 NULL /* app_data */,
310 NULL /* init */,
311 NULL /* finish */,
312 RsaMethodSize,
313 NULL /* sign */,
314 NULL /* verify */,
315 RsaMethodEncrypt,
316 RsaMethodSignRaw,
317 RsaMethodDecrypt,
318 RsaMethodVerifyRaw,
319 NULL /* private_transform */,
320 NULL /* mod_exp */,
321 NULL /* bn_mod_exp */,
322 RSA_FLAG_OPAQUE,
323 NULL /* keygen */,
326 crypto::ScopedEVP_PKEY CreateRSAWrapper(SecKeyRef key,
327 const CSSM_KEY* cssm_key) {
328 crypto::ScopedRSA rsa(
329 RSA_new_method(global_boringssl_engine.Get().engine()));
330 if (!rsa)
331 return crypto::ScopedEVP_PKEY();
333 RSA_set_ex_data(
334 rsa.get(), global_boringssl_engine.Get().rsa_ex_index(),
335 new KeyExData(key, cssm_key));
337 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
338 if (!pkey)
339 return crypto::ScopedEVP_PKEY();
341 if (!EVP_PKEY_set1_RSA(pkey.get(), rsa.get()))
342 return crypto::ScopedEVP_PKEY();
344 return pkey.Pass();
347 // Custom ECDSA_METHOD that uses the platform APIs.
348 // Note that for now, only signing through ECDSA_sign() is really supported.
349 // all other method pointers are either stubs returning errors, or no-ops.
351 const KeyExData* EcKeyGetExData(const EC_KEY* ec_key) {
352 return reinterpret_cast<const KeyExData*>(EC_KEY_get_ex_data(
353 ec_key, global_boringssl_engine.Get().ec_key_ex_index()));
356 size_t EcdsaMethodGroupOrderSize(const EC_KEY* ec_key) {
357 const KeyExData* ex_data = EcKeyGetExData(ec_key);
358 // LogicalKeySizeInBits is the size of an EC public key. But an
359 // ECDSA signature length depends on the size of the base point's
360 // order. For P-256, P-384, and P-521, these two sizes are the same.
361 return (ex_data->cssm_key->KeyHeader.LogicalKeySizeInBits + 7) / 8;
364 int EcdsaMethodSign(const uint8_t* digest,
365 size_t digest_len,
366 uint8_t* sig,
367 unsigned int* sig_len,
368 EC_KEY* ec_key) {
369 const KeyExData *ex_data = EcKeyGetExData(ec_key);
370 if (!ex_data) {
371 OPENSSL_PUT_ERROR(RSA, sign_raw, ERR_R_INTERNAL_ERROR);
372 return 0;
374 DCHECK_EQ(CSSM_ALGID_ECDSA, ex_data->cssm_key->KeyHeader.AlgorithmId);
376 // TODO(davidben): Fix BoringSSL to make sig_len a size_t*.
377 size_t out_len;
378 int ret = MakeCSSMSignature(
379 ex_data, &out_len, sig, ECDSA_size(ec_key), digest, digest_len);
380 if (!ret)
381 return 0;
382 *sig_len = out_len;
383 return 1;
386 int EcdsaMethodVerify(const uint8_t* digest,
387 size_t digest_len,
388 const uint8_t* sig,
389 size_t sig_len,
390 EC_KEY* eckey) {
391 NOTIMPLEMENTED();
392 OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ECDSA_R_NOT_IMPLEMENTED);
393 return 0;
396 const ECDSA_METHOD mac_ecdsa_method = {
398 0 /* references */,
399 1 /* is_static */
400 } /* common */,
401 NULL /* app_data */,
403 NULL /* init */,
404 NULL /* finish */,
405 EcdsaMethodGroupOrderSize,
406 EcdsaMethodSign,
407 EcdsaMethodVerify,
408 ECDSA_FLAG_OPAQUE,
411 crypto::ScopedEVP_PKEY CreateECDSAWrapper(SecKeyRef key,
412 const CSSM_KEY* cssm_key) {
413 crypto::ScopedEC_KEY ec_key(
414 EC_KEY_new_method(global_boringssl_engine.Get().engine()));
415 if (!ec_key)
416 return crypto::ScopedEVP_PKEY();
418 EC_KEY_set_ex_data(
419 ec_key.get(), global_boringssl_engine.Get().ec_key_ex_index(),
420 new KeyExData(key, cssm_key));
422 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
423 if (!pkey)
424 return crypto::ScopedEVP_PKEY();
426 if (!EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()))
427 return crypto::ScopedEVP_PKEY();
429 return pkey.Pass();
432 crypto::ScopedEVP_PKEY CreatePkeyWrapper(SecKeyRef key) {
433 const CSSM_KEY* cssm_key;
434 OSStatus status = SecKeyGetCSSMKey(key, &cssm_key);
435 if (status != noErr)
436 return crypto::ScopedEVP_PKEY();
438 switch (cssm_key->KeyHeader.AlgorithmId) {
439 case CSSM_ALGID_RSA:
440 return CreateRSAWrapper(key, cssm_key);
441 case CSSM_ALGID_ECDSA:
442 return CreateECDSAWrapper(key, cssm_key);
443 default:
444 // TODO(davidben): Filter out anything other than ECDSA and RSA
445 // elsewhere. We don't support other key types.
446 NOTREACHED();
447 LOG(ERROR) << "Unknown key type";
448 return crypto::ScopedEVP_PKEY();
452 } // namespace
454 crypto::ScopedEVP_PKEY FetchClientCertPrivateKey(
455 const X509Certificate* certificate) {
456 // Look up the private key.
457 base::ScopedCFTypeRef<SecKeyRef> private_key(
458 FetchSecKeyRefForCertificate(certificate));
459 if (!private_key)
460 return crypto::ScopedEVP_PKEY();
462 // Create an EVP_PKEY wrapper.
463 return CreatePkeyWrapper(private_key.get());
466 } // namespace net