diff options
author | thaidn@google.com <thaidn@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-13 01:12:12 +0000 |
---|---|---|
committer | thaidn@google.com <thaidn@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-13 01:12:12 +0000 |
commit | f99a6549ccd1849f5fd2e9bae60ff574fef26e0f (patch) | |
tree | 7ca170908cb356f4ee63997e4da8b1228446d13d /net/quic | |
parent | 84634d071c488f461bf7af07884dd4a952910104 (diff) | |
download | chromium_src-f99a6549ccd1849f5fd2e9bae60ff574fef26e0f.zip chromium_src-f99a6549ccd1849f5fd2e9bae60ff574fef26e0f.tar.gz chromium_src-f99a6549ccd1849f5fd2e9bae60ff574fef26e0f.tar.bz2 |
QUIC: NSS part for P256 key exchange.
Merge internal CL: 42820233.
Review URL: https://chromiumcodereview.appspot.com/12578003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187729 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/quic')
-rw-r--r-- | net/quic/crypto/p256_key_exchange.h | 22 | ||||
-rw-r--r-- | net/quic/crypto/p256_key_exchange_nss.cc | 226 | ||||
-rw-r--r-- | net/quic/crypto/p256_key_exchange_openssl.cc | 35 | ||||
-rw-r--r-- | net/quic/crypto/p256_key_exchange_test.cc | 15 |
4 files changed, 258 insertions, 40 deletions
diff --git a/net/quic/crypto/p256_key_exchange.h b/net/quic/crypto/p256_key_exchange.h index 21e9eff..59439b6 100644 --- a/net/quic/crypto/p256_key_exchange.h +++ b/net/quic/crypto/p256_key_exchange.h @@ -9,6 +9,7 @@ #include "base/memory/scoped_ptr.h" #include "base/string_piece.h" +#include "net/base/net_export.h" #include "net/quic/crypto/key_exchange.h" #if defined(USE_OPENSSL) @@ -25,7 +26,7 @@ namespace net { // P256KeyExchange implements a KeyExchange using elliptic-curve // Diffie-Hellman on NIST P-256. -class P256KeyExchange : public KeyExchange { +class NET_EXPORT_PRIVATE P256KeyExchange : public KeyExchange { public: virtual ~P256KeyExchange(); @@ -57,23 +58,20 @@ class P256KeyExchange : public KeyExchange { }; #if defined(USE_OPENSSL) - // P256KeyExchange takes ownership of |private_key|. - P256KeyExchange(EC_KEY* private_key, uint8* public_key); + // P256KeyExchange takes ownership of |private_key|, and expects + // |public_key| consists of |kUncompressedP256PointBytes| bytes. + P256KeyExchange(EC_KEY* private_key, const uint8* public_key); crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> private_key_; - uint8 public_key_[kUncompressedP256PointBytes]; #else - // Password used by |NewPrivateKey| to encrypt exported EC private keys. - // This is not used to provide any security, but to workaround NSS being - // unable to export unencrypted EC keys. Note that SPDY and ChannelID - // use the same approach. - static const char kExportPassword[]; - - // P256KeyExchange takes ownership of |key_pair|. - explicit P256KeyExchange(crypto::ECPrivateKey* key_pair); + // P256KeyExchange takes ownership of |key_pair|, and expects + // |public_key| consists of |kUncompressedP256PointBytes| bytes. + P256KeyExchange(crypto::ECPrivateKey* key_pair, const uint8* public_key); scoped_ptr<crypto::ECPrivateKey> key_pair_; #endif + // The public key stored as an uncompressed P-256 point. + uint8 public_key_[kUncompressedP256PointBytes]; }; } // namespace net diff --git a/net/quic/crypto/p256_key_exchange_nss.cc b/net/quic/crypto/p256_key_exchange_nss.cc new file mode 100644 index 0000000..eff2028 --- /dev/null +++ b/net/quic/crypto/p256_key_exchange_nss.cc @@ -0,0 +1,226 @@ +// Copyright (c) 2013 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/quic/crypto/p256_key_exchange.h" + +#include "base/logging.h" +#include "base/sys_byteorder.h" + +namespace net { + +namespace { + +// Password used by |NewPrivateKey| to encrypt exported EC private keys. +// This is not used to provide any security, but to workaround NSS being +// unwilling to export unencrypted EC keys. Note that SPDY and ChannelID +// use the same approach. +const char kExportPassword[] = ""; + +// Convert StringPiece to vector of uint8 +static std::vector<uint8> StringPieceToVector(base::StringPiece piece) { + return std::vector<uint8>(piece.data(), piece.data() + piece.length()); +} + +} // namespace + +P256KeyExchange::P256KeyExchange(crypto::ECPrivateKey* key_pair, + const uint8* public_key) + : key_pair_(key_pair) { + memcpy(public_key_, public_key, sizeof(public_key_)); +} + +P256KeyExchange::~P256KeyExchange() { +} + +// static +P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { + if (key.size() < 2) { + DLOG(INFO) << "Key pair is too small"; + return NULL; + } + + const uint8* data = reinterpret_cast<const uint8*>(key.data()); + size_t size = static_cast<size_t>(data[0]) | + (static_cast<size_t>(data[1]) << 8); + key.remove_prefix(2); + if (key.size() < size) { + DLOG(INFO) << "Key pair does not contain key material"; + return NULL; + } + + base::StringPiece private_piece(key.data(), size); + key.remove_prefix(size); + if (key.empty()) { + DLOG(INFO) << "Key pair does not contain public key"; + return NULL; + } + + base::StringPiece public_piece(key); + + scoped_ptr<crypto::ECPrivateKey> key_pair( + crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( + kExportPassword, + // TODO(thaidn): fix this interface to avoid copying secrets. + StringPieceToVector(private_piece), + StringPieceToVector(public_piece))); + + if (!key_pair.get()) { + DLOG(INFO) << "Can't decrypt private key"; + return NULL; + } + + // Perform some sanity checks on the public key. + SECKEYPublicKey* public_key = key_pair->public_key(); + if (public_key->keyType != ecKey || + public_key->u.ec.publicValue.len != kUncompressedP256PointBytes || + !public_key->u.ec.publicValue.data || + public_key->u.ec.publicValue.data[0] != kUncompressedECPointForm) { + DLOG(INFO) << "Key is invalid"; + return NULL; + } + + // Ensure that the key is using the correct curve, i.e., NIST P-256. + const SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); + if (!oid_data) { + DLOG(INFO) << "Can't get P-256's OID"; + return NULL; + } + + if (public_key->u.ec.DEREncodedParams.len != oid_data->oid.len + 2 || + !public_key->u.ec.DEREncodedParams.data || + public_key->u.ec.DEREncodedParams.data[0] != SEC_ASN1_OBJECT_ID || + public_key->u.ec.DEREncodedParams.data[1] != oid_data->oid.len || + memcmp(public_key->u.ec.DEREncodedParams.data + 2, + oid_data->oid.data, oid_data->oid.len) != 0) { + DLOG(INFO) << "Key is invalid"; + } + + return new P256KeyExchange(key_pair.release(), + public_key->u.ec.publicValue.data); +} + +// static +std::string P256KeyExchange::NewPrivateKey() { + scoped_ptr<crypto::ECPrivateKey> key_pair(crypto::ECPrivateKey::Create()); + + if (!key_pair.get()) { + DLOG(INFO) << "Can't generate new key pair"; + return std::string(); + } + + std::vector<uint8> private_key; + if (!key_pair->ExportEncryptedPrivateKey(kExportPassword, + 1 /* iteration */, + &private_key)) { + DLOG(INFO) << "Can't export private key"; + return std::string(); + } + + // NSS lacks the ability to import an ECC private key without + // also importing the public key, so it is necessary to also + // store the public key. + std::vector<uint8> public_key; + if (!key_pair->ExportPublicKey(&public_key)) { + DLOG(INFO) << "Can't export public key"; + return std::string(); + } + + // TODO(thaidn): determine how large encrypted private key can be + uint16 private_key_size = private_key.size(); + const size_t result_size = sizeof(private_key_size) + + private_key_size + + public_key.size(); + std::vector<char> result(result_size); + char* resultp = &result[0]; + // Export the key string. + // The first two bytes are the private key's size in little endian. + private_key_size = base::ByteSwapToLE16(private_key_size); + memcpy(resultp, &private_key_size, sizeof(private_key_size)); + resultp += sizeof(private_key_size); + memcpy(resultp, &private_key[0], private_key.size()); + resultp += private_key.size(); + memcpy(resultp, &public_key[0], public_key.size()); + + return std::string(&result[0], result_size); +} + +bool P256KeyExchange::CalculateSharedKey( + const base::StringPiece& peer_public_value, + std::string* out_result) const { + if (peer_public_value.size() != kUncompressedP256PointBytes || + peer_public_value[0] != kUncompressedECPointForm) { + DLOG(INFO) << "Peer public value is invalid"; + return false; + } + + DCHECK(key_pair_.get()); + DCHECK(key_pair_->public_key()); + + SECKEYPublicKey peer_public_key; + memset(&peer_public_key, 0, sizeof(peer_public_key)); + + peer_public_key.keyType = ecKey; + // Both sides of a ECDH key exchange need to use the same EC params. + peer_public_key.u.ec.DEREncodedParams.len = + key_pair_->public_key()->u.ec.DEREncodedParams.len; + peer_public_key.u.ec.DEREncodedParams.data = + key_pair_->public_key()->u.ec.DEREncodedParams.data; + + peer_public_key.u.ec.publicValue.type = siBuffer; + peer_public_key.u.ec.publicValue.data = + reinterpret_cast<uint8*>(const_cast<char*>(peer_public_value.data())); + peer_public_key.u.ec.publicValue.len = peer_public_value.size(); + + // The NSS function performing ECDH key exchange is PK11_PubDeriveWithKDF. + // As this function is used for SSL/TLS's ECDH key exchanges it has many + // arguments, most of which are not required in QUIC. + // Key derivation function CKD_NULL is used because the return value of + // |CalculateSharedKey| is the actual ECDH shared key, not any derived keys + // from it. + crypto::ScopedPK11SymKey premaster_secret( + PK11_PubDeriveWithKDF( + key_pair_->key(), + &peer_public_key, + PR_FALSE, + NULL, + NULL, + CKM_ECDH1_DERIVE, /* mechanism */ + CKM_GENERIC_SECRET_KEY_GEN, /* target */ + CKA_DERIVE, + 0, + CKD_NULL, /* kdf */ + NULL, + NULL)); + + if (!premaster_secret.get()) { + DLOG(INFO) << "Can't derive ECDH shared key"; + return false; + } + + if (PK11_ExtractKeyValue(premaster_secret.get()) != SECSuccess) { + DLOG(INFO) << "Can't extract raw ECDH shared key"; + return false; + } + + SECItem* key_data = PK11_GetKeyData(premaster_secret.get()); + if (!key_data || !key_data->data || key_data->len != kP256FieldBytes) { + DLOG(INFO) << "ECDH shared key is invalid"; + return false; + } + + out_result->assign(reinterpret_cast<char*>(key_data->data), key_data->len); + return true; +} + +base::StringPiece P256KeyExchange::public_value() const { + return base::StringPiece(reinterpret_cast<const char*>(public_key_), + sizeof(public_key_)); +} + +CryptoTag P256KeyExchange::tag() const { + return kP256; +} + +} // namespace net + diff --git a/net/quic/crypto/p256_key_exchange_openssl.cc b/net/quic/crypto/p256_key_exchange_openssl.cc index 74f68a6..ae7f30e 100644 --- a/net/quic/crypto/p256_key_exchange_openssl.cc +++ b/net/quic/crypto/p256_key_exchange_openssl.cc @@ -12,7 +12,7 @@ namespace net { -P256KeyExchange::P256KeyExchange(EC_KEY* private_key, uint8* public_key) +P256KeyExchange::P256KeyExchange(EC_KEY* private_key, const uint8* public_key) : private_key_(private_key) { memcpy(public_key_, public_key, sizeof(public_key_)); } @@ -23,8 +23,7 @@ P256KeyExchange::~P256KeyExchange() { // static P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { if (key.empty()) { - DLOG(INFO) << "P256KeyExchange::New: " - "Private key is empty"; + DLOG(INFO) << "Private key is empty"; return NULL; } @@ -32,8 +31,7 @@ P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> private_key( d2i_ECPrivateKey(NULL, &keyp, key.size())); if (!private_key.get() || !EC_KEY_check_key(private_key.get())) { - DLOG(INFO) << "P256KeyExchange::New: " - "Private key is invalid"; + DLOG(INFO) << "Private key is invalid"; return NULL; } @@ -45,8 +43,7 @@ P256KeyExchange* P256KeyExchange::New(base::StringPiece key) { public_key, sizeof(public_key), NULL) != sizeof(public_key)) { - DLOG(INFO) << "P256KeyExchange::New: " - "Can't get public key"; + DLOG(INFO) << "Can't get public key"; return NULL; } @@ -58,23 +55,20 @@ std::string P256KeyExchange::NewPrivateKey() { crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> key( EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); if (!key.get() || !EC_KEY_generate_key(key.get())) { - DLOG(INFO) << "P256KeyExchange::NewPrivateKey: " - "Can't generate a new private key"; - return ""; + DLOG(INFO) << "Can't generate a new private key"; + return std::string(); } int key_len = i2d_ECPrivateKey(key.get(), NULL); if (key_len <= 0) { - DLOG(INFO) << "P256KeyExchange::NewPrivateKey: " - "Can't convert private key to string"; - return ""; + DLOG(INFO) << "Can't convert private key to string"; + return std::string(); } scoped_ptr<uint8[]> private_key(new uint8[key_len]); uint8* keyp = private_key.get(); if (!i2d_ECPrivateKey(key.get(), &keyp)) { - DLOG(INFO) << "P256KeyExchange::NewPrivateKey: " - "Can't convert private key to string"; - return ""; + DLOG(INFO) << "Can't convert private key to string"; + return std::string(); } return std::string(reinterpret_cast<char*>(private_key.get()), key_len); } @@ -83,8 +77,7 @@ bool P256KeyExchange::CalculateSharedKey( const base::StringPiece& peer_public_value, std::string* out_result) const { if (peer_public_value.size() != kUncompressedP256PointBytes) { - DLOG(INFO) << "P256KeyExchange::CalculateSharedKey: " - "Peer public value is invalid"; + DLOG(INFO) << "Peer public value is invalid"; return false; } @@ -97,8 +90,7 @@ bool P256KeyExchange::CalculateSharedKey( reinterpret_cast<const uint8*>(peer_public_value.data()), peer_public_value.size(), NULL)) { - DLOG(INFO) << "P256KeyExchange::CalculateSharedKey: " - "Can't convert peer public value to curve point"; + DLOG(INFO) << "Can't convert peer public value to curve point"; return false; } @@ -109,8 +101,7 @@ bool P256KeyExchange::CalculateSharedKey( point.get(), private_key_.get(), NULL) != sizeof(result)) { - DLOG(INFO) << "P256KeyExchange::CalculateSharedKey: " - "Can't compute ECDH shared key"; + DLOG(INFO) << "Can't compute ECDH shared key"; return false; } diff --git a/net/quic/crypto/p256_key_exchange_test.cc b/net/quic/crypto/p256_key_exchange_test.cc index 61df64a..799c853 100644 --- a/net/quic/crypto/p256_key_exchange_test.cc +++ b/net/quic/crypto/p256_key_exchange_test.cc @@ -10,15 +10,19 @@ namespace net { namespace test { -#if defined(USE_OPENSSL) // SharedKey just tests that the basic key exchange identity holds: that both // parties end up with the same key. TEST(P256KeyExchange, SharedKey) { for (int i = 0; i < 5; i++) { - scoped_ptr<P256KeyExchange> alice(P256KeyExchange::New( - P256KeyExchange::NewPrivateKey())); - scoped_ptr<P256KeyExchange> bob(P256KeyExchange::New( - P256KeyExchange::NewPrivateKey())); + std::string alice_private(P256KeyExchange::NewPrivateKey()); + std::string bob_private(P256KeyExchange::NewPrivateKey()); + + ASSERT_FALSE(alice_private.empty()); + ASSERT_FALSE(bob_private.empty()); + ASSERT_NE(alice_private, bob_private); + + scoped_ptr<P256KeyExchange> alice(P256KeyExchange::New(alice_private)); + scoped_ptr<P256KeyExchange> bob(P256KeyExchange::New(bob_private)); ASSERT_TRUE(alice.get() != NULL); ASSERT_TRUE(bob.get() != NULL); @@ -32,7 +36,6 @@ TEST(P256KeyExchange, SharedKey) { ASSERT_EQ(alice_shared, bob_shared); } } -#endif } // namespace test } // namespace net |