diff options
author | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-09 05:08:51 +0000 |
---|---|---|
committer | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-09 05:08:51 +0000 |
commit | eaa60485f027b00047a2e142d9616ede4333a46b (patch) | |
tree | eb20edb20255f8f76d76f3ff4f1399a04a6d9398 /crypto/ec_private_key_nss.cc | |
parent | e80f64a0f8ed441443bec21eeb5c732398d11377 (diff) | |
download | chromium_src-eaa60485f027b00047a2e142d9616ede4333a46b.zip chromium_src-eaa60485f027b00047a2e142d9616ede4333a46b.tar.gz chromium_src-eaa60485f027b00047a2e142d9616ede4333a46b.tar.bz2 |
Add ECPrivateKey for Elliptic Curve keypair generation.
The implementation uses NSS on all platforms unless USE_OPENSSL is defined
(which is only stubbed out in this CL).
BUG=88782
TEST=ECPrivateKeyUnitTest
Review URL: http://codereview.chromium.org/8413024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@109188 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'crypto/ec_private_key_nss.cc')
-rw-r--r-- | crypto/ec_private_key_nss.cc | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/crypto/ec_private_key_nss.cc b/crypto/ec_private_key_nss.cc new file mode 100644 index 0000000..cc46101 --- /dev/null +++ b/crypto/ec_private_key_nss.cc @@ -0,0 +1,300 @@ +// Copyright (c) 2011 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 "crypto/ec_private_key.h" + +extern "C" { +// Work around NSS missing SEC_BEGIN_PROTOS in secmodt.h. This must come before +// other NSS headers. +#include <secmodt.h> +} + +#include <cryptohi.h> +#include <keyhi.h> +#include <pk11pub.h> +#include <secmod.h> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "crypto/nss_util.h" +#include "crypto/nss_util_internal.h" +#include "crypto/scoped_nss_types.h" +#include "crypto/third_party/nss/chromium-nss.h" + +namespace { + +// Copied from rsa_private_key_nss.cc. +static bool ReadAttribute(SECKEYPrivateKey* key, + CK_ATTRIBUTE_TYPE type, + std::vector<uint8>* output) { + SECItem item; + SECStatus rv; + rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, type, &item); + if (rv != SECSuccess) { + DLOG(ERROR) << "PK11_ReadRawAttribute: " << PORT_GetError(); + return false; + } + + output->assign(item.data, item.data + item.len); + SECITEM_FreeItem(&item, PR_FALSE); + return true; +} + +} // namespace + +namespace crypto { + +ECPrivateKey::~ECPrivateKey() { + if (key_) + SECKEY_DestroyPrivateKey(key_); + if (public_key_) + SECKEY_DestroyPublicKey(public_key_); +} + +// static +ECPrivateKey* ECPrivateKey::Create() { + return CreateWithParams(PR_FALSE /* not permanent */, + PR_FALSE /* not sensitive */); +} + +// static +ECPrivateKey* ECPrivateKey::CreateSensitive() { +#if defined(USE_NSS) + return CreateWithParams(PR_TRUE /* permanent */, + PR_TRUE /* sensitive */); +#else + // If USE_NSS is not defined, we initialize NSS with no databases, so we can't + // create permanent keys. + NOTREACHED(); + return NULL; +#endif +} + +// static +ECPrivateKey* ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( + const std::string& password, + const std::vector<uint8>& encrypted_private_key_info, + const std::vector<uint8>& subject_public_key_info) { + return CreateFromEncryptedPrivateKeyInfoWithParams( + password, + encrypted_private_key_info, + subject_public_key_info, + PR_FALSE /* not permanent */, + PR_FALSE /* not sensitive */); +} + +// static +ECPrivateKey* ECPrivateKey::CreateSensitiveFromEncryptedPrivateKeyInfo( + const std::string& password, + const std::vector<uint8>& encrypted_private_key_info, + const std::vector<uint8>& subject_public_key_info) { +#if defined(USE_NSS) + return CreateFromEncryptedPrivateKeyInfoWithParams( + password, + encrypted_private_key_info, + subject_public_key_info, + PR_TRUE /* permanent */, + PR_TRUE /* sensitive */); +#else + // If USE_NSS is not defined, we initialize NSS with no databases, so we can't + // create permanent keys. + NOTREACHED(); + return NULL; +#endif +} + +bool ECPrivateKey::ExportEncryptedPrivateKey( + const std::string& password, + int iterations, + std::vector<uint8>* output) { + // We export as an EncryptedPrivateKeyInfo bundle instead of a plain PKCS #8 + // PrivateKeyInfo because PK11_ImportDERPrivateKeyInfoAndReturnKey doesn't + // support EC keys. + // https://bugzilla.mozilla.org/show_bug.cgi?id=327773 + SECItem password_item = { + siBuffer, + reinterpret_cast<unsigned char*>(const_cast<char*>(password.data())), + password.size() + }; + + SECKEYEncryptedPrivateKeyInfo* encrypted = PK11_ExportEncryptedPrivKeyInfo( + NULL, // Slot, optional. + SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC, + &password_item, + key_, + iterations, + NULL); // wincx. + + if (!encrypted) { + DLOG(ERROR) << "PK11_ExportEncryptedPrivKeyInfo: " << PORT_GetError(); + return false; + } + + ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + SECItem der_key = {siBuffer, NULL, 0}; + SECItem* encoded_item = SEC_ASN1EncodeItem( + arena.get(), + &der_key, + encrypted, + SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate)); + SECKEY_DestroyEncryptedPrivateKeyInfo(encrypted, PR_TRUE); + if (!encoded_item) { + DLOG(ERROR) << "SEC_ASN1EncodeItem: " << PORT_GetError(); + return false; + } + + output->assign(der_key.data, der_key.data + der_key.len); + + return true; +} + +bool ECPrivateKey::ExportPublicKey(std::vector<uint8>* output) { + ScopedSECItem der_pubkey( + SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_)); + if (!der_pubkey.get()) { + return false; + } + + output->assign(der_pubkey->data, der_pubkey->data + der_pubkey->len); + return true; +} + +bool ECPrivateKey::ExportValue(std::vector<uint8>* output) { + return ReadAttribute(key_, CKA_VALUE, output); +} + +bool ECPrivateKey::ExportECParams(std::vector<uint8>* output) { + return ReadAttribute(key_, CKA_EC_PARAMS, output); +} + +ECPrivateKey::ECPrivateKey() : key_(NULL), public_key_(NULL) {} + +// static +ECPrivateKey* ECPrivateKey::CreateWithParams(bool permanent, + bool sensitive) { + EnsureNSSInit(); + + scoped_ptr<ECPrivateKey> result(new ECPrivateKey); + + ScopedPK11Slot slot(GetPrivateNSSKeySlot()); + if (!slot.get()) + return NULL; + + SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); + if (!oid_data) { + DLOG(ERROR) << "SECOID_FindOIDByTag: " << PORT_GetError(); + return NULL; + } + + // SECKEYECParams is a SECItem containing the DER encoded ASN.1 ECParameters + // value. For a named curve, that is just the OBJECT IDENTIFIER of the curve. + // In addition to the oid data, the encoding requires one byte for the ASN.1 + // tag and one byte for the length (assuming the length is <= 127). + DCHECK_LE(oid_data->oid.len, 127U); + std::vector<unsigned char> parameters_buf(2 + oid_data->oid.len); + SECKEYECParams ec_parameters = { + siDEROID, ¶meters_buf[0], parameters_buf.size() + }; + + ec_parameters.data[0] = SEC_ASN1_OBJECT_ID; + ec_parameters.data[1] = oid_data->oid.len; + memcpy(ec_parameters.data + 2, oid_data->oid.data, oid_data->oid.len); + + result->key_ = PK11_GenerateKeyPair(slot.get(), + CKM_EC_KEY_PAIR_GEN, + &ec_parameters, + &result->public_key_, + permanent, + sensitive, + NULL); + if (!result->key_) { + DLOG(ERROR) << "PK11_GenerateKeyPair: " << PORT_GetError(); + return NULL; + } + + return result.release(); +} + +// static +ECPrivateKey* ECPrivateKey::CreateFromEncryptedPrivateKeyInfoWithParams( + const std::string& password, + const std::vector<uint8>& encrypted_private_key_info, + const std::vector<uint8>& subject_public_key_info, + bool permanent, + bool sensitive) { + EnsureNSSInit(); + + scoped_ptr<ECPrivateKey> result(new ECPrivateKey); + + ScopedPK11Slot slot(GetPrivateNSSKeySlot()); + if (!slot.get()) + return NULL; + + SECItem encoded_spki = { + siBuffer, + const_cast<unsigned char*>(&subject_public_key_info[0]), + subject_public_key_info.size() + }; + CERTSubjectPublicKeyInfo* decoded_spki = SECKEY_DecodeDERSubjectPublicKeyInfo( + &encoded_spki); + if (!decoded_spki) { + DLOG(ERROR) << "SECKEY_DecodeDERSubjectPublicKeyInfo: " << PORT_GetError(); + return NULL; + } + + result->public_key_ = SECKEY_ExtractPublicKey(decoded_spki); + + SECKEY_DestroySubjectPublicKeyInfo(decoded_spki); + + if (!result->public_key_) { + DLOG(ERROR) << "SECKEY_ExtractPublicKey: " << PORT_GetError(); + return NULL; + } + + SECItem encoded_epki = { + siBuffer, + const_cast<unsigned char*>(&encrypted_private_key_info[0]), + encrypted_private_key_info.size() + }; + SECKEYEncryptedPrivateKeyInfo epki; + memset(&epki, 0, sizeof(epki)); + + ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + + SECStatus rv = SEC_QuickDERDecodeItem( + arena.get(), + &epki, + SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate), + &encoded_epki); + if (rv != SECSuccess) { + DLOG(ERROR) << "SEC_ASN1DecodeItem: " << PORT_GetError(); + return NULL; + } + + SECItem password_item = { + siBuffer, + reinterpret_cast<unsigned char*>(const_cast<char*>(password.data())), + password.size() + }; + + rv = ImportEncryptedECPrivateKeyInfoAndReturnKey( + slot.get(), + &epki, + &password_item, + NULL, // nickname + &result->public_key_->u.ec.publicValue, + permanent, + sensitive, + &result->key_, + NULL); // wincx + if (rv != SECSuccess) { + DLOG(ERROR) << "ImportEncryptedECPrivateKeyInfoAndReturnKey: " + << PORT_GetError(); + return NULL; + } + + return result.release(); +} + +} // namespace crypto |