summaryrefslogtreecommitdiffstats
path: root/net/quic
diff options
context:
space:
mode:
authorthaidn@google.com <thaidn@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-08 18:36:24 +0000
committerthaidn@google.com <thaidn@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-08 18:36:24 +0000
commitf39d0ad6d7705731f1d79ae16382353152870c22 (patch)
treea2e3bcb371d8516b08ee8e745b17b9f555769dd3 /net/quic
parent2cf798a202836d587a47812258cf08bc634808b5 (diff)
downloadchromium_src-f39d0ad6d7705731f1d79ae16382353152870c22.zip
chromium_src-f39d0ad6d7705731f1d79ae16382353152870c22.tar.gz
chromium_src-f39d0ad6d7705731f1d79ae16382353152870c22.tar.bz2
Add P256 key exchange. This CL is for OpenSSL. The NSS part is coming in another CL.
Merge internal CL: 42820233. Review URL: https://chromiumcodereview.appspot.com/12546007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@186998 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/quic')
-rw-r--r--net/quic/crypto/p256_key_exchange.h81
-rw-r--r--net/quic/crypto/p256_key_exchange_openssl.cc131
-rw-r--r--net/quic/crypto/p256_key_exchange_test.cc39
3 files changed, 251 insertions, 0 deletions
diff --git a/net/quic/crypto/p256_key_exchange.h b/net/quic/crypto/p256_key_exchange.h
new file mode 100644
index 0000000..21e9eff
--- /dev/null
+++ b/net/quic/crypto/p256_key_exchange.h
@@ -0,0 +1,81 @@
+// 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.
+
+#ifndef NET_QUIC_CRYPTO_P256_KEY_EXCHANGE_H_
+#define NET_QUIC_CRYPTO_P256_KEY_EXCHANGE_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/string_piece.h"
+#include "net/quic/crypto/key_exchange.h"
+
+#if defined(USE_OPENSSL)
+#include "crypto/openssl_util.h"
+// Forward declaration for openssl/*.h
+typedef struct ec_key_st EC_KEY;
+extern "C" void EC_KEY_free(EC_KEY* key);
+#else
+#include "crypto/ec_private_key.h"
+#include "crypto/scoped_nss_types.h"
+#endif
+
+namespace net {
+
+// P256KeyExchange implements a KeyExchange using elliptic-curve
+// Diffie-Hellman on NIST P-256.
+class P256KeyExchange : public KeyExchange {
+ public:
+ virtual ~P256KeyExchange();
+
+ // New creates a new key exchange object from a private key. If
+ // |private_key| is invalid, NULL is returned.
+ static P256KeyExchange* New(base::StringPiece private_key);
+
+ // |NewPrivateKey| returns a private key, suitable for passing to |New|.
+ // If |NewPrivateKey| can't generate a private key, it returns an empty
+ // string.
+ static std::string NewPrivateKey();
+
+ // KeyExchange interface.
+ virtual bool CalculateSharedKey(const base::StringPiece& peer_public_value,
+ std::string* shared_key) const OVERRIDE;
+ virtual base::StringPiece public_value() const OVERRIDE;
+ virtual CryptoTag tag() const OVERRIDE;
+
+ private:
+ enum {
+ // A P-256 field element consists of 32 bytes.
+ kP256FieldBytes = 32,
+ // A P-256 point in uncompressed form consists of 0x04 (to denote
+ // that the point is uncompressed) followed by two, 32-byte field
+ // elements.
+ kUncompressedP256PointBytes = 1 + 2 * kP256FieldBytes,
+ // The first byte in an uncompressed P-256 point.
+ kUncompressedECPointForm = 0x04,
+ };
+
+#if defined(USE_OPENSSL)
+ // P256KeyExchange takes ownership of |private_key|.
+ P256KeyExchange(EC_KEY* private_key, 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);
+
+ scoped_ptr<crypto::ECPrivateKey> key_pair_;
+#endif
+};
+
+} // namespace net
+#endif // NET_QUIC_CRYPTO_P256_KEY_EXCHANGE_H_
+
diff --git a/net/quic/crypto/p256_key_exchange_openssl.cc b/net/quic/crypto/p256_key_exchange_openssl.cc
new file mode 100644
index 0000000..74f68a6
--- /dev/null
+++ b/net/quic/crypto/p256_key_exchange_openssl.cc
@@ -0,0 +1,131 @@
+// 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 <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/evp.h>
+
+#include "base/logging.h"
+
+namespace net {
+
+P256KeyExchange::P256KeyExchange(EC_KEY* private_key, uint8* public_key)
+ : private_key_(private_key) {
+ memcpy(public_key_, public_key, sizeof(public_key_));
+}
+
+P256KeyExchange::~P256KeyExchange() {
+}
+
+// static
+P256KeyExchange* P256KeyExchange::New(base::StringPiece key) {
+ if (key.empty()) {
+ DLOG(INFO) << "P256KeyExchange::New: "
+ "Private key is empty";
+ return NULL;
+ }
+
+ const uint8* keyp = reinterpret_cast<const uint8*>(key.data());
+ 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";
+ return NULL;
+ }
+
+ uint8 public_key[kUncompressedP256PointBytes];
+ if (EC_POINT_point2oct(
+ EC_KEY_get0_group(private_key.get()),
+ EC_KEY_get0_public_key(private_key.get()),
+ POINT_CONVERSION_UNCOMPRESSED,
+ public_key,
+ sizeof(public_key),
+ NULL) != sizeof(public_key)) {
+ DLOG(INFO) << "P256KeyExchange::New: "
+ "Can't get public key";
+ return NULL;
+ }
+
+ return new P256KeyExchange(private_key.release(), public_key);
+}
+
+// static
+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 "";
+ }
+
+ int key_len = i2d_ECPrivateKey(key.get(), NULL);
+ if (key_len <= 0) {
+ DLOG(INFO) << "P256KeyExchange::NewPrivateKey: "
+ "Can't convert private key to string";
+ return "";
+ }
+ 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 "";
+ }
+ return std::string(reinterpret_cast<char*>(private_key.get()), key_len);
+}
+
+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";
+ return false;
+ }
+
+ crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free> point(
+ EC_POINT_new(EC_KEY_get0_group(private_key_.get())));
+ if (!point.get() ||
+ !EC_POINT_oct2point( /* also test if point is on curve */
+ EC_KEY_get0_group(private_key_.get()),
+ point.get(),
+ 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";
+ return false;
+ }
+
+ uint8 result[kP256FieldBytes];
+ if (ECDH_compute_key(
+ result,
+ sizeof(result),
+ point.get(),
+ private_key_.get(),
+ NULL) != sizeof(result)) {
+ DLOG(INFO) << "P256KeyExchange::CalculateSharedKey: "
+ "Can't compute ECDH shared key";
+ return false;
+ }
+
+ out_result->assign(reinterpret_cast<char*>(result), sizeof(result));
+ 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_test.cc b/net/quic/crypto/p256_key_exchange_test.cc
new file mode 100644
index 0000000..61df64a
--- /dev/null
+++ b/net/quic/crypto/p256_key_exchange_test.cc
@@ -0,0 +1,39 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+
+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()));
+
+ ASSERT_TRUE(alice.get() != NULL);
+ ASSERT_TRUE(bob.get() != NULL);
+
+ const base::StringPiece alice_public(alice->public_value());
+ const base::StringPiece bob_public(bob->public_value());
+
+ std::string alice_shared, bob_shared;
+ ASSERT_TRUE(alice->CalculateSharedKey(bob_public, &alice_shared));
+ ASSERT_TRUE(bob->CalculateSharedKey(alice_public, &bob_shared));
+ ASSERT_EQ(alice_shared, bob_shared);
+ }
+}
+#endif
+
+} // namespace test
+} // namespace net
+