diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-02 17:43:35 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-02 17:43:35 +0000 |
commit | 6f54ab335c8794b0b93fe9473adadd27e95b5cf8 (patch) | |
tree | 6a12370c1597daef4f7d7e13fa5e4180e0aef7d5 /net | |
parent | 0622b36cbdbbbf942f1bdcc3c74f658e2517c104 (diff) | |
download | chromium_src-6f54ab335c8794b0b93fe9473adadd27e95b5cf8.zip chromium_src-6f54ab335c8794b0b93fe9473adadd27e95b5cf8.tar.gz chromium_src-6f54ab335c8794b0b93fe9473adadd27e95b5cf8.tar.bz2 |
QUIC - Some sketching of the crypto handshake.
Merge internal CL: 42490294
R=wtc@chromium.org, agl@chromium.org
Review URL: https://chromiumcodereview.appspot.com/12381018
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@185726 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/net.gyp | 6 | ||||
-rw-r--r-- | net/quic/crypto/crypto_framer.cc | 42 | ||||
-rw-r--r-- | net/quic/crypto/crypto_framer.h | 8 | ||||
-rw-r--r-- | net/quic/crypto/crypto_handshake.cc | 143 | ||||
-rw-r--r-- | net/quic/crypto/crypto_handshake.h | 117 | ||||
-rw-r--r-- | net/quic/crypto/crypto_handshake_test.cc | 14 | ||||
-rw-r--r-- | net/quic/crypto/crypto_protocol.cc | 153 | ||||
-rw-r--r-- | net/quic/crypto/crypto_protocol.h | 80 | ||||
-rw-r--r-- | net/quic/crypto/crypto_utils.cc | 93 | ||||
-rw-r--r-- | net/quic/crypto/crypto_utils.h | 33 | ||||
-rw-r--r-- | net/quic/crypto/curve25519_key_exchange.cc | 101 | ||||
-rw-r--r-- | net/quic/crypto/curve25519_key_exchange.h | 51 | ||||
-rw-r--r-- | net/quic/crypto/key_exchange.h | 40 | ||||
-rw-r--r-- | net/quic/quic_crypto_client_stream.cc | 5 | ||||
-rw-r--r-- | net/quic/quic_crypto_client_stream.h | 3 | ||||
-rw-r--r-- | net/quic/test_tools/quic_test_utils.cc | 18 |
16 files changed, 672 insertions, 235 deletions
diff --git a/net/net.gyp b/net/net.gyp index 1eb30ac..8bacb46 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -695,10 +695,15 @@ 'quic/congestion_control/tcp_receiver.h', 'quic/crypto/crypto_framer.cc', 'quic/crypto/crypto_framer.h', + 'quic/crypto/crypto_handshake.cc', + 'quic/crypto/crypto_handshake.h', 'quic/crypto/crypto_protocol.cc', 'quic/crypto/crypto_protocol.h', 'quic/crypto/crypto_utils.cc', 'quic/crypto/crypto_utils.h', + 'quic/crypto/curve25519_key_exchange.cc', + 'quic/crypto/curve25519_key_exchange.h', + 'quic/crypto/key_exchange.h', 'quic/crypto/null_decrypter.cc', 'quic/crypto/null_decrypter.h', 'quic/crypto/null_encrypter.cc', @@ -1519,6 +1524,7 @@ 'quic/congestion_control/tcp_cubic_sender_test.cc', 'quic/congestion_control/tcp_receiver_test.cc', 'quic/crypto/crypto_framer_test.cc', + 'quic/crypto/crypto_handshake_test.cc', 'quic/crypto/null_decrypter_test.cc', 'quic/crypto/null_encrypter_test.cc', 'quic/crypto/quic_random_test.cc', diff --git a/net/quic/crypto/crypto_framer.cc b/net/quic/crypto/crypto_framer.cc index db1bb6f..86b2df7 100644 --- a/net/quic/crypto/crypto_framer.cc +++ b/net/quic/crypto/crypto_framer.cc @@ -18,6 +18,31 @@ const size_t kCryptoTagSize = sizeof(uint32); const size_t kNumEntriesSize = sizeof(uint16); const size_t kValueLenSize = sizeof(uint16); +// OneShotVisitor is a framer visitor that records a single handshake message. +class OneShotVisitor : public CryptoFramerVisitorInterface { + public: + explicit OneShotVisitor(CryptoHandshakeMessage* out) + : out_(out), + error_(false) { + } + + void OnError(CryptoFramer* framer) { + error_ = true; + } + + void OnHandshakeMessage(const CryptoHandshakeMessage& message) { + *out_ = message; + } + + bool error() const { + return error_; + } + + private: + CryptoHandshakeMessage* const out_; + bool error_; +}; + } // namespace CryptoFramer::CryptoFramer() @@ -30,6 +55,22 @@ CryptoFramer::CryptoFramer() CryptoFramer::~CryptoFramer() {} +// static +CryptoHandshakeMessage* CryptoFramer::ParseMessage(StringPiece in) { + scoped_ptr<CryptoHandshakeMessage> msg(new CryptoHandshakeMessage); + OneShotVisitor visitor(msg.get()); + CryptoFramer framer; + + framer.set_visitor(&visitor); + if (!framer.ProcessInput(in) || + visitor.error() || + framer.InputBytesRemaining()) { + return NULL; + } + + return msg.release(); +} + bool CryptoFramer::ProcessInput(StringPiece input) { DCHECK_EQ(QUIC_NO_ERROR, error_); if (error_ != QUIC_NO_ERROR) { @@ -119,6 +160,7 @@ bool CryptoFramer::ProcessInput(StringPiece input) { return true; } +// static QuicData* CryptoFramer::ConstructHandshakeMessage( const CryptoHandshakeMessage& message) { if (message.tag_value_map.size() > kMaxEntries) { diff --git a/net/quic/crypto/crypto_framer.h b/net/quic/crypto/crypto_framer.h index 34966fc..efa5259 100644 --- a/net/quic/crypto/crypto_framer.h +++ b/net/quic/crypto/crypto_framer.h @@ -42,6 +42,11 @@ class NET_EXPORT_PRIVATE CryptoFramer { virtual ~CryptoFramer(); + // ParseMessage parses exactly one message from the given StringPiece. If + // there is an error, the message is truncated, or the message has trailing + // garbage then NULL will be returned. + static CryptoHandshakeMessage* ParseMessage(base::StringPiece in); + // Set callbacks to be called from the framer. A visitor must be set, or // else the framer will crash. It is acceptable for the visitor to do // nothing. If this is called multiple times, only the last visitor @@ -66,7 +71,8 @@ class NET_EXPORT_PRIVATE CryptoFramer { // Returns a new QuicData owned by the caller that contains a serialized // |message|, or NULL if there was an error. - QuicData* ConstructHandshakeMessage(const CryptoHandshakeMessage& message); + static QuicData* ConstructHandshakeMessage( + const CryptoHandshakeMessage& message); private: // Clears per-message state. Does not clear the visitor. diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc new file mode 100644 index 0000000..48c9645 --- /dev/null +++ b/net/quic/crypto/crypto_handshake.cc @@ -0,0 +1,143 @@ +// 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/crypto_handshake.h" + +#include "base/stl_util.h" +#include "net/base/net_util.h" +#include "net/quic/crypto/key_exchange.h" +#include "net/quic/crypto/quic_random.h" +#include "net/quic/quic_protocol.h" + +using std::string; + +namespace net { + +QuicCryptoClientConfig::QuicCryptoClientConfig() + : version(0), + idle_connection_state_lifetime(QuicTime::Delta::Zero()), + keepalive_timeout(QuicTime::Delta::Zero()) { +} + +QuicCryptoClientConfig::~QuicCryptoClientConfig() {} + +void QuicCryptoClientConfig::SetDefaults() { + // Version must be 0. + version = 0; + + // Key exchange methods. + key_exchange.resize(2); + key_exchange[0] = kC255; + key_exchange[1] = kP256; + + // Authenticated encryption algorithms. + aead.resize(2); + aead[0] = kAESG; + aead[1] = kAESH; + + // Congestion control feedback types. + // TODO(wtc): add kINAR when inter-arrival is supported. + congestion_control.resize(1); + congestion_control[0] = kQBIC; + + // Idle connection state lifetime. + idle_connection_state_lifetime = QuicTime::Delta::FromSeconds(300); + + // Keepalive timeout. + keepalive_timeout = QuicTime::Delta::Zero(); // Don't send keepalive probes. +} + +void QuicCryptoClientConfig::FillClientHello(const string& nonce, + const string& server_hostname, + CryptoHandshakeMessage* out) { + out->tag = kCHLO; + + out->SetValue(kVERS, version); + out->SetVector(kKEXS, key_exchange); + out->SetVector(kAEAD, aead); + out->SetVector(kCGST, congestion_control); + out->tag_value_map[kNONC] = nonce; + + // Idle connection state lifetime. + uint32 idle_connection_state_lifetime_secs = + idle_connection_state_lifetime.ToSeconds(); + out->SetValue(kICSL, idle_connection_state_lifetime_secs); + + // Keepalive timeout. + uint32 keepalive_timeout_secs = keepalive_timeout.ToSeconds(); + out->SetValue(kKATO, keepalive_timeout_secs); + + // Server name indication. + // If server_hostname is not an IP address literal, it is a DNS hostname. + IPAddressNumber ip_number; + if (!server_hostname.empty() && + !ParseIPLiteralToNumber(server_hostname, &ip_number)) { + out->tag_value_map[kSNI] = server_hostname; + } +} + +// TODO(rtenneti): Delete QuicCryptoServerConfig. +QuicCryptoServerConfig::QuicCryptoServerConfig() { +} + +QuicCryptoServerConfig::~QuicCryptoServerConfig() { + STLDeleteValues(&configs_); +} + +void QuicCryptoServerConfig::AddTestingConfig(QuicRandom* rand, + const QuicClock* clock) { +} + +bool QuicCryptoServerConfig::ProcessClientHello( + const CryptoHandshakeMessage& client_hello, + const string& nonce, + CryptoHandshakeMessage* out) { + CHECK(!configs_.empty()); + const Config* config(configs_[active_config_]); + + // TODO(agl): This is obviously missing most of the handshake. + out->tag = kSHLO; + out->tag_value_map[kNONC] = nonce; + out->tag_value_map[kSCFG] = config->serialized; + return true; +} + +QuicCryptoServerConfig::Config::Config() { +} + +QuicCryptoServerConfig::Config::~Config() { + STLDeleteValues(&key_exchanges); +} + +QuicCryptoNegotiatedParams::QuicCryptoNegotiatedParams() + : version(0), + key_exchange(0), + aead(0), + congestion_control(0), + idle_connection_state_lifetime(QuicTime::Delta::Zero()) { +} + +QuicCryptoNegotiatedParams::~QuicCryptoNegotiatedParams() {} + +void QuicCryptoNegotiatedParams::SetDefaults() { + // TODO(wtc): actually negotiate the parameters using client defaults + // and server defaults. + + // Version must be 0. + version = 0; + + // Key exchange method. + key_exchange = kP256; + + // Authenticated encryption algorithm. + aead = kAESG; + + // Congestion control feedback type. + congestion_control = kQBIC; + + // Idle connection state lifetime. + idle_connection_state_lifetime = QuicTime::Delta::FromSeconds(300); +} + +} // namespace net diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h new file mode 100644 index 0000000..06b2103 --- /dev/null +++ b/net/quic/crypto/crypto_handshake.h @@ -0,0 +1,117 @@ +// 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_CRYPTO_HANDSHAKE_H_ +#define NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_ + +#include <map> +#include <string> + +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_protocol.h" + +namespace net { + +class KeyExchange; +class QuicRandom; +class QuicClock; + +// QuicCryptoClientConfig contains crypto-related configuration settings for a +// client. +class NET_EXPORT_PRIVATE QuicCryptoClientConfig { + public: + // Initializes the members to 0 or empty values. + QuicCryptoClientConfig(); + ~QuicCryptoClientConfig(); + + // Sets the members to reasonable, default values. + void SetDefaults(); + + // FillClientHello sets |out| to be a CHLO message based on the configuration + // of this object. + void FillClientHello(const std::string& nonce, + const std::string& server_hostname, + CryptoHandshakeMessage* out); + + // Protocol version + uint16 version; + // Key exchange methods + CryptoTagVector key_exchange; + // Authenticated encryption with associated data (AEAD) algorithms + CryptoTagVector aead; + // Congestion control feedback types + CryptoTagVector congestion_control; + // Idle connection state lifetime + QuicTime::Delta idle_connection_state_lifetime; + // Keepalive timeout, or 0 to turn off keepalive probes + QuicTime::Delta keepalive_timeout; +}; + +// TODO(rtenneti): Delete QuicCryptoServerConfig. +// +// QuicCryptoServerConfig contains the crypto configuration of a QUIC server. +// Unlike a client, a QUIC server can have multiple configurations active in +// order to support clients resuming with a previous configuration. +// TODO(agl): when adding configurations at runtime is added, this object will +// need to consider locking. +class NET_EXPORT_PRIVATE QuicCryptoServerConfig { + public: + QuicCryptoServerConfig(); + ~QuicCryptoServerConfig(); + + // AddTestingConfig adds a single, testing config. + void AddTestingConfig(QuicRandom* rand, const QuicClock* clock); + + // ProcessClientHello processes |client_hello| and decides whether to accept + // or reject the connection. If the connection is to be accepted, |out| is + // set to the contents of the ServerHello and true is returned. |nonce| is + // used as the server's nonce. Otherwise |out| is set to be a REJ message + // and false is returned. + bool ProcessClientHello(const CryptoHandshakeMessage& client_hello, + const std::string& nonce, + CryptoHandshakeMessage* out); + + private: + // Config represents a server config: a collection of preferences and + // Diffie-Hellman public values. + struct Config { + Config(); + ~Config(); + + // serialized contains the bytes of this server config, suitable for sending + // on the wire. + std::string serialized; + // key_exchange_tags contains the key exchange methods from the config, + // in preference order. + CryptoTagVector key_exchange_tags; + // key_exchanges maps from elements of |key_exchange_tags| to the object + // that implements the specific key exchange. + std::map<CryptoTag, KeyExchange*> key_exchanges; + }; + + std::map<ServerConfigID, Config*> configs_; + + std::string active_config_; +}; + +// Parameters negotiated by the crypto handshake. +struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParams { + // Initializes the members to 0 or empty values. + QuicCryptoNegotiatedParams(); + ~QuicCryptoNegotiatedParams(); + + // Sets the members to the values that would be negotiated from the default + // client-side and server-side configuration settings. + void SetDefaults(); + + uint16 version; + CryptoTag key_exchange; + CryptoTag aead; + CryptoTag congestion_control; + QuicTime::Delta idle_connection_state_lifetime; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_ diff --git a/net/quic/crypto/crypto_handshake_test.cc b/net/quic/crypto/crypto_handshake_test.cc new file mode 100644 index 0000000..0ce2ed4 --- /dev/null +++ b/net/quic/crypto/crypto_handshake_test.cc @@ -0,0 +1,14 @@ +// 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/crypto_handshake.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +} // namespace test +} // namespace net diff --git a/net/quic/crypto/crypto_protocol.cc b/net/quic/crypto/crypto_protocol.cc index 30a1cdc..435fe78 100644 --- a/net/quic/crypto/crypto_protocol.cc +++ b/net/quic/crypto/crypto_protocol.cc @@ -4,99 +4,94 @@ #include "net/quic/crypto/crypto_protocol.h" -namespace net { - -CryptoHandshakeMessage::CryptoHandshakeMessage() {} -CryptoHandshakeMessage::~CryptoHandshakeMessage() {} - -QuicCryptoConfig::QuicCryptoConfig() - : version(0), - idle_connection_state_lifetime(QuicTime::Delta::Zero()), - keepalive_timeout(QuicTime::Delta::Zero()) { -} - -QuicCryptoConfig::~QuicCryptoConfig() {} - -void QuicCryptoConfig::SetClientDefaults() { - // Version must be 0. - version = 0; +#include <stdarg.h> +#include <string.h> - // Key exchange methods. - key_exchange.resize(2); - key_exchange[0] = kC255; - key_exchange[1] = kP256; +#include "base/memory/scoped_ptr.h" - // Authenticated encryption algorithms. - aead.resize(2); - aead[0] = kAESG; - aead[1] = kAESH; +using std::string; - // Congestion control feedback types. - // TODO(wtc): add kINAR when inter-arrival is supported. - congestion_control.resize(1); - congestion_control[0] = kQBIC; +namespace net { - // Idle connection state lifetime. - idle_connection_state_lifetime = QuicTime::Delta::FromSeconds(300); +CryptoHandshakeMessage::CryptoHandshakeMessage() {} +CryptoHandshakeMessage::~CryptoHandshakeMessage() {} - // Keepalive timeout. - keepalive_timeout = QuicTime::Delta::Zero(); // Don't send keepalive probes. +void CryptoHandshakeMessage::SetTaglist(CryptoTag tag, ...) { + // Warning, if sizeof(CryptoTag) > sizeof(int) then this function will break + // because the terminating 0 will only be promoted to int. + COMPILE_ASSERT(sizeof(CryptoTag) <= sizeof(int), + crypto_tag_not_be_larger_than_int_or_varargs_will_break); + + std::vector<CryptoTag> tags; + va_list ap; + + va_start(ap, tag); + for (;;) { + CryptoTag tag = va_arg(ap, CryptoTag); + if (tag == 0) { + break; + } + tags.push_back(tag); + } + + // Because of the way that we keep tags in memory, we can copy the contents + // of the vector and get the correct bytes in wire format. See + // crypto_protocol.h. This assumes that the system is little-endian. + SetVector(tag, tags); + + va_end(ap); } -void QuicCryptoConfig::SetServerDefaults() { - // Version must be 0. - version = 0; - - // Key exchange methods. - // Add only NIST curve P-256 for now to ensure it is selected. - key_exchange.resize(1); - key_exchange[0] = kP256; - - // Authenticated encryption algorithms. - // Add only AES-GCM for now to ensure it is selected. - aead.resize(1); - aead[0] = kAESG; - - // Congestion control feedback types. - // TODO(wtc): add kINAR when inter-arrival is supported. - congestion_control.resize(1); - congestion_control[0] = kQBIC; - - // Idle connection state lifetime. - idle_connection_state_lifetime = QuicTime::Delta::FromSeconds(300); - - // Keepalive timeout. - keepalive_timeout = QuicTime::Delta::Zero(); // Don't send keepalive probes. +QuicErrorCode CryptoHandshakeMessage::GetTaglist(CryptoTag tag, + const CryptoTag** out_tags, + size_t* out_len) const { + CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); + QuicErrorCode ret = QUIC_NO_ERROR; + + if (it == tag_value_map.end()) { + ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } else if (it->second.size() % sizeof(CryptoTag) != 0) { + ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (ret != QUIC_NO_ERROR) { + *out_tags = NULL; + *out_len = 0; + return ret; + } + + *out_tags = reinterpret_cast<const CryptoTag*>(it->second.data()); + *out_len = it->second.size() / sizeof(CryptoTag); + return ret; } -QuicCryptoNegotiatedParams::QuicCryptoNegotiatedParams() - : version(0), - key_exchange(0), - aead(0), - congestion_control(0), - idle_connection_state_lifetime(QuicTime::Delta::Zero()) { +bool CryptoHandshakeMessage::GetString(CryptoTag tag, string* out) const { + CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); + if (it == tag_value_map.end()) { + return false; + } + *out = it->second; + return true; } -QuicCryptoNegotiatedParams::~QuicCryptoNegotiatedParams() {} - -void QuicCryptoNegotiatedParams::SetDefaults() { - // TODO(wtc): actually negotiate the parameters using client defaults - // and server defaults. - - // Version must be 0. - version = 0; - - // Key exchange method. - key_exchange = kP256; +QuicErrorCode CryptoHandshakeMessage::GetUint32(CryptoTag tag, + uint32* out) const { + CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); + QuicErrorCode ret = QUIC_NO_ERROR; - // Authenticated encryption algorithm. - aead = kAESG; + if (it == tag_value_map.end()) { + ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } else if (it->second.size() != sizeof(uint32)) { + ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } - // Congestion control feedback type. - congestion_control = kQBIC; + if (ret != QUIC_NO_ERROR) { + *out = 0; + return ret; + } - // Idle connection state lifetime. - idle_connection_state_lifetime = QuicTime::Delta::FromSeconds(300); + memcpy(out, it->second.data(), sizeof(uint32)); + return ret; } } // namespace net diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index f3fde8b..64b258c 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -12,11 +12,14 @@ #include "base/basictypes.h" #include "base/logging.h" #include "net/base/net_export.h" +#include "net/quic/quic_protocol.h" #include "net/quic/quic_time.h" namespace net { +// CryptoTag is the type of a tag in the wire protocol. typedef uint32 CryptoTag; +typedef std::string ServerConfigID; typedef std::map<CryptoTag, std::string> CryptoTagValueMap; typedef std::vector<CryptoTag> CryptoTagVector; // An intermediate format of a handshake message that's convenient for a @@ -24,6 +27,40 @@ typedef std::vector<CryptoTag> CryptoTagVector; struct NET_EXPORT_PRIVATE CryptoHandshakeMessage { CryptoHandshakeMessage(); ~CryptoHandshakeMessage(); + + // SetValue sets an element with the given tag to the raw, memory contents of + // |v|. + template<class T> void SetValue(CryptoTag tag, const T& v) { + tag_value_map[tag] = std::string(reinterpret_cast<const char*>(&v), + sizeof(v)); + } + + // SetVector sets an element with the given tag to the raw contents of an + // array of elements in |v|. + template<class T> void SetVector(CryptoTag tag, const std::vector<T>& v) { + if (v.empty()) { + tag_value_map[tag] = std::string(); + } else { + tag_value_map[tag] = std::string(reinterpret_cast<const char*>(&v[0]), + v.size() * sizeof(T)); + } + } + + // SetTaglist sets an element with the given tag to contain a list of tags, + // passed as varargs. The argument list must be terminated with a 0 element. + void SetTaglist(CryptoTag tag, ...); + + // GetTaglist finds an element with the given tag containing zero or more + // tags. If such a tag doesn't exist, it returns false. Otherwise it sets + // |out_tags| and |out_len| to point to the array of tags and returns true. + // The array points into the CryptoHandshakeMessage and is valid only for as + // long as the CryptoHandshakeMessage exists and is not modified. + QuicErrorCode GetTaglist(CryptoTag tag, const CryptoTag** out_tags, + size_t* out_len) const; + + bool GetString(CryptoTag tag, std::string* out) const; + QuicErrorCode GetUint32(CryptoTag tag, uint32* out) const; + CryptoTag tag; CryptoTagValueMap tag_value_map; }; @@ -38,6 +75,7 @@ struct NET_EXPORT_PRIVATE CryptoHandshakeMessage { const CryptoTag kCHLO = MAKE_TAG('C', 'H', 'L', 'O'); // Client hello const CryptoTag kSHLO = MAKE_TAG('S', 'H', 'L', 'O'); // Server hello +const CryptoTag kSCFG = MAKE_TAG('S', 'H', 'L', 'O'); // Server config // Key exchange methods const CryptoTag kP256 = MAKE_TAG('P', '2', '5', '6'); // ECDH, Curve P-256 @@ -67,52 +105,12 @@ const CryptoTag kKATO = MAKE_TAG('K', 'A', 'T', 'O'); // Keepalive timeout const CryptoTag kSNI = MAKE_TAG('S', 'N', 'I', '\0'); // Server name // indication const CryptoTag kPUBS = MAKE_TAG('P', 'U', 'B', 'S'); // Public key values +const CryptoTag kSCID = MAKE_TAG('S', 'C', 'I', 'D'); // Server config id const size_t kMaxEntries = 16; // Max number of entries in a message. const size_t kNonceSize = 32; // Size in bytes of the connection nonce. -// Crypto configuration settings. -struct NET_EXPORT_PRIVATE QuicCryptoConfig { - // Initializes the members to 0 or empty values. - QuicCryptoConfig(); - ~QuicCryptoConfig(); - - // Sets the members to client-side or server-side default values. - void SetClientDefaults(); - void SetServerDefaults(); - - // Protocol version - uint16 version; - // Key exchange methods - CryptoTagVector key_exchange; - // Authenticated encryption with associated data (AEAD) algorithms - CryptoTagVector aead; - // Congestion control feedback types - CryptoTagVector congestion_control; - // Idle connection state lifetime - QuicTime::Delta idle_connection_state_lifetime; - // Keepalive timeout, or 0 to turn off keepalive probes - QuicTime::Delta keepalive_timeout; -}; - -// Parameters negotiated by the crypto handshake. -struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParams { - // Initializes the members to 0 or empty values. - QuicCryptoNegotiatedParams(); - ~QuicCryptoNegotiatedParams(); - - // Sets the members to the values that would be negotiated from the default - // client-side and server-side configuration settings. - void SetDefaults(); - - uint16 version; - CryptoTag key_exchange; - CryptoTag aead; - CryptoTag congestion_control; - QuicTime::Delta idle_connection_state_lifetime; -}; - } // namespace net #endif // NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ diff --git a/net/quic/crypto/crypto_utils.cc b/net/quic/crypto/crypto_utils.cc index b381682..ce5002a 100644 --- a/net/quic/crypto/crypto_utils.cc +++ b/net/quic/crypto/crypto_utils.cc @@ -5,7 +5,6 @@ #include "net/quic/crypto/crypto_utils.h" #include "base/string_piece.h" -#include "net/base/net_util.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_clock.h" @@ -15,6 +14,24 @@ using std::string; namespace net { +// static +bool CryptoUtils::FindMutualTag(const CryptoTagVector& preference, + const CryptoTagVector& supported, + CryptoTag* out_result) { + for (CryptoTagVector::const_iterator i = preference.begin(); + i != preference.end(); i++) { + for (CryptoTagVector::const_iterator j = supported.begin(); + j != supported.end(); j++) { + if (*i == *j) { + *out_result = *i; + return true; + } + } + } + + return false; +} + void CryptoUtils::GenerateNonce(const QuicClock* clock, QuicRandom* random_generator, string* nonce) { @@ -28,78 +45,4 @@ void CryptoUtils::GenerateNonce(const QuicClock* clock, random_generator->RandBytes(&(*nonce)[time_size], kNonceSize - time_size); } -void CryptoUtils::FillClientHelloMessage( - const QuicCryptoConfig& client_config, - const string& nonce, - const string& server_hostname, - CryptoHandshakeMessage* message) { - message->tag = kCHLO; - - // Version. - message->tag_value_map[kVERS] = EncodeSingleValue(client_config.version); - - // Key exchange methods. - message->tag_value_map[kKEXS] = EncodeVectorValue(client_config.key_exchange); - - // Authenticated encryption algorithms. - message->tag_value_map[kAEAD] = EncodeVectorValue(client_config.aead); - - // Congestion control feedback types. - message->tag_value_map[kCGST] = - EncodeVectorValue(client_config.congestion_control); - - // Idle connection state lifetime. - uint32 idle_connection_state_lifetime_secs = - client_config.idle_connection_state_lifetime.ToSeconds(); - message->tag_value_map[kICSL] = - EncodeSingleValue(idle_connection_state_lifetime_secs); - - // Keepalive timeout. - uint32 keepalive_timeout_secs = client_config.keepalive_timeout.ToSeconds(); - message->tag_value_map[kKATO] = EncodeSingleValue(keepalive_timeout_secs); - - // Connection nonce. - message->tag_value_map[kNONC] = nonce; - - // Server name indication. - // If server_hostname is not an IP address literal, it is a DNS hostname. - IPAddressNumber ip_number; - if (!server_hostname.empty() && - !ParseIPLiteralToNumber(server_hostname, &ip_number)) { - message->tag_value_map[kSNI] = server_hostname; - } -} - -void CryptoUtils::FillServerHelloMessage( - const QuicCryptoNegotiatedParams& negotiated_params, - const string& nonce, - CryptoHandshakeMessage* message) { - message->tag = kSHLO; - - // Version. - message->tag_value_map[kVERS] = EncodeSingleValue(negotiated_params.version); - - // Key exchange method. - message->tag_value_map[kKEXS] = - EncodeSingleValue(negotiated_params.key_exchange); - - // Authenticated encryption algorithm. - message->tag_value_map[kAEAD] = EncodeSingleValue(negotiated_params.aead); - - // Congestion control feedback type. - message->tag_value_map[kCGST] = - EncodeSingleValue(negotiated_params.congestion_control); - - // Idle connection state lifetime. - uint32 idle_connection_state_lifetime_secs = - negotiated_params.idle_connection_state_lifetime.ToSeconds(); - message->tag_value_map[kICSL] = - EncodeSingleValue(idle_connection_state_lifetime_secs); - - // Keepalive timeout? - - // Connection nonce. - message->tag_value_map[kNONC] = nonce; -} - } // namespace net diff --git a/net/quic/crypto/crypto_utils.h b/net/quic/crypto/crypto_utils.h index 496ad79..ff8111e 100644 --- a/net/quic/crypto/crypto_utils.h +++ b/net/quic/crypto/crypto_utils.h @@ -8,47 +8,28 @@ #define NET_QUIC_CRYPTO_CRYPTO_UTILS_H_ #include <string> -#include <vector> #include "net/base/net_export.h" +#include "net/quic/crypto/crypto_protocol.h" namespace net { class QuicClock; class QuicRandom; -struct CryptoHandshakeMessage; -struct QuicCryptoConfig; -struct QuicCryptoNegotiatedParams; class NET_EXPORT_PRIVATE CryptoUtils { public: - // Encodes a single value as a string for CryptoTagValueMap. - template <class T> - static std::string EncodeSingleValue(const T& value) { - return std::string(reinterpret_cast<const char*>(&value), sizeof(value)); - } - - // Encodes a vector value as a string for CryptoTagValueMap. - template <class T> - static std::string EncodeVectorValue(const std::vector<T>& value) { - return std::string(reinterpret_cast<const char*>(&value[0]), - value.size() * sizeof(value[0])); - } + // FindMutualTag sets |out_result| to the first tag in |preference| that is + // also in |supported| and returns true. If there is no intersection between + // |preference| and |supported| it returns false. + static bool FindMutualTag(const CryptoTagVector& preference, + const CryptoTagVector& supported, + CryptoTag* out_result); // Generates the connection nonce. static void GenerateNonce(const QuicClock* clock, QuicRandom* random_generator, std::string* nonce); - - static void FillClientHelloMessage(const QuicCryptoConfig& client_config, - const std::string& nonce, - const std::string& server_hostname, - CryptoHandshakeMessage* message); - - static void FillServerHelloMessage( - const QuicCryptoNegotiatedParams& negotiated_params, - const std::string& nonce, - CryptoHandshakeMessage* message); }; } // namespace net diff --git a/net/quic/crypto/curve25519_key_exchange.cc b/net/quic/crypto/curve25519_key_exchange.cc new file mode 100644 index 0000000..548c758 --- /dev/null +++ b/net/quic/crypto/curve25519_key_exchange.cc @@ -0,0 +1,101 @@ +// 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/curve25519_key_exchange.h" + +#include <string.h> + +#include "base/logging.h" +#include "net/quic/crypto/quic_random.h" + +// TODO(rtenneti): Remove the following line after support for curve25519 is +// added. +#define crypto_scalarmult_curve25519_SCALARBYTES 32 + +using base::StringPiece; +using std::string; + +namespace net { + +Curve25519KeyExchange::Curve25519KeyExchange() { +} + +Curve25519KeyExchange::~Curve25519KeyExchange() { +} + +// static +Curve25519KeyExchange* Curve25519KeyExchange::New( + const StringPiece& private_key) { +// TODO(rtenneti): Add support for curve25519. +#if 0 + crypto_scalarmult_curve25519_base(ka->public_key_, ka->private_key_); + Curve25519KeyExchange* ka; + + // We don't want to #include the NaCl headers in the public header file, so + // we use literals for the sizes of private_key_ and public_key_. Here we + // assert that those values are equal to the values from the NaCl header. + COMPILE_ASSERT( + sizeof(ka->private_key_) == crypto_scalarmult_curve25519_SCALARBYTES, + header_out_of_sync); + COMPILE_ASSERT( + sizeof(ka->public_key_) == crypto_scalarmult_curve25519_BYTES, + header_out_of_sync); + + if (private_key.size() != crypto_scalarmult_curve25519_SCALARBYTES) { + return NULL; + } + + ka = new Curve25519KeyExchange(); + memcpy(ka->private_key_, private_key.data(), + crypto_scalarmult_curve25519_SCALARBYTES); + return ka; +#else + return NULL; +#endif +} + +// static +string Curve25519KeyExchange::NewPrivateKey(QuicRandom* rand) { + uint8 private_key[crypto_scalarmult_curve25519_SCALARBYTES]; + rand->RandBytes(private_key, sizeof(private_key)); + + // This makes |private_key| a valid scalar, as specified on + // http://cr.yp.to/ecdh.html + private_key[0] &= 248; + private_key[31] &= 127; + private_key[31] |= 64; + return string(reinterpret_cast<char*>(private_key), sizeof(private_key)); +} + +bool Curve25519KeyExchange::CalculateSharedKey( + const StringPiece& public_value, + string* out_result) const { +// TODO(rtenneti): Add support for curve25519. +#if 0 + if (public_value.size() != crypto_scalarmult_curve25519_BYTES) { + return false; + } + + uint8 result[crypto_scalarmult_curve25519_BYTES]; + crypto_scalarmult_curve25519( + result, private_key_, + reinterpret_cast<const uint8*>(public_value.data())); + out_result->assign(reinterpret_cast<char*>(result), sizeof(result)); + + return true; +#else + return false; +#endif +} + +StringPiece Curve25519KeyExchange::public_value() const { + return StringPiece(reinterpret_cast<const char*>(public_key_), + sizeof(public_key_)); +} + +CryptoTag Curve25519KeyExchange::tag() const { + return kC255; +} + +} // namespace net diff --git a/net/quic/crypto/curve25519_key_exchange.h b/net/quic/crypto/curve25519_key_exchange.h new file mode 100644 index 0000000..7a05a36 --- /dev/null +++ b/net/quic/crypto/curve25519_key_exchange.h @@ -0,0 +1,51 @@ +// 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_CURVE25519_KEY_EXCHANGE_H_ +#define NET_QUIC_CRYPTO_CURVE25519_KEY_EXCHANGE_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "base/string_piece.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/key_exchange.h" + +namespace net { + +class QuicRandom; + +// Curve25519KeyExchange implements a KeyExchange using elliptic-curve +// Diffie-Hellman on curve25519. See http://cr.yp.to/ecdh.html +class NET_EXPORT_PRIVATE Curve25519KeyExchange : public KeyExchange { + public: + virtual ~Curve25519KeyExchange(); + + // New creates a new object from a private key. If the private key is + // invalid, NULL is returned. + static Curve25519KeyExchange* New(const base::StringPiece& private_key); + + // NewPrivateKey returns a private key, generated from |rand|, suitable for + // passing to |New|. + static std::string NewPrivateKey(QuicRandom* rand); + + // KeyExchange interface. + virtual bool CalculateSharedKey(const base::StringPiece& their_public_value, + std::string* shared_key) const OVERRIDE; + virtual base::StringPiece public_value() const OVERRIDE; + virtual CryptoTag tag() const OVERRIDE; + + private: + Curve25519KeyExchange(); + +// TODO(rtenneti): Add support for curve25519. +#if 0 + uint8 private_key_[32]; +#endif + uint8 public_key_[32]; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CURVE25519_KEY_EXCHANGE_H_ diff --git a/net/quic/crypto/key_exchange.h b/net/quic/crypto/key_exchange.h new file mode 100644 index 0000000..c1028ca --- /dev/null +++ b/net/quic/crypto/key_exchange.h @@ -0,0 +1,40 @@ +// 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_KEY_EXCHANGE_H_ +#define NET_QUIC_CRYPTO_KEY_EXCHANGE_H_ + +#include <string> + +#include "base/string_piece.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_protocol.h" + +namespace net { + +// KeyExchange is an abstract class that provides an interface to a +// key-exchange primitive. +class NET_EXPORT_PRIVATE KeyExchange { + public: + virtual ~KeyExchange() { } + + // CalculateSharedKey computes the shared key between the local private key + // (which is implicitly known by a KeyExchange object) and a public value + // from the peer. + virtual bool CalculateSharedKey(const base::StringPiece& peer_public_value, + std::string* shared_key) const = 0; + + // public_value returns the local public key which can be sent to a peer in + // order to complete a key exchange. The returned StringPiece is a reference + // to a member of the KeyExchange and is only valid for as long as the + // KeyExchange exists. + virtual base::StringPiece public_value() const = 0; + + // tag returns the tag value that identifies this key exchange function. + virtual CryptoTag tag() const = 0; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_KEY_EXCHANGE_H_ diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index 8f06abd..b9d6ebe 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -37,13 +37,12 @@ void QuicCryptoClientStream::OnHandshakeMessage( } bool QuicCryptoClientStream::CryptoConnect() { - crypto_config_.SetClientDefaults(); + crypto_config_.SetDefaults(); CryptoUtils::GenerateNonce(session()->connection()->clock(), session()->connection()->random_generator(), &nonce_); CryptoHandshakeMessage message; - CryptoUtils::FillClientHelloMessage(crypto_config_, nonce_, - server_hostname_, &message); + crypto_config_.FillClientHello(nonce_, server_hostname_, &message); SendHandshakeMessage(message); return true; } diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h index 1bdcb2a..2c0d7913 100644 --- a/net/quic/quic_crypto_client_stream.h +++ b/net/quic/quic_crypto_client_stream.h @@ -7,6 +7,7 @@ #include <string> +#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/quic_crypto_stream.h" namespace net { @@ -28,7 +29,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { bool CryptoConnect(); private: - QuicCryptoConfig crypto_config_; + QuicCryptoClientConfig crypto_config_; // Client's connection nonce (4-byte timestamp + 28 random bytes) std::string nonce_; // Server's hostname diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc index ea15c97..0f265a5 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -6,6 +6,7 @@ #include "base/stl_util.h" #include "net/quic/crypto/crypto_framer.h" +#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_utils.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" @@ -277,28 +278,27 @@ QuicPacket* ConstructClientHelloPacket(QuicGuid guid, const QuicClock* clock, QuicRandom* random_generator, const string& server_hostname) { - QuicCryptoConfig config; - config.SetClientDefaults(); + QuicCryptoClientConfig config; + config.SetDefaults(); string nonce; CryptoUtils::GenerateNonce(clock, random_generator, &nonce); CryptoHandshakeMessage message; - CryptoUtils::FillClientHelloMessage(config, nonce, server_hostname, - &message); + config.FillClientHello(nonce, server_hostname, &message); return ConstructPacketFromHandshakeMessage(guid, message); } QuicPacket* ConstructServerHelloPacket(QuicGuid guid, const QuicClock* clock, QuicRandom* random_generator) { - QuicCryptoNegotiatedParams negotiated_params; - negotiated_params.SetDefaults(); string nonce; CryptoUtils::GenerateNonce(clock, random_generator, &nonce); - CryptoHandshakeMessage message; - CryptoUtils::FillServerHelloMessage(negotiated_params, nonce, &message); - return ConstructPacketFromHandshakeMessage(guid, message); + CryptoHandshakeMessage dummy_client_hello, server_hello; + QuicCryptoServerConfig server_config; + server_config.AddTestingConfig(random_generator, clock); + server_config.ProcessClientHello(dummy_client_hello, nonce, &server_hello); + return ConstructPacketFromHandshakeMessage(guid, server_hello); } QuicPacketEntropyHash TestEntropyCalculator::ReceivedEntropyHash( |