summaryrefslogtreecommitdiff
path: root/chromium/net/base/keygen_handler_mac.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/base/keygen_handler_mac.cc')
-rw-r--r--chromium/net/base/keygen_handler_mac.cc325
1 files changed, 325 insertions, 0 deletions
diff --git a/chromium/net/base/keygen_handler_mac.cc b/chromium/net/base/keygen_handler_mac.cc
new file mode 100644
index 00000000000..63ea84751aa
--- /dev/null
+++ b/chromium/net/base/keygen_handler_mac.cc
@@ -0,0 +1,325 @@
+// Copyright (c) 2012 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/base/keygen_handler.h"
+
+#include <Security/SecAsn1Coder.h>
+#include <Security/SecAsn1Templates.h>
+#include <Security/Security.h>
+
+#include "base/base64.h"
+#include "base/logging.h"
+#include "base/mac/mac_logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/synchronization/lock.h"
+#include "crypto/cssm_init.h"
+#include "crypto/mac_security_services_lock.h"
+
+// These are in Security.framework but not declared in a public header.
+extern const SecAsn1Template kSecAsn1AlgorithmIDTemplate[];
+extern const SecAsn1Template kSecAsn1SubjectPublicKeyInfoTemplate[];
+
+namespace net {
+
+// Declarations of Netscape keygen cert structures for ASN.1 encoding:
+
+struct PublicKeyAndChallenge {
+ CSSM_X509_SUBJECT_PUBLIC_KEY_INFO spki;
+ CSSM_DATA challenge_string;
+};
+
+// This is a copy of the built-in kSecAsn1IA5StringTemplate, but without the
+// 'streamable' flag, which was causing bogus data to be written.
+const SecAsn1Template kIA5StringTemplate[] = {
+ { SEC_ASN1_IA5_STRING, 0, NULL, sizeof(CSSM_DATA) }
+};
+
+static const SecAsn1Template kPublicKeyAndChallengeTemplate[] = {
+ {
+ SEC_ASN1_SEQUENCE,
+ 0,
+ NULL,
+ sizeof(PublicKeyAndChallenge)
+ },
+ {
+ SEC_ASN1_INLINE,
+ offsetof(PublicKeyAndChallenge, spki),
+ kSecAsn1SubjectPublicKeyInfoTemplate
+ },
+ {
+ SEC_ASN1_INLINE,
+ offsetof(PublicKeyAndChallenge, challenge_string),
+ kIA5StringTemplate
+ },
+ {
+ 0
+ }
+};
+
+struct SignedPublicKeyAndChallenge {
+ PublicKeyAndChallenge pkac;
+ CSSM_X509_ALGORITHM_IDENTIFIER signature_algorithm;
+ CSSM_DATA signature;
+};
+
+static const SecAsn1Template kSignedPublicKeyAndChallengeTemplate[] = {
+ {
+ SEC_ASN1_SEQUENCE,
+ 0,
+ NULL,
+ sizeof(SignedPublicKeyAndChallenge)
+ },
+ {
+ SEC_ASN1_INLINE,
+ offsetof(SignedPublicKeyAndChallenge, pkac),
+ kPublicKeyAndChallengeTemplate
+ },
+ {
+ SEC_ASN1_INLINE,
+ offsetof(SignedPublicKeyAndChallenge, signature_algorithm),
+ kSecAsn1AlgorithmIDTemplate
+ },
+ {
+ SEC_ASN1_BIT_STRING,
+ offsetof(SignedPublicKeyAndChallenge, signature)
+ },
+ {
+ 0
+ }
+};
+
+
+static OSStatus CreateRSAKeyPair(int size_in_bits,
+ SecAccessRef initial_access,
+ SecKeyRef* out_pub_key,
+ SecKeyRef* out_priv_key);
+static OSStatus SignData(CSSM_DATA data,
+ SecKeyRef private_key,
+ CSSM_DATA* signature);
+
+std::string KeygenHandler::GenKeyAndSignChallenge() {
+ std::string result;
+ OSStatus err;
+ SecAccessRef initial_access = NULL;
+ SecKeyRef public_key = NULL;
+ SecKeyRef private_key = NULL;
+ SecAsn1CoderRef coder = NULL;
+ CSSM_DATA signature = {0, NULL};
+
+ {
+ if (url_.has_host()) {
+ // TODO(davidben): Use something like "Key generated for
+ // example.com", but localize it.
+ base::ScopedCFTypeRef<CFStringRef> label(
+ base::SysUTF8ToCFStringRef(url_.host()));
+ // Create an initial access object to set the SecAccessRef. This
+ // sets a label on the Keychain dialogs. Pass NULL as the second
+ // argument to use the default trusted list; only allow the
+ // current application to access without user confirmation.
+ err = SecAccessCreate(label, NULL, &initial_access);
+ // If we fail, just continue without a label.
+ if (err)
+ crypto::LogCSSMError("SecAccessCreate", err);
+ }
+
+ // Create the key-pair.
+ err = CreateRSAKeyPair(key_size_in_bits_, initial_access,
+ &public_key, &private_key);
+ if (err)
+ goto failure;
+
+ // Get the public key data (DER sequence of modulus, exponent).
+ CFDataRef key_data = NULL;
+ err = SecKeychainItemExport(public_key, kSecFormatBSAFE, 0, NULL,
+ &key_data);
+ if (err) {
+ crypto::LogCSSMError("SecKeychainItemExpor", err);
+ goto failure;
+ }
+ base::ScopedCFTypeRef<CFDataRef> scoped_key_data(key_data);
+
+ // Create an ASN.1 encoder.
+ err = SecAsn1CoderCreate(&coder);
+ if (err) {
+ crypto::LogCSSMError("SecAsn1CoderCreate", err);
+ goto failure;
+ }
+
+ // Fill in and DER-encode the PublicKeyAndChallenge:
+ SignedPublicKeyAndChallenge spkac;
+ memset(&spkac, 0, sizeof(spkac));
+ spkac.pkac.spki.algorithm.algorithm = CSSMOID_RSA;
+ spkac.pkac.spki.subjectPublicKey.Length =
+ CFDataGetLength(key_data) * 8; // interpreted as a _bit_ count
+ spkac.pkac.spki.subjectPublicKey.Data =
+ const_cast<uint8_t*>(CFDataGetBytePtr(key_data));
+ spkac.pkac.challenge_string.Length = challenge_.length();
+ spkac.pkac.challenge_string.Data =
+ reinterpret_cast<uint8_t*>(const_cast<char*>(challenge_.data()));
+
+ CSSM_DATA encoded;
+ err = SecAsn1EncodeItem(coder, &spkac.pkac,
+ kPublicKeyAndChallengeTemplate, &encoded);
+ if (err) {
+ crypto::LogCSSMError("SecAsn1EncodeItem", err);
+ goto failure;
+ }
+
+ // Compute a signature of the result:
+ err = SignData(encoded, private_key, &signature);
+ if (err)
+ goto failure;
+ spkac.signature.Data = signature.Data;
+ spkac.signature.Length = signature.Length * 8; // a _bit_ count
+ spkac.signature_algorithm.algorithm = CSSMOID_MD5WithRSA;
+ // TODO(snej): MD5 is weak. Can we use SHA1 instead?
+ // See <https://bugzilla.mozilla.org/show_bug.cgi?id=549460>
+
+ // DER-encode the entire SignedPublicKeyAndChallenge:
+ err = SecAsn1EncodeItem(coder, &spkac,
+ kSignedPublicKeyAndChallengeTemplate, &encoded);
+ if (err) {
+ crypto::LogCSSMError("SecAsn1EncodeItem", err);
+ goto failure;
+ }
+
+ // Base64 encode the result.
+ std::string input(reinterpret_cast<char*>(encoded.Data), encoded.Length);
+ base::Base64Encode(input, &result);
+ }
+
+ failure:
+ if (err)
+ OSSTATUS_LOG(ERROR, err) << "SSL Keygen failed!";
+ else
+ VLOG(1) << "SSL Keygen succeeded! Output is: " << result;
+
+ // Remove keys from keychain if asked to during unit testing:
+ if (!stores_key_) {
+ if (public_key)
+ SecKeychainItemDelete(reinterpret_cast<SecKeychainItemRef>(public_key));
+ if (private_key)
+ SecKeychainItemDelete(reinterpret_cast<SecKeychainItemRef>(private_key));
+ }
+
+ // Clean up:
+ free(signature.Data);
+ if (coder)
+ SecAsn1CoderRelease(coder);
+ if (initial_access)
+ CFRelease(initial_access);
+ if (public_key)
+ CFRelease(public_key);
+ if (private_key)
+ CFRelease(private_key);
+ return result;
+}
+
+
+// Create an RSA key pair with size |size_in_bits|. |initial_access|
+// is passed as the initial access control list in Keychain. The
+// public and private keys are placed in |out_pub_key| and
+// |out_priv_key|, respectively.
+static OSStatus CreateRSAKeyPair(int size_in_bits,
+ SecAccessRef initial_access,
+ SecKeyRef* out_pub_key,
+ SecKeyRef* out_priv_key) {
+ OSStatus err;
+ SecKeychainRef keychain;
+ err = SecKeychainCopyDefault(&keychain);
+ if (err) {
+ crypto::LogCSSMError("SecKeychainCopyDefault", err);
+ return err;
+ }
+ base::ScopedCFTypeRef<SecKeychainRef> scoped_keychain(keychain);
+ {
+ base::AutoLock locked(crypto::GetMacSecurityServicesLock());
+ err = SecKeyCreatePair(
+ keychain,
+ CSSM_ALGID_RSA,
+ size_in_bits,
+ 0LL,
+ // public key usage and attributes:
+ CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP,
+ CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT,
+ // private key usage and attributes:
+ CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_SIGN | CSSM_KEYUSE_UNWRAP,
+ CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT |
+ CSSM_KEYATTR_SENSITIVE,
+ initial_access,
+ out_pub_key, out_priv_key);
+ }
+ if (err)
+ crypto::LogCSSMError("SecKeyCreatePair", err);
+ return err;
+}
+
+static OSStatus CreateSignatureContext(SecKeyRef key,
+ CSSM_ALGORITHMS algorithm,
+ CSSM_CC_HANDLE* out_cc_handle) {
+ OSStatus err;
+ const CSSM_ACCESS_CREDENTIALS* credentials = NULL;
+ {
+ base::AutoLock locked(crypto::GetMacSecurityServicesLock());
+ err = SecKeyGetCredentials(key,
+ CSSM_ACL_AUTHORIZATION_SIGN,
+ kSecCredentialTypeDefault,
+ &credentials);
+ }
+ if (err) {
+ crypto::LogCSSMError("SecKeyGetCredentials", err);
+ return err;
+ }
+
+ CSSM_CSP_HANDLE csp_handle = 0;
+ {
+ base::AutoLock locked(crypto::GetMacSecurityServicesLock());
+ err = SecKeyGetCSPHandle(key, &csp_handle);
+ }
+ if (err) {
+ crypto::LogCSSMError("SecKeyGetCSPHandle", err);
+ return err;
+ }
+
+ const CSSM_KEY* cssm_key = NULL;
+ {
+ base::AutoLock locked(crypto::GetMacSecurityServicesLock());
+ err = SecKeyGetCSSMKey(key, &cssm_key);
+ }
+ if (err) {
+ crypto::LogCSSMError("SecKeyGetCSSMKey", err);
+ return err;
+ }
+
+ err = CSSM_CSP_CreateSignatureContext(csp_handle,
+ algorithm,
+ credentials,
+ cssm_key,
+ out_cc_handle);
+ if (err)
+ crypto::LogCSSMError("CSSM_CSP_CreateSignatureContext", err);
+ return err;
+}
+
+static OSStatus SignData(CSSM_DATA data,
+ SecKeyRef private_key,
+ CSSM_DATA* signature) {
+ CSSM_CC_HANDLE cc_handle;
+ OSStatus err = CreateSignatureContext(private_key,
+ CSSM_ALGID_MD5WithRSA,
+ &cc_handle);
+ if (err) {
+ crypto::LogCSSMError("CreateSignatureContext", err);
+ return err;
+ }
+ err = CSSM_SignData(cc_handle, &data, 1, CSSM_ALGID_NONE, signature);
+ if (err)
+ crypto::LogCSSMError("CSSM_SignData", err);
+ CSSM_DeleteContext(cc_handle);
+ return err;
+}
+
+} // namespace net