summaryrefslogtreecommitdiffstats
path: root/net/quic
diff options
context:
space:
mode:
authorthaidn@google.com <thaidn@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-13 01:12:12 +0000
committerthaidn@google.com <thaidn@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-13 01:12:12 +0000
commitf99a6549ccd1849f5fd2e9bae60ff574fef26e0f (patch)
tree7ca170908cb356f4ee63997e4da8b1228446d13d /net/quic
parent84634d071c488f461bf7af07884dd4a952910104 (diff)
downloadchromium_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.h22
-rw-r--r--net/quic/crypto/p256_key_exchange_nss.cc226
-rw-r--r--net/quic/crypto/p256_key_exchange_openssl.cc35
-rw-r--r--net/quic/crypto/p256_key_exchange_test.cc15
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