diff options
author | Ben Murdoch <benm@google.com> | 2010-07-29 17:14:53 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2010-08-04 14:29:45 +0100 |
commit | c407dc5cd9bdc5668497f21b26b09d988ab439de (patch) | |
tree | 7eaf8707c0309516bdb042ad976feedaf72b0bb1 /net/base/keygen_handler_mac.cc | |
parent | 0998b1cdac5733f299c12d88bc31ef9c8035b8fa (diff) | |
download | external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.zip external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.tar.gz external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.tar.bz2 |
Merge Chromium src@r53293
Change-Id: Ia79acf8670f385cee48c45b0a75371d8e950af34
Diffstat (limited to 'net/base/keygen_handler_mac.cc')
-rw-r--r-- | net/base/keygen_handler_mac.cc | 292 |
1 files changed, 283 insertions, 9 deletions
diff --git a/net/base/keygen_handler_mac.cc b/net/base/keygen_handler_mac.cc index f6b4551..c2c63d7 100644 --- a/net/base/keygen_handler_mac.cc +++ b/net/base/keygen_handler_mac.cc @@ -1,23 +1,297 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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/crypto/cssm_init.h" +#include "base/lock.h" #include "base/logging.h" +#include "base/scoped_cftyperef.h" + +// These are in Security.framework but not declared in a public header. +extern const SecAsn1Template kSecAsn1AlgorithmIDTemplate[]; +extern const SecAsn1Template kSecAsn1SubjectPublicKeyInfoTemplate[]; namespace net { -KeygenHandler::KeygenHandler(int key_size_index, - const std::string& challenge) - : key_size_index_(key_size_index), - challenge_(challenge) { - NOTIMPLEMENTED(); -} +// 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, + SecKeyRef* out_pub_key, + SecKeyRef* out_priv_key); +static OSStatus SignData(CSSM_DATA data, + SecKeyRef private_key, + CSSM_DATA* signature); std::string KeygenHandler::GenKeyAndSignChallenge() { - NOTIMPLEMENTED(); - return std::string(); + std::string result; + OSStatus err; + SecKeyRef public_key = NULL; + SecKeyRef private_key = NULL; + SecAsn1CoderRef coder = NULL; + CSSM_DATA signature = {0, NULL}; + + { + // Create the key-pair. + err = CreateRSAKeyPair(key_size_in_bits_, &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) { + base::LogCSSMError("SecKeychainItemExpor", err); + goto failure; + } + scoped_cftyperef<CFDataRef> scoped_key_data(key_data); + + // Create an ASN.1 encoder. + err = SecAsn1CoderCreate(&coder); + if (err) { + base::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) { + base::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) { + base::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) { + LOG(ERROR) << "SSL Keygen failed! OSStatus = " << err; + } else { + LOG(INFO) << "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 (public_key) + CFRelease(public_key); + if (private_key) + CFRelease(private_key); + return result; +} + + +static OSStatus CreateRSAKeyPair(int size_in_bits, + SecKeyRef* out_pub_key, + SecKeyRef* out_priv_key) { + OSStatus err; + SecKeychainRef keychain; + err = SecKeychainCopyDefault(&keychain); + if (err) { + base::LogCSSMError("SecKeychainCopyDefault", err); + return err; + } + scoped_cftyperef<SecKeychainRef> scoped_keychain(keychain); + { + AutoLock locked(base::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, + NULL, + out_pub_key, out_priv_key); + } + if (err) + base::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; + { + AutoLock locked(base::GetMacSecurityServicesLock()); + err = SecKeyGetCredentials(key, + CSSM_ACL_AUTHORIZATION_SIGN, + kSecCredentialTypeDefault, + &credentials); + } + if (err) { + base::LogCSSMError("SecKeyGetCredentials", err); + return err; + } + + CSSM_CSP_HANDLE csp_handle = 0; + { + AutoLock locked(base::GetMacSecurityServicesLock()); + err = SecKeyGetCSPHandle(key, &csp_handle); + } + if (err) { + base::LogCSSMError("SecKeyGetCSPHandle", err); + return err; + } + + const CSSM_KEY* cssm_key = NULL; + { + AutoLock locked(base::GetMacSecurityServicesLock()); + err = SecKeyGetCSSMKey(key, &cssm_key); + } + if (err) { + base::LogCSSMError("SecKeyGetCSSMKey", err); + return err; + } + + err = CSSM_CSP_CreateSignatureContext(csp_handle, + algorithm, + credentials, + cssm_key, + out_cc_handle); + if (err) + base::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) { + base::LogCSSMError("CreateSignatureContext", err); + return err; + } + err = CSSM_SignData(cc_handle, &data, 1, CSSM_ALGID_NONE, signature); + if (err) + base::LogCSSMError("CSSM_SignData", err); + CSSM_DeleteContext(cc_handle); + return err; } } // namespace net |