diff options
-rw-r--r-- | net/net.gyp | 4 | ||||
-rw-r--r-- | net/quic/crypto/p256_key_exchange.h | 81 | ||||
-rw-r--r-- | net/quic/crypto/p256_key_exchange_openssl.cc | 131 | ||||
-rw-r--r-- | net/quic/crypto/p256_key_exchange_test.cc | 39 |
4 files changed, 255 insertions, 0 deletions
diff --git a/net/net.gyp b/net/net.gyp index 04d5633..b5b371e 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -711,6 +711,8 @@ 'quic/crypto/null_decrypter.h', 'quic/crypto/null_encrypter.cc', 'quic/crypto/null_encrypter.h', + 'quic/crypto/p256_key_exchange.h', + 'quic/crypto/p256_key_exchange_openssl.cc', 'quic/crypto/quic_decrypter.cc', 'quic/crypto/quic_decrypter.h', 'quic/crypto/quic_encrypter.cc', @@ -1117,6 +1119,7 @@ 'base/x509_certificate_openssl.cc', 'base/x509_util_openssl.cc', 'base/x509_util_openssl.h', + 'quic/crypto/p256_key_exchange_openssl.cc', 'socket/ssl_client_socket_openssl.cc', 'socket/ssl_client_socket_openssl.h', 'socket/ssl_server_socket_openssl.cc', @@ -1535,6 +1538,7 @@ 'quic/crypto/curve25519_key_exchange_test.cc', 'quic/crypto/null_decrypter_test.cc', 'quic/crypto/null_encrypter_test.cc', + 'quic/crypto/p256_key_exchange_test.cc', 'quic/crypto/quic_random_test.cc', 'quic/test_tools/mock_clock.cc', 'quic/test_tools/mock_clock.h', 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 + |