diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-26 08:26:14 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-26 08:26:14 +0000 |
commit | ccc66e8acadcd816e3f9ca19ce34a64bcb83a48d (patch) | |
tree | 0dd228a4fb3666bd5b5d4400594bb4c1ebe0b20c /net | |
parent | e8e49bf53386d069ea5097370f881c41a0d703f7 (diff) | |
download | chromium_src-ccc66e8acadcd816e3f9ca19ce34a64bcb83a48d.zip chromium_src-ccc66e8acadcd816e3f9ca19ce34a64bcb83a48d.tar.gz chromium_src-ccc66e8acadcd816e3f9ca19ce34a64bcb83a48d.tar.bz2 |
Land Recent QUIC Changes
Removed the tab characters.
Fixed review comments from rch.
R=rch@chromium.org
QUIC - pending changelist - Use static for congestion manager constants.
Merger internal change: 43867480
R=rch@chromium.org
QUIC - cleanup of stats
Merge internal change: 43864412
R=rch@chromium.org
QUIC - Add two new methods to QuicConnectionHelper to abstarct away differences between server and chromium.
Merge internal change: 43850438
R=rch@chromium.org
QUIC - Start tracking number of rejected connections and connection close.
Merge internal change: 43846131
R=rch@chromium.org
QUIC - Temporary work around for bug with server tests.
Merge internal change: 43845688
R=rch@chromium.org
QUIC - Bugfix; don't queue ConnectionClosePackets and don't process incoming packets if the connection is closed.
Merge internal change: 43813759
R=rch@chromium.org
QUIC - More cleanups
Merge internal change: 43805954
R=rch@chromium.org
QUIC Crypto - Change CryptoHandshakeMessage to a class.
Merge internal change: 43798895
R=rch@chromium.org, wtc@chromium.org
QUIC crypto: cleanup between steps 4 and 5.
Merge internal change: 43791096
R=rch@chromium.org, agl@chromium.org
QUIC crypto: steps 3 and 4 from the plan.
Merge internal change: 43787806
R=rch@chromium.org, agl@chromium.org
QUIC - Improved sendalarm accuracy
Merge internal change: 43785455
R=rch@chromium.org
Review URL: https://chromiumcodereview.appspot.com/12863007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190594 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
36 files changed, 1081 insertions, 513 deletions
diff --git a/net/quic/congestion_control/quic_congestion_manager.cc b/net/quic/congestion_control/quic_congestion_manager.cc index 79b6c4d..d4dcdb3 100644 --- a/net/quic/congestion_control/quic_congestion_manager.cc +++ b/net/quic/congestion_control/quic_congestion_manager.cc @@ -12,13 +12,13 @@ #include "net/quic/congestion_control/send_algorithm_interface.h" namespace { -const int kBitrateSmoothingPeriodMs = 1000; -const int kMinBitrateSmoothingPeriodMs = 500; -const int kHistoryPeriodMs = 5000; +static const int kBitrateSmoothingPeriodMs = 1000; +static const int kMinBitrateSmoothingPeriodMs = 500; +static const int kHistoryPeriodMs = 5000; -const int kDefaultRetransmissionTimeMs = 500; -const size_t kMaxRetransmissions = 10; -const size_t kTailDropWindowSize = 5; +static const int kDefaultRetransmissionTimeMs = 500; +static const size_t kMaxRetransmissions = 10; +static const size_t kTailDropWindowSize = 5; COMPILE_ASSERT(kHistoryPeriodMs >= kBitrateSmoothingPeriodMs, history_must_be_longer_or_equal_to_the_smoothing_period); diff --git a/net/quic/crypto/crypto_framer.cc b/net/quic/crypto/crypto_framer.cc index b22dbb2..b0ff9ea 100644 --- a/net/quic/crypto/crypto_framer.cc +++ b/net/quic/crypto/crypto_framer.cc @@ -48,7 +48,6 @@ class OneShotVisitor : public CryptoFramerVisitorInterface { CryptoFramer::CryptoFramer() : visitor_(NULL), - message_tag_(0), num_entries_(0), values_len_(0) { Clear(); @@ -86,7 +85,9 @@ bool CryptoFramer::ProcessInput(StringPiece input) { if (reader.BytesRemaining() < kCryptoTagSize) { break; } - reader.ReadUInt32(&message_tag_); + CryptoTag message_tag; + reader.ReadUInt32(&message_tag); + message_.set_tag(message_tag); state_ = STATE_READING_NUM_ENTRIES; case STATE_READING_NUM_ENTRIES: if (reader.BytesRemaining() < kNumEntriesSize) { @@ -146,12 +147,9 @@ bool CryptoFramer::ProcessInput(StringPiece input) { for (int i = 0; i < num_entries_; ++i) { StringPiece value; reader.ReadStringPiece(&value, tag_length_map_[tags_[i]]); - tag_value_map_[tags_[i]] = value.as_string(); + message_.SetStringPiece(tags_[i], value); } - CryptoHandshakeMessage message; - message.tag = message_tag_; - message.tag_value_map.swap(tag_value_map_); - visitor_->OnHandshakeMessage(message); + visitor_->OnHandshakeMessage(message_); Clear(); state_ = STATE_READING_TAG; break; @@ -164,57 +162,57 @@ bool CryptoFramer::ProcessInput(StringPiece input) { // static QuicData* CryptoFramer::ConstructHandshakeMessage( const CryptoHandshakeMessage& message) { - if (message.tag_value_map.size() > kMaxEntries) { + if (message.tag_value_map().size() > kMaxEntries) { return NULL; } size_t len = sizeof(uint32); // message tag len += sizeof(uint16); // number of map entries - CryptoTagValueMap::const_iterator it = message.tag_value_map.begin(); - while (it != message.tag_value_map.end()) { + CryptoTagValueMap::const_iterator it = message.tag_value_map().begin(); + while (it != message.tag_value_map().end()) { len += sizeof(uint32); // tag len += sizeof(uint16); // value len len += it->second.length(); // value ++it; } - if (message.tag_value_map.size() % 2 == 1) { + if (message.tag_value_map().size() % 2 == 1) { len += sizeof(uint16); // padding } QuicDataWriter writer(len); - if (!writer.WriteUInt32(message.tag)) { + if (!writer.WriteUInt32(message.tag())) { DCHECK(false) << "Failed to write message tag."; return NULL; } - if (!writer.WriteUInt16(message.tag_value_map.size())) { + if (!writer.WriteUInt16(message.tag_value_map().size())) { DCHECK(false) << "Failed to write size."; return NULL; } // Tags - for (it = message.tag_value_map.begin(); - it != message.tag_value_map.end(); ++it) { + for (it = message.tag_value_map().begin(); + it != message.tag_value_map().end(); ++it) { if (!writer.WriteUInt32(it->first)) { DCHECK(false) << "Failed to write tag."; return NULL; } } // Lengths - for (it = message.tag_value_map.begin(); - it != message.tag_value_map.end(); ++it) { + for (it = message.tag_value_map().begin(); + it != message.tag_value_map().end(); ++it) { if (!writer.WriteUInt16(it->second.length())) { DCHECK(false) << "Failed to write length."; return NULL; } } // Possible padding - if (message.tag_value_map.size() % 2 == 1) { + if (message.tag_value_map().size() % 2 == 1) { if (!writer.WriteUInt16(0)) { DCHECK(false) << "Failed to write padding."; return NULL; } } // Values - for (it = message.tag_value_map.begin(); - it != message.tag_value_map.end(); ++it) { + for (it = message.tag_value_map().begin(); + it != message.tag_value_map().end(); ++it) { if (!writer.WriteBytes(it->second.data(), it->second.length())) { DCHECK(false) << "Failed to write value."; return NULL; @@ -224,7 +222,7 @@ QuicData* CryptoFramer::ConstructHandshakeMessage( } void CryptoFramer::Clear() { - tag_value_map_.clear(); + message_.Clear(); tag_length_map_.clear(); tags_.clear(); error_ = QUIC_NO_ERROR; diff --git a/net/quic/crypto/crypto_framer.h b/net/quic/crypto/crypto_framer.h index 1d289a6..4e3623b8 100644 --- a/net/quic/crypto/crypto_framer.h +++ b/net/quic/crypto/crypto_framer.h @@ -12,6 +12,7 @@ #include "base/memory/scoped_ptr.h" #include "base/string_piece.h" #include "net/base/net_export.h" +#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/quic_protocol.h" @@ -20,7 +21,6 @@ namespace net { class CryptoFramer; class QuicDataReader; class QuicData; -struct CryptoHandshakeMessage; class NET_EXPORT_PRIVATE CryptoFramerVisitorInterface { public: @@ -99,8 +99,8 @@ class NET_EXPORT_PRIVATE CryptoFramer { std::string buffer_; // Current state of the parsing. CryptoFramerState state_; - // Tag of the message currently being parsed. - CryptoTag message_tag_; + // The message currently being parsed. + CryptoHandshakeMessage message_; // Number of entires in the message currently being parsed. uint16 num_entries_; // Vector of tags in the message currently being parsed. @@ -108,8 +108,6 @@ class NET_EXPORT_PRIVATE CryptoFramer { // Length of the data associated with each tag in the message currently // being parsed. std::map<CryptoTag, size_t> tag_length_map_; - // Data associated with each tag in the message currently being parsed. - CryptoTagValueMap tag_value_map_; // Cumulative length of all values in the message currently being parsed. size_t values_len_; }; diff --git a/net/quic/crypto/crypto_framer_test.cc b/net/quic/crypto/crypto_framer_test.cc index 58c4f45..6c0419a 100644 --- a/net/quic/crypto/crypto_framer_test.cc +++ b/net/quic/crypto/crypto_framer_test.cc @@ -10,6 +10,7 @@ #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_test_utils.h" using base::StringPiece; @@ -51,8 +52,6 @@ class TestCryptoVisitor : public ::net::CryptoFramerVisitorInterface { vector<CryptoHandshakeMessage> messages_; }; -} // namespace test - TEST(CryptoFramerTest, MakeCryptoTag) { CryptoTag tag = MAKE_TAG('A', 'B', 'C', 'D'); char bytes[4]; @@ -65,10 +64,10 @@ TEST(CryptoFramerTest, MakeCryptoTag) { TEST(CryptoFramerTest, ConstructHandshakeMessage) { CryptoHandshakeMessage message; - message.tag = 0xFFAA7733; - message.tag_value_map[0x12345678] = "abcdef"; - message.tag_value_map[0x12345679] = "ghijk"; - message.tag_value_map[0x1234567A] = "lmnopqr"; + message.set_tag(0xFFAA7733); + message.SetStringPiece(0x12345678, "abcdef"); + message.SetStringPiece(0x12345679, "ghijk"); + message.SetStringPiece(0x1234567A, "lmnopqr"); unsigned char packet[] = { // tag @@ -110,9 +109,9 @@ TEST(CryptoFramerTest, ConstructHandshakeMessage) { TEST(CryptoFramerTest, ConstructHandshakeMessageWithTwoKeys) { CryptoHandshakeMessage message; - message.tag = 0xFFAA7733; - message.tag_value_map[0x12345678] = "abcdef"; - message.tag_value_map[0x12345679] = "ghijk"; + message.set_tag(0xFFAA7733); + message.SetStringPiece(0x12345678, "abcdef"); + message.SetStringPiece(0x12345679, "ghijk"); unsigned char packet[] = { // tag @@ -146,8 +145,8 @@ TEST(CryptoFramerTest, ConstructHandshakeMessageWithTwoKeys) { TEST(CryptoFramerTest, ConstructHandshakeMessageZeroLength) { CryptoHandshakeMessage message; - message.tag = 0xFFAA7733; - message.tag_value_map[0x12345678] = ""; + message.set_tag(0xFFAA7733); + message.SetStringPiece(0x12345678, ""); unsigned char packet[] = { // tag @@ -173,9 +172,9 @@ TEST(CryptoFramerTest, ConstructHandshakeMessageZeroLength) { TEST(CryptoFramerTest, ConstructHandshakeMessageTooManyEntries) { CryptoHandshakeMessage message; - message.tag = 0xFFAA7733; + message.set_tag(0xFFAA7733); for (uint32 key = 1; key <= kMaxEntries + 1; ++key) { - message.tag_value_map[key] = "abcdef"; + message.SetStringPiece(key, "abcdef"); } CryptoFramer framer; @@ -213,10 +212,11 @@ TEST(CryptoFramerTest, ProcessInput) { arraysize(input)))); EXPECT_EQ(0u, framer.InputBytesRemaining()); ASSERT_EQ(1u, visitor.messages_.size()); - EXPECT_EQ(0xFFAA7733, visitor.messages_[0].tag); - EXPECT_EQ(2u, visitor.messages_[0].tag_value_map.size()); - EXPECT_EQ("abcdef", visitor.messages_[0].tag_value_map[0x12345678]); - EXPECT_EQ("ghijk", visitor.messages_[0].tag_value_map[0x12345679]); + const CryptoHandshakeMessage& message = visitor.messages_[0]; + EXPECT_EQ(0xFFAA7733, message.tag()); + EXPECT_EQ(2u, message.tag_value_map().size()); + EXPECT_EQ("abcdef", CryptoTestUtils::GetValueForTag(message, 0x12345678)); + EXPECT_EQ("ghijk", CryptoTestUtils::GetValueForTag(message, 0x12345679)); } TEST(CryptoFramerTest, ProcessInputWithThreeKeys) { @@ -258,11 +258,12 @@ TEST(CryptoFramerTest, ProcessInputWithThreeKeys) { arraysize(input)))); EXPECT_EQ(0u, framer.InputBytesRemaining()); ASSERT_EQ(1u, visitor.messages_.size()); - EXPECT_EQ(0xFFAA7733, visitor.messages_[0].tag); - EXPECT_EQ(3u, visitor.messages_[0].tag_value_map.size()); - EXPECT_EQ("abcdef", visitor.messages_[0].tag_value_map[0x12345678]); - EXPECT_EQ("ghijk", visitor.messages_[0].tag_value_map[0x12345679]); - EXPECT_EQ("lmnopqr", visitor.messages_[0].tag_value_map[0x1234567A]); + const CryptoHandshakeMessage& message = visitor.messages_[0]; + EXPECT_EQ(0xFFAA7733, message.tag()); + EXPECT_EQ(3u, message.tag_value_map().size()); + EXPECT_EQ("abcdef", CryptoTestUtils::GetValueForTag(message, 0x12345678)); + EXPECT_EQ("ghijk", CryptoTestUtils::GetValueForTag(message, 0x12345679)); + EXPECT_EQ("lmnopqr", CryptoTestUtils::GetValueForTag(message, 0x1234567A)); } TEST(CryptoFramerTest, ProcessInputIncrementally) { @@ -296,10 +297,11 @@ TEST(CryptoFramerTest, ProcessInputIncrementally) { } EXPECT_EQ(0u, framer.InputBytesRemaining()); ASSERT_EQ(1u, visitor.messages_.size()); - EXPECT_EQ(0xFFAA7733, visitor.messages_[0].tag); - EXPECT_EQ(2u, visitor.messages_[0].tag_value_map.size()); - EXPECT_EQ("abcdef", visitor.messages_[0].tag_value_map[0x12345678]); - EXPECT_EQ("ghijk", visitor.messages_[0].tag_value_map[0x12345679]); + const CryptoHandshakeMessage& message = visitor.messages_[0]; + EXPECT_EQ(0xFFAA7733, message.tag()); + EXPECT_EQ(2u, message.tag_value_map().size()); + EXPECT_EQ("abcdef", CryptoTestUtils::GetValueForTag(message, 0x12345678)); + EXPECT_EQ("ghijk", CryptoTestUtils::GetValueForTag(message, 0x12345679)); } TEST(CryptoFramerTest, ProcessInputTagsOutOfOrder) { @@ -387,4 +389,6 @@ TEST(CryptoFramerTest, ProcessInputInvalidLengthPadding) { EXPECT_EQ(QUIC_CRYPTO_INVALID_VALUE_LENGTH, framer.error()); } +} // namespace test + } // namespace net diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc index 9d8c48f..f25133d 100644 --- a/net/quic/crypto/crypto_handshake.cc +++ b/net/quic/crypto/crypto_handshake.cc @@ -4,8 +4,11 @@ #include "net/quic/crypto/crypto_handshake.h" +#include <ctype.h> + #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" #include "crypto/hkdf.h" #include "crypto/secure_hash.h" #include "net/base/net_util.h" @@ -13,6 +16,7 @@ #include "net/quic/crypto/crypto_utils.h" #include "net/quic/crypto/curve25519_key_exchange.h" #include "net/quic/crypto/key_exchange.h" +#include "net/quic/crypto/p256_key_exchange.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" @@ -20,14 +24,18 @@ using base::StringPiece; using crypto::SecureHash; +using std::map; using std::string; using std::vector; namespace net { + // kVersion contains the one (and, for the moment, only) version number that we // implement. static const uint16 kVersion = 0; +// kLabel is constant that is used in key derivation to tie the resulting key +// to this protocol. static const char kLabel[] = "QUIC key expansion"; using crypto::SecureHash; @@ -39,12 +47,12 @@ QuicServerConfigProtobuf::~QuicServerConfigProtobuf() { STLDeleteElements(&keys_); } -CryptoHandshakeMessage::CryptoHandshakeMessage() {} +CryptoHandshakeMessage::CryptoHandshakeMessage() : tag_(0) {} CryptoHandshakeMessage::CryptoHandshakeMessage( const CryptoHandshakeMessage& other) - : tag(other.tag), - tag_value_map(other.tag_value_map) { + : tag_(other.tag_), + tag_value_map_(other.tag_value_map_) { // Don't copy serialized_. scoped_ptr doesn't have a copy constructor. // The new object can reconstruct serialized_ lazily. } @@ -53,14 +61,20 @@ CryptoHandshakeMessage::~CryptoHandshakeMessage() {} CryptoHandshakeMessage& CryptoHandshakeMessage::operator=( const CryptoHandshakeMessage& other) { - tag = other.tag; - tag_value_map = other.tag_value_map; + tag_ = other.tag_; + tag_value_map_ = other.tag_value_map_; // Don't copy serialized_. scoped_ptr doesn't have an assignment operator. // However, invalidate serialized_. serialized_.reset(); return *this; } +void CryptoHandshakeMessage::Clear() { + tag_ = 0; + tag_value_map_.clear(); + serialized_.reset(); +} + const QuicData& CryptoHandshakeMessage::GetSerialized() const { if (!serialized_.get()) { serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this)); @@ -68,6 +82,11 @@ const QuicData& CryptoHandshakeMessage::GetSerialized() const { return *serialized_.get(); } +void CryptoHandshakeMessage::Insert(CryptoTagValueMap::const_iterator begin, + CryptoTagValueMap::const_iterator end) { + tag_value_map_.insert(begin, end); +} + 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. @@ -79,11 +98,11 @@ void CryptoHandshakeMessage::SetTaglist(CryptoTag tag, ...) { va_start(ap, tag); for (;;) { - CryptoTag tag = va_arg(ap, CryptoTag); - if (tag == 0) { + CryptoTag list_item = va_arg(ap, CryptoTag); + if (list_item == 0) { break; } - tags.push_back(tag); + tags.push_back(list_item); } // Because of the way that we keep tags in memory, we can copy the contents @@ -94,13 +113,18 @@ void CryptoHandshakeMessage::SetTaglist(CryptoTag tag, ...) { va_end(ap); } +void CryptoHandshakeMessage::SetStringPiece(CryptoTag tag, + StringPiece value) { + tag_value_map_[tag] = value.as_string(); +} + QuicErrorCode CryptoHandshakeMessage::GetTaglist(CryptoTag tag, const CryptoTag** out_tags, size_t* out_len) const { - CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); + CryptoTagValueMap::const_iterator it = tag_value_map_.find(tag); QuicErrorCode ret = QUIC_NO_ERROR; - if (it == tag_value_map.end()) { + 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; @@ -119,18 +143,17 @@ QuicErrorCode CryptoHandshakeMessage::GetTaglist(CryptoTag tag, bool CryptoHandshakeMessage::GetStringPiece(CryptoTag tag, StringPiece* out) const { - CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); - if (it == tag_value_map.end()) { + CryptoTagValueMap::const_iterator it = tag_value_map_.find(tag); + if (it == tag_value_map_.end()) { return false; } *out = it->second; return true; } -QuicErrorCode CryptoHandshakeMessage::GetNthValue16( - CryptoTag tag, - unsigned index, - StringPiece* out) const { +QuicErrorCode CryptoHandshakeMessage::GetNthValue16(CryptoTag tag, + unsigned index, + StringPiece* out) const { StringPiece value; if (!GetStringPiece(tag, &value)) { return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; @@ -164,8 +187,8 @@ QuicErrorCode CryptoHandshakeMessage::GetNthValue16( } bool CryptoHandshakeMessage::GetString(CryptoTag tag, string* out) const { - CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); - if (it == tag_value_map.end()) { + CryptoTagValueMap::const_iterator it = tag_value_map_.find(tag); + if (it == tag_value_map_.end()) { return false; } *out = it->second; @@ -182,12 +205,16 @@ QuicErrorCode CryptoHandshakeMessage::GetUint32(CryptoTag tag, return GetPOD(tag, out, sizeof(uint32)); } +string CryptoHandshakeMessage::DebugString() const { + return DebugStringInternal(0); +} + QuicErrorCode CryptoHandshakeMessage::GetPOD( CryptoTag tag, void* out, size_t len) const { - CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); + CryptoTagValueMap::const_iterator it = tag_value_map_.find(tag); QuicErrorCode ret = QUIC_NO_ERROR; - if (it == tag_value_map.end()) { + if (it == tag_value_map_.end()) { ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; } else if (it->second.size() != len) { ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; @@ -202,207 +229,388 @@ QuicErrorCode CryptoHandshakeMessage::GetPOD( return ret; } -QuicCryptoNegotiatedParams::QuicCryptoNegotiatedParams() +// TagToString is a utility function for pretty-printing handshake messages +// that converts a tag to a string. It will try to maintain the human friendly +// name if possible (i.e. kABCD -> "ABCD"), or will just treat it as a number +// if not. +static string TagToString(CryptoTag tag) { + char chars[4]; + bool ascii = true; + const CryptoTag orig_tag = tag; + + for (size_t i = 0; i < sizeof(chars); i++) { + chars[i] = tag; + if (chars[i] == 0 && i == 3) { + chars[i] = ' '; + } + if (!isprint(static_cast<unsigned char>(chars[i]))) { + ascii = false; + break; + } + tag >>= 8; + } + + if (ascii) { + return string(chars, sizeof(chars)); + } + + return base::UintToString(orig_tag); +} + +string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { + string ret = string(2 * indent, ' ') + TagToString(tag_) + "<\n"; + ++indent; + for (CryptoTagValueMap::const_iterator it = tag_value_map_.begin(); + it != tag_value_map_.end(); ++it) { + ret += string(2 * indent, ' ') + TagToString(it->first) + ": "; + + bool done = false; + switch (it->first) { + case kKATO: + case kVERS: + // uint32 value + if (it->second.size() == 4) { + uint32 value; + memcpy(&value, it->second.data(), sizeof(value)); + ret += base::UintToString(value); + done = true; + } + break; + case kKEXS: + case kAEAD: + case kCGST: + // tag lists + if (it->second.size() % sizeof(CryptoTag) == 0) { + for (size_t j = 0; j < it->second.size(); j += sizeof(CryptoTag)) { + CryptoTag tag; + memcpy(&tag, it->second.data() + j, sizeof(tag)); + if (j > 0) { + ret += ","; + } + ret += TagToString(tag); + } + done = true; + } + break; + case kSCFG: + // nested messages. + if (!it->second.empty()) { + scoped_ptr<CryptoHandshakeMessage> msg( + CryptoFramer::ParseMessage(it->second)); + if (msg.get()) { + ret += "\n"; + ret += msg->DebugStringInternal(indent + 1); + + done = true; + } + } + break; + } + + if (!done) { + // If there's no specific format for this tag, or the value is invalid, + // then just use hex. + ret += base::HexEncode(it->second.data(), it->second.size()); + } + ret += "\n"; + } + --indent; + ret += string(2 * indent, ' ') + ">"; + return ret; +} + + +QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters() : version(0), key_exchange(0), aead(0) { } -QuicCryptoNegotiatedParams::~QuicCryptoNegotiatedParams() { +QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() { } + QuicCryptoConfig::QuicCryptoConfig() : version(0) { } QuicCryptoConfig::~QuicCryptoConfig() { - STLDeleteElements(&key_exchanges); } -bool QuicCryptoConfig::ProcessPeerHandshake( - const CryptoHandshakeMessage& peer_msg, - CryptoUtils::Priority priority, - QuicCryptoNegotiatedParams* out_params, - string* error_details) const { - if (peer_msg.GetUint16(kVERS, &out_params->version) != QUIC_NO_ERROR || + +QuicCryptoClientConfig::QuicCryptoClientConfig() { +} + +QuicCryptoClientConfig::~QuicCryptoClientConfig() { + STLDeleteValues(&cached_states_); +} + +QuicCryptoClientConfig::CachedState::CachedState() { +} + +QuicCryptoClientConfig::CachedState::~CachedState() { +} + +bool QuicCryptoClientConfig::CachedState::is_complete() const { + return !server_config_.empty(); +} + +const CryptoHandshakeMessage* +QuicCryptoClientConfig::CachedState::GetServerConfig() const { + if (server_config_.empty()) { + return NULL; + } + + if (!scfg_.get()) { + scfg_.reset(CryptoFramer::ParseMessage(server_config_)); + DCHECK(scfg_.get()); + } + return scfg_.get(); +} + +bool QuicCryptoClientConfig::CachedState::SetServerConfig( + StringPiece scfg) { + scfg_.reset(CryptoFramer::ParseMessage(scfg)); + if (!scfg_.get()) { + return false; + } + server_config_ = scfg.as_string(); + return true; +} + +const string& QuicCryptoClientConfig::CachedState::server_config() + const { + return server_config_; +} + +const string& QuicCryptoClientConfig::CachedState::source_address_token() + const { + return source_address_token_; +} + +const string& QuicCryptoClientConfig::CachedState::orbit() const { + return orbit_; +} + +void QuicCryptoClientConfig::SetDefaults() { + // Version must be 0. + version = kVersion; + + // Key exchange methods. + kexs.resize(1); + kexs[0] = kC255; + + // Authenticated encryption algorithms. + aead.resize(1); + aead[0] = kAESG; +} + +const QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::Lookup( + const string& server_hostname) { + map<string, CachedState*>::const_iterator it = + cached_states_.find(server_hostname); + if (it == cached_states_.end()) { + return NULL; + } + return it->second; +} + +void QuicCryptoClientConfig::FillInchoateClientHello( + const string& server_hostname, + const CachedState* cached, + CryptoHandshakeMessage* out) { + out->set_tag(kCHLO); + + // Server name indication. + // If server_hostname is not an IP address literal, it is a DNS hostname. + IPAddressNumber ip; + if (!server_hostname.empty() && + !ParseIPLiteralToNumber(server_hostname, &ip)) { + out->SetStringPiece(kSNI, server_hostname); + } + out->SetValue(kVERS, version); + + if (cached && !cached->source_address_token().empty()) { + out->SetValue(kSRCT, cached->source_address_token()); + } +} + +QuicErrorCode QuicCryptoClientConfig::FillClientHello( + const string& server_hostname, + QuicGuid guid, + const CachedState* cached, + const QuicClock* clock, + QuicRandom* rand, + QuicCryptoNegotiatedParameters* out_params, + CryptoHandshakeMessage* out, + string* error_details) { + FillInchoateClientHello(server_hostname, cached, out); + + const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); + if (!scfg) { + // This should never happen as our caller should have checked + // cached->is_complete() before calling this function. + if (error_details) { + *error_details = "Handshake not ready"; + } + return QUIC_CRYPTO_INTERNAL_ERROR; + } + + StringPiece scid; + if (!scfg->GetStringPiece(kSCID, &scid)) { + if (error_details) { + *error_details = "SCFG missing SCID"; + } + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + out->SetStringPiece(kSCID, scid); + + // Calculate the mutual algorithms that the connection is going to use. + if (scfg->GetUint16(kVERS, &out_params->version) != QUIC_NO_ERROR || out_params->version != kVersion) { if (error_details) { *error_details = "Bad version"; } - return false; + return QUIC_VERSION_NOT_SUPPORTED; } const CryptoTag* their_aeads; const CryptoTag* their_key_exchanges; size_t num_their_aeads, num_their_key_exchanges; - if (peer_msg.GetTaglist(kAEAD, &their_aeads, - &num_their_aeads) != QUIC_NO_ERROR || - peer_msg.GetTaglist(kKEXS, &their_key_exchanges, - &num_their_key_exchanges) != QUIC_NO_ERROR) { + if (scfg->GetTaglist(kAEAD, &their_aeads, + &num_their_aeads) != QUIC_NO_ERROR || + scfg->GetTaglist(kKEXS, &their_key_exchanges, + &num_their_key_exchanges) != QUIC_NO_ERROR) { if (error_details) { *error_details = "Missing AEAD or KEXS"; } - return false; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } size_t key_exchange_index; if (!CryptoUtils::FindMutualTag(aead, their_aeads, num_their_aeads, - priority, + CryptoUtils::PEER_PRIORITY, &out_params->aead, NULL) || !CryptoUtils::FindMutualTag(kexs, their_key_exchanges, num_their_key_exchanges, - priority, + CryptoUtils::PEER_PRIORITY, &out_params->key_exchange, &key_exchange_index)) { if (error_details) { *error_details = "Unsupported AEAD or KEXS"; } - return false; + return QUIC_CRYPTO_NO_SUPPORT; } + out->SetTaglist(kAEAD, out_params->aead, 0); + out->SetTaglist(kKEXS, out_params->key_exchange, 0); StringPiece public_value; - if (peer_msg.GetNthValue16(kPUBS, key_exchange_index, &public_value) != + if (scfg->GetNthValue16(kPUBS, key_exchange_index, &public_value) != QUIC_NO_ERROR) { if (error_details) { *error_details = "Missing public value"; } - return false; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } - KeyExchange* key_exchange = NULL; - for (vector<KeyExchange*>::const_iterator i = key_exchanges.begin(); - i != key_exchanges.end(); ++i) { - if ((*i)->tag() == out_params->key_exchange) { - key_exchange = *i; - break; + string nonce; + CryptoUtils::GenerateNonce(clock, rand, cached->orbit(), &nonce); + out->SetStringPiece(kNONC, nonce); + + scoped_ptr<KeyExchange> key_exchange; + switch (out_params->key_exchange) { + case kC255: + key_exchange.reset(Curve25519KeyExchange::New( + Curve25519KeyExchange::NewPrivateKey(rand))); + break; + case kP256: + key_exchange.reset(P256KeyExchange::New( + Curve25519KeyExchange::NewPrivateKey(rand))); + break; + default: + DCHECK(false); + if (error_details) { + *error_details = "Configured to support an unknown key exchange"; } + return QUIC_CRYPTO_INTERNAL_ERROR; } - DCHECK(key_exchange != NULL); if (!key_exchange->CalculateSharedKey(public_value, &out_params->premaster_secret)) { if (error_details) { *error_details = "Key exchange failure"; } - return false; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } + out->SetStringPiece(kPUBS, key_exchange->public_value()); - return true; -} - -QuicCryptoClientConfig::QuicCryptoClientConfig() - : hkdf_info(kLabel, arraysize(kLabel)) { -} + string hkdf_input(kLabel, arraysize(kLabel)); + hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid)); -void QuicCryptoClientConfig::SetDefaults(QuicRandom* rand) { - // Version must be 0. - version = kVersion; + const QuicData& client_hello_serialized = out->GetSerialized(); + hkdf_input.append(client_hello_serialized.data(), + client_hello_serialized.length()); + hkdf_input.append(cached->server_config()); - // Key exchange methods. - const string private_key = Curve25519KeyExchange::NewPrivateKey(rand); - key_exchanges.clear(); - key_exchanges.push_back(Curve25519KeyExchange::New(private_key)); - kexs.resize(1); - kexs[0] = kC255; + CryptoUtils::DeriveKeys(out_params, nonce, hkdf_input, CryptoUtils::CLIENT); - // Authenticated encryption algorithms. - aead.resize(1); - aead[0] = kAESG; + return QUIC_NO_ERROR; } -void QuicCryptoClientConfig::FillClientHello(const string& nonce, - const string& server_hostname, - CryptoHandshakeMessage* out) { - out->tag = kCHLO; +QuicErrorCode QuicCryptoClientConfig::ProcessRejection( + const string& server_hostname, + const CryptoHandshakeMessage& rej, + string* error_details) { + CachedState* cached; - out->SetValue(kVERS, version); - out->SetVector(kKEXS, kexs); - out->SetVector(kAEAD, aead); - out->tag_value_map[kNONC] = nonce; - - // Build the public values tag. - size_t pubs_len = 0; - for (vector<KeyExchange*>::const_iterator i = key_exchanges.begin(); - i != key_exchanges.end(); ++i) { - pubs_len += 2 /* length bytes */; - pubs_len += (*i)->public_value().size(); - } - - size_t pubs_offset = 0; - scoped_ptr<uint8[]> pubs(new uint8[pubs_len]); - for (vector<KeyExchange*>::const_iterator i = key_exchanges.begin(); - i != key_exchanges.end(); ++i) { - StringPiece pub = (*i)->public_value(); - pubs[pubs_offset++] = static_cast<uint8>(pub.size()); - pubs[pubs_offset++] = static_cast<uint8>(pub.size() >> 8); - memcpy(&pubs[pubs_offset], pub.data(), pub.size()); - pubs_offset += pub.size(); - } - out->tag_value_map[kPUBS] = - string(reinterpret_cast<char*>(pubs.get()), pubs_len); + map<string, CachedState*>::const_iterator it = + cached_states_.find(server_hostname); + if (it == cached_states_.end()) { + cached = new CachedState; + cached_states_[server_hostname] = cached; + } else { + cached = it->second; + } - // Server name indication. - // If server_hostname is not an IP address literal, it is a DNS hostname. - IPAddressNumber ip; - if (!server_hostname.empty() && - !ParseIPLiteralToNumber(server_hostname, &ip)) { - out->tag_value_map[kSNI] = server_hostname; + StringPiece scfg; + if (!rej.GetStringPiece(kSCFG, &scfg)) { + if (error_details) { + *error_details = "Missing SCFG"; + } + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + + if (!cached->SetServerConfig(scfg)) { + if (error_details) { + *error_details = "Invalid SCFG"; + } + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } + + return QUIC_NO_ERROR; } QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( const CryptoHandshakeMessage& server_hello, const string& nonce, - QuicCryptoNegotiatedParams* out_params, + QuicCryptoNegotiatedParameters* out_params, string* error_details) { - if (server_hello.tag != kSHLO) { + if (server_hello.tag() != kSHLO) { *error_details = "Bad tag"; return QUIC_INVALID_CRYPTO_MESSAGE_TYPE; } - StringPiece scfg_bytes; - if (!server_hello.GetStringPiece(kSCFG, &scfg_bytes)) { - *error_details = "Missing SCFG"; - return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; - } - - scoped_ptr<CryptoHandshakeMessage> scfg( - CryptoFramer::ParseMessage(scfg_bytes)); - if (!scfg.get() || scfg->tag != kSCFG) { - *error_details = "Invalid SCFG"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (!ProcessPeerHandshake(*scfg, CryptoUtils::PEER_PRIORITY, out_params, - error_details)) { - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - hkdf_info.append(scfg_bytes.data(), scfg_bytes.size()); - - out_params->encrypter.reset(QuicEncrypter::Create(out_params->aead)); - out_params->decrypter.reset(QuicDecrypter::Create(out_params->aead)); - size_t key_bytes = out_params->encrypter->GetKeySize(); - size_t nonce_prefix_bytes = out_params->encrypter->GetNoncePrefixSize(); - uint32 key_length_in_bits = 8 * 2 * (key_bytes + nonce_prefix_bytes); - hkdf_info.append(reinterpret_cast<char*>(&key_length_in_bits), - sizeof(key_length_in_bits)); - - crypto::HKDF hkdf(out_params->premaster_secret, nonce, - hkdf_info, key_bytes, nonce_prefix_bytes); - out_params->encrypter->SetKey(hkdf.client_write_key()); - out_params->encrypter->SetNoncePrefix(hkdf.client_write_iv()); - out_params->decrypter->SetKey(hkdf.server_write_key()); - out_params->decrypter->SetNoncePrefix(hkdf.server_write_iv()); + // TODO(agl): + // learn about updated SCFGs. + // read ephemeral public value for forward-secret keys. return QUIC_NO_ERROR; } -QuicCryptoServerConfig::QuicCryptoServerConfig() - : hkdf_info(kLabel, arraysize(kLabel)) { +QuicCryptoServerConfig::QuicCryptoServerConfig() { } QuicCryptoServerConfig::~QuicCryptoServerConfig() { @@ -419,20 +627,24 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::ConfigForTesting( const string private_key = Curve25519KeyExchange::NewPrivateKey(rand); scoped_ptr<Curve25519KeyExchange> curve25519( Curve25519KeyExchange::New(private_key)); - const string public_value = curve25519->public_value().as_string(); - - char len_bytes[2]; - len_bytes[0] = public_value.size(); - len_bytes[1] = public_value.size() >> 8; - - msg.tag = kSCFG; + StringPiece public_value = curve25519->public_value(); + string encoded_public_value; + // First two bytes encode the length of the public value. + encoded_public_value.push_back(public_value.size()); + encoded_public_value.push_back(public_value.size() >> 8); + encoded_public_value.append(public_value.data(), public_value.size()); + + msg.set_tag(kSCFG); msg.SetTaglist(kKEXS, kC255, 0); msg.SetTaglist(kAEAD, kAESG, 0); msg.SetValue(kVERS, static_cast<uint16>(0)); - msg.tag_value_map[kPUBS] = - string(len_bytes, sizeof(len_bytes)) + public_value; - msg.tag_value_map.insert(extra_tags.tag_value_map.begin(), - extra_tags.tag_value_map.end()); + msg.SetStringPiece(kPUBS, encoded_public_value); + msg.Insert(extra_tags.tag_value_map().begin(), + extra_tags.tag_value_map().end()); + + char scid_bytes[16]; + rand->RandBytes(scid_bytes, sizeof(scid_bytes)); + msg.SetStringPiece(kSCID, StringPiece(scid_bytes, sizeof(scid_bytes))); scoped_ptr<QuicData> serialized( CryptoFramer::ConstructHandshakeMessage(msg)); @@ -446,7 +658,7 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::ConfigForTesting( return config.release(); } -CryptoTagValueMap* QuicCryptoServerConfig::AddConfig( +CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( QuicServerConfigProtobuf* protobuf) { scoped_ptr<CryptoHandshakeMessage> msg( CryptoFramer::ParseMessage(protobuf->config())); @@ -455,9 +667,9 @@ CryptoTagValueMap* QuicCryptoServerConfig::AddConfig( LOG(WARNING) << "Failed to parse server config message"; return NULL; } - if (msg->tag != kSCFG) { + if (msg->tag() != kSCFG) { LOG(WARNING) << "Server config message has tag " - << msg->tag << " expected " + << msg->tag() << " expected " << kSCFG; return NULL; } @@ -465,6 +677,13 @@ CryptoTagValueMap* QuicCryptoServerConfig::AddConfig( scoped_ptr<Config> config(new Config); config->serialized = protobuf->config(); + StringPiece scid; + if (!msg->GetStringPiece(kSCID, &scid)) { + LOG(WARNING) << "Server config message is missing SCID"; + return NULL; + } + config->id = scid.as_string(); + const CryptoTag* aead_tags; size_t aead_len; if (msg->GetTaglist(kAEAD, &aead_tags, &aead_len) != QUIC_NO_ERROR) { @@ -557,10 +776,10 @@ CryptoTagValueMap* QuicCryptoServerConfig::AddConfig( configs_[id] = config.release(); active_config_ = id; - return new CryptoTagValueMap(msg->tag_value_map); + return msg.release(); } -CryptoTagValueMap* QuicCryptoServerConfig::AddTestingConfig( +CryptoHandshakeMessage* QuicCryptoServerConfig::AddTestingConfig( QuicRandom* rand, const QuicClock* clock, const CryptoHandshakeMessage& extra_tags) { @@ -569,62 +788,106 @@ CryptoTagValueMap* QuicCryptoServerConfig::AddTestingConfig( return AddConfig(config.get()); } -bool QuicCryptoServerConfig::ProcessClientHello( +QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( const CryptoHandshakeMessage& client_hello, - const string& nonce, + QuicGuid guid, CryptoHandshakeMessage* out, - QuicCryptoNegotiatedParams *out_params, + QuicCryptoNegotiatedParameters *out_params, string* error_details) { CHECK(!configs_.empty()); // FIXME(agl): we should use the client's SCID, not just the active config. const Config* config(configs_[active_config_]); - out->tag = kREJ; - out->tag_value_map.clear(); + StringPiece scid; + if (!client_hello.GetStringPiece(kSCID, &scid) || + scid.as_string() != config->id) { + // If the client didn't provide a server config ID, or gave the wrong one, + // then the handshake cannot possibly complete. We reject the handshake and + // give the client enough information to do better next time. + out->Clear(); + out->set_tag(kREJ); + out->SetStringPiece(kSCFG, config->serialized); + return QUIC_NO_ERROR; + } - if (!config->ProcessPeerHandshake(client_hello, - CryptoUtils::LOCAL_PRIORITY, - out_params, - error_details)) { - return false; + const CryptoTag* their_aeads; + const CryptoTag* their_key_exchanges; + size_t num_their_aeads, num_their_key_exchanges; + if (client_hello.GetTaglist(kAEAD, &their_aeads, + &num_their_aeads) != QUIC_NO_ERROR || + client_hello.GetTaglist(kKEXS, &their_key_exchanges, + &num_their_key_exchanges) != QUIC_NO_ERROR || + num_their_aeads != 1 || + num_their_key_exchanges != 1) { + if (error_details) { + *error_details = "Missing or invalid AEAD or KEXS"; + } + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + size_t key_exchange_index; + if (!CryptoUtils::FindMutualTag(config->aead, + their_aeads, num_their_aeads, + CryptoUtils::LOCAL_PRIORITY, + &out_params->aead, + NULL) || + !CryptoUtils::FindMutualTag(config->kexs, + their_key_exchanges, num_their_key_exchanges, + CryptoUtils::LOCAL_PRIORITY, + &out_params->key_exchange, + &key_exchange_index)) { + if (error_details) { + *error_details = "Unsupported AEAD or KEXS"; + } + return QUIC_CRYPTO_NO_SUPPORT; + } + + StringPiece public_value; + if (!client_hello.GetStringPiece(kPUBS, &public_value)) { + if (error_details) { + *error_details = "Missing public value"; + } + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (!config->key_exchanges[key_exchange_index]->CalculateSharedKey( + public_value, &out_params->premaster_secret)) { + if (error_details) { + *error_details = "Invalid public value"; + } + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } + out_params->server_config_id = scid.as_string(); + StringPiece client_nonce; if (!client_hello.GetStringPiece(kNONC, &client_nonce)) { - return false; + if (error_details) { + *error_details = "Invalid nonce"; + } + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } + string hkdf_input(kLabel, arraysize(kLabel)); + hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid)); + const QuicData& client_hello_serialized = client_hello.GetSerialized(); - hkdf_info.append(client_hello_serialized.data(), - client_hello_serialized.length()); - hkdf_info.append(config->serialized); - - out_params->encrypter.reset(QuicEncrypter::Create(out_params->aead)); - out_params->decrypter.reset(QuicDecrypter::Create(out_params->aead)); - size_t key_bytes = out_params->encrypter->GetKeySize(); - size_t nonce_prefix_bytes = out_params->encrypter->GetNoncePrefixSize(); - uint32 key_length_in_bits = 8 * 2 * (key_bytes + nonce_prefix_bytes); - hkdf_info.append(reinterpret_cast<char*>(&key_length_in_bits), - sizeof(key_length_in_bits)); - - crypto::HKDF hkdf(out_params->premaster_secret, client_nonce, - hkdf_info, key_bytes, nonce_prefix_bytes); - out_params->encrypter->SetKey(hkdf.server_write_key()); - out_params->encrypter->SetNoncePrefix(hkdf.server_write_iv()); - out_params->decrypter->SetKey(hkdf.client_write_key()); - out_params->decrypter->SetNoncePrefix(hkdf.client_write_iv()); - - // 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; + hkdf_input.append(client_hello_serialized.data(), + client_hello_serialized.length()); + hkdf_input.append(config->serialized); + + CryptoUtils::DeriveKeys(out_params, client_nonce, hkdf_input, + CryptoUtils::SERVER); + + out->set_tag(kSHLO); + return QUIC_NO_ERROR; } QuicCryptoServerConfig::Config::Config() { } QuicCryptoServerConfig::Config::~Config() { + STLDeleteElements(&key_exchanges); } } // namespace net diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h index 92f8f9b..fe2565e 100644 --- a/net/quic/crypto/crypto_handshake.h +++ b/net/quic/crypto/crypto_handshake.h @@ -13,7 +13,6 @@ #include "base/string_piece.h" #include "net/base/net_export.h" #include "net/quic/crypto/crypto_protocol.h" -#include "net/quic/crypto/crypto_utils.h" namespace net { @@ -25,13 +24,17 @@ class QuicClock; // An intermediate format of a handshake message that's convenient for a // CryptoFramer to serialize from or parse into. -struct NET_EXPORT_PRIVATE CryptoHandshakeMessage { +class NET_EXPORT_PRIVATE CryptoHandshakeMessage { + public: CryptoHandshakeMessage(); CryptoHandshakeMessage(const CryptoHandshakeMessage& other); ~CryptoHandshakeMessage(); CryptoHandshakeMessage& operator=(const CryptoHandshakeMessage& other); + // Clears state. + void Clear(); + // GetSerialized returns the serialized form of this message and caches the // result. Subsequently altering the message does not invalidate the cache. const QuicData& GetSerialized() const; @@ -39,25 +42,37 @@ struct NET_EXPORT_PRIVATE 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)); + 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(); + tag_value_map_[tag] = std::string(); } else { - tag_value_map[tag] = std::string(reinterpret_cast<const char*>(&v[0]), - v.size() * sizeof(T)); + tag_value_map_[tag] = std::string(reinterpret_cast<const char*>(&v[0]), + v.size() * sizeof(T)); } } + // Returns the message tag. + CryptoTag tag() const { return tag_; } + // Sets the message tag. + void set_tag(CryptoTag tag) { tag_ = tag; } + + const CryptoTagValueMap& tag_value_map() const { return tag_value_map_; } + + void Insert(CryptoTagValueMap::const_iterator begin, + CryptoTagValueMap::const_iterator end); + // 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, ...); + void SetStringPiece(CryptoTag tag, base::StringPiece value); + // 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. @@ -78,8 +93,9 @@ struct NET_EXPORT_PRIVATE CryptoHandshakeMessage { QuicErrorCode GetUint16(CryptoTag tag, uint16* out) const; QuicErrorCode GetUint32(CryptoTag tag, uint32* out) const; - CryptoTag tag; - CryptoTagValueMap tag_value_map; + // DebugString returns a multi-line, string representation of the message + // suitable for including in debug output. + std::string DebugString() const; private: // GetPOD is a utility function for extracting a plain-old-data value. If @@ -91,6 +107,11 @@ struct NET_EXPORT_PRIVATE CryptoHandshakeMessage { // little-endian. QuicErrorCode GetPOD(CryptoTag tag, void* out, size_t len) const; + std::string DebugStringInternal(size_t indent) const; + + CryptoTag tag_; + CryptoTagValueMap tag_value_map_; + // The serialized form of the handshake message. This member is constructed // lasily. mutable scoped_ptr<QuicData> serialized_; @@ -150,10 +171,10 @@ class NET_EXPORT_PRIVATE QuicServerConfigProtobuf { }; // Parameters negotiated by the crypto handshake. -struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParams { +struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters { // Initializes the members to 0 or empty values. - QuicCryptoNegotiatedParams(); - ~QuicCryptoNegotiatedParams(); + QuicCryptoNegotiatedParameters(); + ~QuicCryptoNegotiatedParameters(); uint16 version; CryptoTag key_exchange; @@ -161,6 +182,7 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParams { std::string premaster_secret; scoped_ptr<QuicEncrypter> encrypter; scoped_ptr<QuicDecrypter> decrypter; + std::string server_config_id; }; // QuicCryptoConfig contains common configuration between clients and servers. @@ -169,19 +191,11 @@ class NET_EXPORT_PRIVATE QuicCryptoConfig { QuicCryptoConfig(); ~QuicCryptoConfig(); - // ProcessPeerHandshake performs common processing when receiving a peer's - // handshake message. - bool ProcessPeerHandshake(const CryptoHandshakeMessage& peer_handshake, - CryptoUtils::Priority priority, - QuicCryptoNegotiatedParams* out_params, - std::string* error_details) const; - // Protocol version uint16 version; // Key exchange methods. The following two members' values correspond by // index. CryptoTagVector kexs; - std::vector<KeyExchange*> key_exchanges; // Authenticated encryption with associated data (AEAD) algorithms. CryptoTagVector aead; @@ -193,17 +207,82 @@ class NET_EXPORT_PRIVATE QuicCryptoConfig { // client. class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { public: + // A CachedState contains the information that the client needs in order to + // perform a 0-RTT handshake with a server. This information can be reused + // over several connections to the same server. + class CachedState { + public: + CachedState(); + ~CachedState(); + + // is_complete returns true if this object contains enough information to + // perform a handshake with the server. + bool is_complete() const; + + // GetServerConfig returns the parsed contents of |server_config|, or NULL + // if |server_config| is empty. The return value is owned by this object + // and is destroyed when this object is. + const CryptoHandshakeMessage* GetServerConfig() const; + + // SetServerConfig checks that |scfg| parses correctly and stores it in + // |server_config|. It returns true if the parsing succeeds and false + // otherwise. + bool SetServerConfig(base::StringPiece scfg); + + const std::string& server_config() const; + const std::string& source_address_token() const; + const std::string& orbit() const; + + private: + std::string server_config_id_; // An opaque id from the server. + std::string server_config_; // A serialized handshake message. + std::string source_address_token_; // An opaque proof of IP ownership. + std::string orbit_; // An opaque server-id used in nonce generation. + + // scfg contains the cached, parsed value of |server_config|. + mutable scoped_ptr<CryptoHandshakeMessage> scfg_; + }; + QuicCryptoClientConfig(); + ~QuicCryptoClientConfig(); - // Sets the members to reasonable, default values. |rand| is used in order to - // generate ephemeral ECDH keys. - void SetDefaults(QuicRandom* rand); + // Sets the members to reasonable, default values. + void SetDefaults(); + + // Lookup returns a CachedState for the given hostname, or NULL if no + // information is known. + const CachedState* Lookup(const std::string& server_hostname); + + // FillInchoateClientHello sets |out| to be a CHLO message that elicits a + // source-address token or SCFG from a server. If |cached| is non-NULL, the + // source-address token will be taken from it. + void FillInchoateClientHello(const std::string& server_hostname, + const CachedState* cached, + CryptoHandshakeMessage* out); // 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); + // of this object. This object must have cached enough information about + // |server_hostname| in order to perform a handshake. This can be checked + // with the |is_complete| member of |CachedState|. + // + // |clock| and |rand| are used to generate the nonce and |out_params| is + // filled with the results of the handshake that the server is expected to + // accept. + QuicErrorCode FillClientHello(const std::string& server_hostname, + QuicGuid guid, + const CachedState* cached, + const QuicClock* clock, + QuicRandom* rand, + QuicCryptoNegotiatedParameters* out_params, + CryptoHandshakeMessage* out, + std::string* error_details); + + // ProcessRejection processes a REJ message from a server and updates the + // cached information about that server. After this, |is_complete| may return + // true for that server's CachedState. + QuicErrorCode ProcessRejection(const std::string& server_hostname, + const CryptoHandshakeMessage& rej, + std::string* error_details); // ProcessServerHello processes the message in |server_hello|, writes the // negotiated parameters to |out_params| and returns QUIC_NO_ERROR. If @@ -211,11 +290,13 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // |error_details| and returns an error code. QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, const std::string& nonce, - QuicCryptoNegotiatedParams* out_params, + QuicCryptoNegotiatedParameters* out_params, std::string* error_details); - // The |info| string for the HKDF function. - std::string hkdf_info; + private: + // cached_states_ maps from the server hostname to the cached information + // about that server. + std::map<std::string, CachedState*> cached_states_; }; // QuicCryptoServerConfig contains the crypto configuration of a QUIC server. @@ -236,29 +317,28 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { const QuicClock* clock, const CryptoHandshakeMessage& extra_tags); - // It returns the tag/value map of the config if successful. The caller takes - // ownership of the map. - CryptoTagValueMap* AddConfig(QuicServerConfigProtobuf* protobuf); + // AddConfig adds a QuicServerConfigProtobuf to the availible configurations. + // It returns the SCFG message from the config if successful. The caller + // takes ownership of the CryptoHandshakeMessage. + CryptoHandshakeMessage* AddConfig(QuicServerConfigProtobuf* protobuf); // AddTestingConfig creates a test config and then calls AddConfig to add it. // Any tags in |extra_tags| will be copied into the config. - CryptoTagValueMap* AddTestingConfig(QuicRandom* rand, - const QuicClock* clock, - const CryptoHandshakeMessage& extra_tags); + CryptoHandshakeMessage* AddTestingConfig( + QuicRandom* rand, + const QuicClock* clock, + const CryptoHandshakeMessage& extra_tags); // 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, |out_params| is completed 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, - QuicCryptoNegotiatedParams* out_params, - std::string* error_details); - - // The |info| string for the HKDF function. - std::string hkdf_info; + // set to the contents of the ServerHello, |out_params| is completed and + // QUIC_NO_ERROR is returned. |nonce| is used as the server's nonce. + // Otherwise |out| is set to be a REJ message and an error code is returned. + QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, + QuicGuid guid, + CryptoHandshakeMessage* out, + QuicCryptoNegotiatedParameters* out_params, + std::string* error_details); private: // Config represents a server config: a collection of preferences and @@ -270,9 +350,19 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // serialized contains the bytes of this server config, suitable for sending // on the wire. std::string serialized; + // id contains the SCID of this server config. + std::string id; + + // key_exchanges contains key exchange objects with the private keys + // already loaded. The values correspond, one-to-one, with the tags in + // |kexs| from the parent class. + std::vector<KeyExchange*> key_exchanges; // tag_value_map contains the raw key/value pairs for the config. CryptoTagValueMap tag_value_map; + + private: + DISALLOW_COPY_AND_ASSIGN(Config); }; std::map<ServerConfigID, Config*> configs_; diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index 010b1d0..0b84a98 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -25,7 +25,7 @@ typedef std::vector<CryptoTag> CryptoTagVector; 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 +const CryptoTag kSCFG = MAKE_TAG('S', 'C', 'F', 'G'); // Server config const CryptoTag kREJ = MAKE_TAG('R', 'E', 'J', '\0'); // Reject // Key exchange methods @@ -56,6 +56,7 @@ 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 CryptoTag kSRCT = MAKE_TAG('S', 'R', 'C', 'T'); // Source-address token const size_t kMaxEntries = 16; // Max number of entries in a message. diff --git a/net/quic/crypto/crypto_utils.cc b/net/quic/crypto/crypto_utils.cc index 0ff5336..b6ae58b 100644 --- a/net/quic/crypto/crypto_utils.cc +++ b/net/quic/crypto/crypto_utils.cc @@ -4,8 +4,11 @@ #include "net/quic/crypto/crypto_utils.h" -#include "base/string_piece.h" +#include "crypto/hkdf.h" +#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_clock.h" @@ -63,15 +66,46 @@ bool CryptoUtils::FindMutualTag(const CryptoTagVector& our_tags_vector, void CryptoUtils::GenerateNonce(const QuicClock* clock, QuicRandom* random_generator, + const string& orbit, string* nonce) { // a 4-byte timestamp + 28 random bytes. nonce->reserve(kNonceSize); nonce->resize(kNonceSize); QuicTime::Delta now = clock->NowAsDeltaSinceUnixEpoch(); uint32 gmt_unix_time = now.ToSeconds(); - const size_t time_size = sizeof(gmt_unix_time); - memcpy(&(*nonce)[0], &gmt_unix_time, time_size); - random_generator->RandBytes(&(*nonce)[time_size], kNonceSize - time_size); + memcpy(&(*nonce)[0], &gmt_unix_time, sizeof(gmt_unix_time)); + + size_t bytes_written = sizeof(gmt_unix_time); + if (orbit.size() == 8) { + memcpy(&(*nonce)[bytes_written], orbit.data(), orbit.size()); + bytes_written += orbit.size(); + } + random_generator->RandBytes(&(*nonce)[bytes_written], + kNonceSize - bytes_written); +} + +void CryptoUtils::DeriveKeys(QuicCryptoNegotiatedParameters* params, + StringPiece nonce, + const string& hkdf_input, + Perspective perspective) { + params->encrypter.reset(QuicEncrypter::Create(params->aead)); + params->decrypter.reset(QuicDecrypter::Create(params->aead)); + size_t key_bytes = params->encrypter->GetKeySize(); + size_t nonce_prefix_bytes = params->encrypter->GetNoncePrefixSize(); + + crypto::HKDF hkdf(params->premaster_secret, nonce, + hkdf_input, key_bytes, nonce_prefix_bytes); + if (perspective == SERVER) { + params->encrypter->SetKey(hkdf.server_write_key()); + params->encrypter->SetNoncePrefix(hkdf.server_write_iv()); + params->decrypter->SetKey(hkdf.client_write_key()); + params->decrypter->SetNoncePrefix(hkdf.client_write_iv()); + } else { + params->encrypter->SetKey(hkdf.client_write_key()); + params->encrypter->SetNoncePrefix(hkdf.client_write_iv()); + params->decrypter->SetKey(hkdf.server_write_key()); + params->decrypter->SetNoncePrefix(hkdf.server_write_iv()); + } } } // namespace net diff --git a/net/quic/crypto/crypto_utils.h b/net/quic/crypto/crypto_utils.h index 1fe4934..058a1ab 100644 --- a/net/quic/crypto/crypto_utils.h +++ b/net/quic/crypto/crypto_utils.h @@ -9,13 +9,16 @@ #include <string> +#include "base/string_piece.h" #include "net/base/net_export.h" +#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" namespace net { class QuicClock; class QuicRandom; +struct QuicCryptoNegotiatedParameters; class NET_EXPORT_PRIVATE CryptoUtils { public: @@ -24,6 +27,11 @@ class NET_EXPORT_PRIVATE CryptoUtils { PEER_PRIORITY, }; + enum Perspective { + SERVER, + CLIENT, + }; + // FindMutualTag sets |out_result| to the first tag in the priority list that // is also in the other list and returns true. If there is no intersection it // returns false. @@ -39,10 +47,23 @@ class NET_EXPORT_PRIVATE CryptoUtils { CryptoTag* out_result, size_t* out_index); - // Generates the connection nonce. + // Generates the connection nonce. The nonce is formed as: + // <4 bytes> current time + // <8 bytes> |orbit| (or random if |orbit| is empty) + // <20 bytes> random static void GenerateNonce(const QuicClock* clock, QuicRandom* random_generator, + const std::string& orbit, std::string* nonce); + + // DeriveKeys populates the |encrypter| and |decrypter| members of |params| + // given the contents of |premaster_secret|, |nonce| and |hkdf_input|. + // |perspective| controls whether the server's keys are assigned to + // |encrypter| or |decrypter|. + static void DeriveKeys(QuicCryptoNegotiatedParameters* params, + base::StringPiece nonce, + const std::string& hkdf_input, + Perspective perspective); }; } // namespace net diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc index 5847b1e..d87f903 100644 --- a/net/quic/quic_client_session_test.cc +++ b/net/quic/quic_client_session_test.cc @@ -75,7 +75,7 @@ TEST_F(QuicClientSessionTest, GoAwayReceived) { ASSERT_TRUE(session_.CryptoConnect(callback_.callback())); // Simulate the server crypto handshake. CryptoHandshakeMessage server_message; - server_message.tag = kSHLO; + server_message.set_tag(kSHLO); session_.GetCryptoStream()->OnHandshakeMessage(server_message); // After receiving a GoAway, I should no longer be able to create outgoing diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 955d82c..5bb15c6 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -8,7 +8,6 @@ #include "base/logging.h" #include "base/stl_util.h" -#include "net/base/net_errors.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/quic_utils.h" @@ -387,7 +386,7 @@ void QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { OnCanWrite(); } } else if (!delay.IsInfinite()) { - helper_->SetSendAlarm(delay); + helper_->SetSendAlarm(time_of_last_received_packet_.Add(delay)); } } @@ -690,6 +689,9 @@ const QuicConnectionStats& QuicConnection::GetStats() { void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address, const IPEndPoint& peer_address, const QuicEncryptedPacket& packet) { + if (!connected_) { + return; + } if (debug_visitor_) { debug_visitor_->OnPacketReceived(self_address, peer_address, packet); } @@ -725,7 +727,7 @@ bool QuicConnection::OnCanWrite() { // We're not write blocked, but some stream didn't write out all of its // bytes. Register for 'immediate' resumption so we'll keep writing after // other quic connections have had a chance to use the socket. - helper_->SetSendAlarm(QuicTime::Delta::Zero()); + helper_->SetSendAlarm(clock_->ApproximateNow()); } } @@ -867,15 +869,17 @@ bool QuicConnection::CanWrite(bool is_retransmission, return false; } + QuicTime now = clock_->Now(); QuicTime::Delta delay = congestion_manager_.TimeUntilSend( - clock_->Now(), is_retransmission, has_retransmittable_data); + now, is_retransmission, has_retransmittable_data); if (delay.IsInfinite()) { - return false; + // TODO(pwestin): should be false but trigger other bugs see b/8350327. + return true; } // If the scheduler requires a delay, then we can not send this packet now. if (!delay.IsZero()) { - helper_->SetSendAlarm(delay); + helper_->SetSendAlarm(now.Add(delay)); return false; } return true; @@ -949,12 +953,15 @@ bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number, int error; QuicTime now = clock_->Now(); int rv = helper_->WritePacketToWire(*encrypted, &error); - if (rv == -1 && error == ERR_IO_PENDING) { + if (rv == -1 && helper_->IsWriteBlocked(error)) { // TODO(satyashekhar): It might be more efficient (fewer system calls), if // all connections share this variable i.e this becomes a part of // PacketWriterInterface. write_blocked_ = true; - return false; + // If the socket buffers the the data, then the packet should not + // be queued and sent again, which would result in an unnecessary duplicate + // packet being sent. + return helper_->IsWriteBlockedDataBuffered(); } time_of_last_sent_packet_ = now; DVLOG(1) << "time of last sent packet: " << now.ToMicroseconds(); @@ -1153,10 +1160,16 @@ void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, SerializedPacket serialized_packet = packet_creator_.SerializeConnectionClose(&frame); - SendOrQueuePacket(serialized_packet.sequence_number, - serialized_packet.packet, - serialized_packet.entropy_hash, - serialized_packet.retransmittable_frames != NULL); + + // We need to update the sent entrophy hash for all sent packets. + entropy_manager_.RecordSentPacketEntropyHash( + serialized_packet.sequence_number, + serialized_packet.entropy_hash); + + WritePacket(serialized_packet.sequence_number, + serialized_packet.packet, + serialized_packet.retransmittable_frames != NULL, + kForce); } void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error, diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index 6bde120..3c3c00d 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -149,6 +149,15 @@ class NET_EXPORT_PRIVATE QuicConnectionHelperInterface { virtual int WritePacketToWire(const QuicEncryptedPacket& packet, int* error) = 0; + // Returns true if the helper buffers and subsequently rewrites data + // when an attempt to write results in the underlying socket becoming + // write blocked. + virtual bool IsWriteBlockedDataBuffered() = 0; + + // Returns true if |error| represents a write-block error code such + // as EAGAIN or ERR_IO_PENDING. + virtual bool IsWriteBlocked(int error) = 0; + // Sets up an alarm to retransmit a packet if we haven't received an ack // in the expected time frame. Implementations must invoke // OnRetransmissionAlarm when the alarm fires. Implementations must also @@ -156,10 +165,10 @@ class NET_EXPORT_PRIVATE QuicConnectionHelperInterface { // alarm is already set, this call is a no-op. virtual void SetRetransmissionAlarm(QuicTime::Delta delay) = 0; - // Sets an alarm to send packets after |delay_in_us|. Implementations must + // Sets an alarm to send packets at |alarm_time|. Implementations must // invoke OnCanWrite when the alarm fires. Implementations must also // handle the case where |this| is deleted before the alarm fires. - virtual void SetSendAlarm(QuicTime::Delta delay) = 0; + virtual void SetSendAlarm(QuicTime alarm_time) = 0; // Sets an alarm which fires when the connection may have timed out. // Implementations must call CheckForTimeout() and then reregister the alarm diff --git a/net/quic/quic_connection_helper.cc b/net/quic/quic_connection_helper.cc index 2d1fecf..a0e3204 100644 --- a/net/quic/quic_connection_helper.cc +++ b/net/quic/quic_connection_helper.cc @@ -9,6 +9,7 @@ #include "base/task_runner.h" #include "base/time.h" #include "net/base/io_buffer.h" +#include "net/base/net_errors.h" #include "net/quic/quic_utils.h" namespace net { @@ -69,6 +70,16 @@ int QuicConnectionHelper::WritePacketToWire( return rv; } +bool QuicConnectionHelper::IsWriteBlockedDataBuffered() { + // Chrome sockets' Write() methods buffer the data until the Write is + // permitted. + return true; +} + +bool QuicConnectionHelper::IsWriteBlocked(int error) { + return error == ERR_IO_PENDING; +} + void QuicConnectionHelper::SetRetransmissionAlarm(QuicTime::Delta delay) { if (!retransmission_alarm_registered_) { task_runner_->PostDelayedTask( @@ -95,13 +106,14 @@ void QuicConnectionHelper::ClearAckAlarm() { ack_alarm_time_ = QuicTime::Zero(); } -void QuicConnectionHelper::SetSendAlarm(QuicTime::Delta delay) { +void QuicConnectionHelper::SetSendAlarm(QuicTime alarm_time) { send_alarm_registered_ = true; task_runner_->PostDelayedTask( FROM_HERE, base::Bind(&QuicConnectionHelper::OnSendAlarm, weak_factory_.GetWeakPtr()), - base::TimeDelta::FromMicroseconds(delay.ToMicroseconds())); + base::TimeDelta::FromMicroseconds( + alarm_time.Subtract(QuicTime::Zero()).ToMicroseconds())); } void QuicConnectionHelper::SetTimeoutAlarm(QuicTime::Delta delay) { diff --git a/net/quic/quic_connection_helper.h b/net/quic/quic_connection_helper.h index 4f06ff31..12f80bb 100644 --- a/net/quic/quic_connection_helper.h +++ b/net/quic/quic_connection_helper.h @@ -47,8 +47,10 @@ class NET_EXPORT_PRIVATE QuicConnectionHelper virtual QuicRandom* GetRandomGenerator() OVERRIDE; virtual int WritePacketToWire(const QuicEncryptedPacket& packet, int* error) OVERRIDE; + virtual bool IsWriteBlockedDataBuffered() OVERRIDE; + virtual bool IsWriteBlocked(int error) OVERRIDE; virtual void SetRetransmissionAlarm(QuicTime::Delta delay) OVERRIDE; - virtual void SetSendAlarm(QuicTime::Delta delay) OVERRIDE; + virtual void SetSendAlarm(QuicTime alarm_time) OVERRIDE; virtual void SetTimeoutAlarm(QuicTime::Delta delay) OVERRIDE; virtual bool IsSendAlarmSet() OVERRIDE; virtual void UnregisterSendAlarmIfRegistered() OVERRIDE; diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc index eeca0bf..08743d05 100644 --- a/net/quic/quic_connection_helper_test.cc +++ b/net/quic/quic_connection_helper_test.cc @@ -224,12 +224,14 @@ TEST_F(QuicConnectionHelperTest, IsSendAlarmSet) { } TEST_F(QuicConnectionHelperTest, SetSendAlarm) { - helper_->SetSendAlarm(QuicTime::Delta::Zero()); + helper_->SetSendAlarm( + clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1))); EXPECT_TRUE(helper_->IsSendAlarmSet()); } TEST_F(QuicConnectionHelperTest, UnregisterSendAlarmIfRegistered) { - helper_->SetSendAlarm(QuicTime::Delta::Zero()); + helper_->SetSendAlarm( + clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1))); helper_->UnregisterSendAlarmIfRegistered() ; EXPECT_FALSE(helper_->IsSendAlarmSet()); } diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index 6becb69..72322ff 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -135,8 +135,16 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { retransmission_alarm_ = clock_->ApproximateNow().Add(delay); } - virtual void SetSendAlarm(QuicTime::Delta delay) OVERRIDE { - send_alarm_ = clock_->ApproximateNow().Add(delay); + virtual bool IsWriteBlockedDataBuffered() OVERRIDE { + return false; + } + + virtual bool IsWriteBlocked(int error) OVERRIDE { + return error == ERR_IO_PENDING; + } + + virtual void SetSendAlarm(QuicTime alarm_time) OVERRIDE { + send_alarm_ = alarm_time; } virtual void SetTimeoutAlarm(QuicTime::Delta delay) OVERRIDE { @@ -809,7 +817,8 @@ TEST_F(QuicConnectionTest, FECQueueing) { TEST_F(QuicConnectionTest, FramePacking) { // Block the connection. - helper_->SetSendAlarm(QuicTime::Delta::FromSeconds(1)); + helper_->SetSendAlarm( + clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1))); // Send an ack and two stream frames in 1 packet by queueing them. connection_.SendAck(); @@ -842,7 +851,8 @@ TEST_F(QuicConnectionTest, FramePackingFEC) { // Enable fec. connection_.options()->max_packets_per_fec_group = 6; // Block the connection. - helper_->SetSendAlarm(QuicTime::Delta::FromSeconds(1)); + helper_->SetSendAlarm( + clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1))); // Send an ack and two stream frames in 1 packet by queueing them. connection_.SendAck(); diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index a84a756..badddce 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -14,12 +14,10 @@ namespace net { QuicCryptoClientStream::QuicCryptoClientStream(QuicSession* session, const string& server_hostname) : QuicCryptoStream(session), + next_state_(STATE_IDLE), server_hostname_(server_hostname) { config_.SetDefaults(); - - QuicGuid guid = session->connection()->guid(); - crypto_config_.hkdf_info.append(reinterpret_cast<char*>(&guid), - sizeof(guid)); + crypto_config_.SetDefaults(); } QuicCryptoClientStream::~QuicCryptoClientStream() { @@ -27,51 +25,111 @@ QuicCryptoClientStream::~QuicCryptoClientStream() { void QuicCryptoClientStream::OnHandshakeMessage( const CryptoHandshakeMessage& message) { - // Do not process handshake messages after the handshake is complete. - if (handshake_complete()) { - CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE); - return; - } + DoHandshakeLoop(&message); +} - if (message.tag != kSHLO) { - CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE); - return; - } +bool QuicCryptoClientStream::CryptoConnect() { + next_state_ = STATE_SEND_CHLO; + DoHandshakeLoop(NULL); + return true; +} + +const QuicNegotiatedParameters& +QuicCryptoClientStream::negotiated_params() const { + return negotiated_params_; +} +const QuicCryptoNegotiatedParameters& +QuicCryptoClientStream::crypto_negotiated_params() const { + return crypto_negotiated_params_; +} + +void QuicCryptoClientStream::DoHandshakeLoop( + const CryptoHandshakeMessage* in) { + CryptoHandshakeMessage out; + QuicErrorCode error; string error_details; - QuicErrorCode error = config_.ProcessPeerHandshake( - message, - CryptoUtils::PEER_PRIORITY, - &negotiated_params_, - &error_details); - if (error != QUIC_NO_ERROR) { - CloseConnectionWithDetails(error, error_details); - return; - } - QuicErrorCode err = crypto_config_.ProcessServerHello( - message, nonce_, &crypto_negotiated_params_, &error_details); - if (err != QUIC_NO_ERROR) { - CloseConnectionWithDetails(err, error_details); - return; + if (in != NULL) { + DLOG(INFO) << "Client received: " << in->DebugString(); } - SetHandshakeComplete(QUIC_NO_ERROR); - return; -} - -bool QuicCryptoClientStream::CryptoConnect() { - crypto_config_.SetDefaults(session()->connection()->random_generator()); - CryptoUtils::GenerateNonce(session()->connection()->clock(), - session()->connection()->random_generator(), - &nonce_); - CryptoHandshakeMessage message; - crypto_config_.FillClientHello(nonce_, server_hostname_, &message); - config_.ToHandshakeMessage(&message); - const QuicData& data = message.GetSerialized(); - crypto_config_.hkdf_info.append(data.data(), data.length()); - SendHandshakeMessage(message); - return true; + for (;;) { + const State state = next_state_; + next_state_ = STATE_IDLE; + switch (state) { + case STATE_SEND_CHLO: { + const QuicCryptoClientConfig::CachedState* cached = + crypto_config_.Lookup(server_hostname_); + if (!cached || !cached->is_complete()) { + crypto_config_.FillInchoateClientHello(server_hostname_, cached, + &out); + next_state_ = STATE_RECV_REJ; + DLOG(INFO) << "Client Sending: " << out.DebugString(); + SendHandshakeMessage(out); + return; + } + const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); + config_.ToHandshakeMessage(&out); + error = crypto_config_.FillClientHello( + server_hostname_, + session()->connection()->guid(), + cached, + session()->connection()->clock(), + session()->connection()->random_generator(), + &crypto_negotiated_params_, + &out, + &error_details); + if (error != QUIC_NO_ERROR) { + CloseConnectionWithDetails(error, error_details); + return; + } + error = config_.ProcessFinalPeerHandshake( + *scfg, CryptoUtils::PEER_PRIORITY, &negotiated_params_, + &error_details); + if (error != QUIC_NO_ERROR) { + CloseConnectionWithDetails(error, error_details); + return; + } + next_state_ = STATE_RECV_SHLO; + DLOG(INFO) << "Client Sending: " << out.DebugString(); + SendHandshakeMessage(out); + return; + } + case STATE_RECV_REJ: + // We sent a dummy CHLO because we don't have enough information to + // perform a handshake. Here we hope to have a REJ that contains the + // information that we need. + if (in->tag() != kREJ) { + CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Expected REJ"); + return; + } + error = crypto_config_.ProcessRejection(server_hostname_, *in, + &error_details); + if (error != QUIC_NO_ERROR) { + CloseConnectionWithDetails(error, error_details); + return; + } + next_state_ = STATE_SEND_CHLO; + break; + case STATE_RECV_SHLO: + // We sent a CHLO that we expected to be accepted and now we're hoping + // for a SHLO from the server to confirm that. + if (in->tag() != kSHLO) { + // TODO(agl): in the future we would attempt the handshake again. + CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Expected SHLO"); + return; + } + SetHandshakeComplete(QUIC_NO_ERROR); + return; + case STATE_IDLE: + // This means that the peer sent us a message that we weren't expecting. + CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE); + return; + } + } } } // namespace net diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h index 3b0c97d..463994e 100644 --- a/net/quic/quic_crypto_client_stream.h +++ b/net/quic/quic_crypto_client_stream.h @@ -13,7 +13,6 @@ namespace net { class QuicSession; -struct CryptoHandshakeMessage; namespace test { class CryptoTestUtils; @@ -30,16 +29,33 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { // Performs a crypto handshake with the server. Returns true if the crypto // handshake is started successfully. + // TODO(agl): this should probably return void. virtual bool CryptoConnect(); + const QuicNegotiatedParameters& negotiated_params() const; + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const; + private: friend class test::CryptoTestUtils; + enum State { + STATE_IDLE, + STATE_SEND_CHLO, + STATE_RECV_REJ, + STATE_RECV_SHLO, + }; + + // DoHandshakeLoop performs a step of the handshake state machine. Note that + // |in| is NULL for the first call. + void DoHandshakeLoop(const CryptoHandshakeMessage* in); + + State next_state_; + QuicConfig config_; QuicCryptoClientConfig crypto_config_; QuicNegotiatedParameters negotiated_params_; - QuicCryptoNegotiatedParams crypto_negotiated_params_; + QuicCryptoNegotiatedParameters crypto_negotiated_params_; // Client's connection nonce (4-byte timestamp + 28 random bytes) std::string nonce_; diff --git a/net/quic/quic_crypto_client_stream_test.cc b/net/quic/quic_crypto_client_stream_test.cc index 81f67bb..9c33d5b 100644 --- a/net/quic/quic_crypto_client_stream_test.cc +++ b/net/quic/quic_crypto_client_stream_test.cc @@ -90,66 +90,6 @@ TEST_F(QuicCryptoClientStreamTest, NotInitiallyConected) { EXPECT_FALSE(stream_.handshake_complete()); } -TEST_F(QuicCryptoClientStreamTest, ClientHelloContents) { - EXPECT_TRUE(stream_.CryptoConnect()); - - SimpleQuicFramer framer; - ASSERT_TRUE(framer.ProcessPacket(*connection_->packets_[0])); - ASSERT_EQ(1u, framer.stream_frames().size()); - const QuicStreamFrame& frame(framer.stream_frames()[0]); - EXPECT_EQ(kCryptoStreamId, frame.stream_id); - EXPECT_FALSE(frame.fin); - EXPECT_EQ(0u, frame.offset); - - scoped_ptr<CryptoHandshakeMessage> chlo(framer.HandshakeMessage(0)); - EXPECT_EQ(kCHLO, chlo->tag); - - CryptoTagValueMap& tag_value_map = chlo->tag_value_map; - - // kSNI - EXPECT_EQ(kServerHostname, tag_value_map[kSNI]); - - // kNONC - // TODO(wtc): check the nonce. - ASSERT_EQ(32u, tag_value_map[kNONC].size()); - - // kVERS - ASSERT_EQ(2u, tag_value_map[kVERS].size()); - uint16 version; - memcpy(&version, tag_value_map[kVERS].data(), 2); - EXPECT_EQ(0u, version); - - // kKEXS - ASSERT_EQ(4u, tag_value_map[kKEXS].size()); - CryptoTag key_exchange[1]; - memcpy(&key_exchange[0], &tag_value_map[kKEXS][0], 4); - EXPECT_EQ(kC255, key_exchange[0]); - - // kAEAD - ASSERT_EQ(4u, tag_value_map[kAEAD].size()); - CryptoTag cipher[1]; - memcpy(&cipher[0], &tag_value_map[kAEAD][0], 4); - EXPECT_EQ(kAESG, cipher[0]); - - // kICSL - ASSERT_EQ(4u, tag_value_map[kICSL].size()); - uint32 idle_lifetime; - memcpy(&idle_lifetime, tag_value_map[kICSL].data(), 4); - EXPECT_EQ(300u, idle_lifetime); - - // kKATO - ASSERT_EQ(4u, tag_value_map[kKATO].size()); - uint32 keepalive_timeout; - memcpy(&keepalive_timeout, tag_value_map[kKATO].data(), 4); - EXPECT_EQ(0u, keepalive_timeout); - - // kCGST - ASSERT_EQ(4u, tag_value_map[kCGST].size()); - CryptoTag congestion[1]; - memcpy(&congestion[0], &tag_value_map[kCGST][0], 4); - EXPECT_EQ(kQBIC, congestion[0]); -} - TEST_F(QuicCryptoClientStreamTest, ConnectedAfterSHLO) { CompleteCryptoHandshake(); EXPECT_TRUE(stream_.handshake_complete()); @@ -160,22 +100,34 @@ TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) { EXPECT_CALL(*connection_, SendConnectionClose( QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE)); - message_.tag = kCHLO; + message_.set_tag(kCHLO); ConstructHandshakeMessage(); stream_.ProcessData(message_data_->data(), message_data_->length()); } TEST_F(QuicCryptoClientStreamTest, BadMessageType) { - message_.tag = kCHLO; + EXPECT_TRUE(stream_.CryptoConnect()); + + message_.set_tag(kCHLO); ConstructHandshakeMessage(); - EXPECT_CALL(*connection_, - SendConnectionClose(QUIC_INVALID_CRYPTO_MESSAGE_TYPE)); + EXPECT_CALL(*connection_, SendConnectionCloseWithDetails( + QUIC_INVALID_CRYPTO_MESSAGE_TYPE, "Expected REJ")); stream_.ProcessData(message_data_->data(), message_data_->length()); } -TEST_F(QuicCryptoClientStreamTest, CryptoConnect) { - EXPECT_TRUE(stream_.CryptoConnect()); +TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { + CompleteCryptoHandshake(); + + const QuicNegotiatedParameters& params(stream_.negotiated_params()); + EXPECT_EQ(kQBIC, params.congestion_control); + EXPECT_EQ(300, params.idle_connection_state_lifetime.ToSeconds()); + EXPECT_EQ(0, params.keepalive_timeout.ToSeconds()); + + const QuicCryptoNegotiatedParameters& crypto_params( + stream_.crypto_negotiated_params()); + EXPECT_EQ(kAESG, crypto_params.aead); + EXPECT_EQ(kC255, crypto_params.key_exchange); } } // namespace diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc index 8fdd9da..6fc508d 100644 --- a/net/quic/quic_crypto_server_stream.cc +++ b/net/quic/quic_crypto_server_stream.cc @@ -18,17 +18,23 @@ QuicCryptoServerStream::QuicCryptoServerStream(QuicSession* session) CryptoHandshakeMessage extra_tags; config_.ToHandshakeMessage(&extra_tags); - QuicGuid guid = session->connection()->guid(); - crypto_config_.hkdf_info.append(reinterpret_cast<char*>(&guid), - sizeof(guid)); // TODO(agl): AddTestingConfig generates a new, random config. In the future // this will be replaced with a real source of configs. - scoped_ptr<CryptoTagValueMap> config_tags( + scoped_ptr<CryptoHandshakeMessage> scfg( crypto_config_.AddTestingConfig(session->connection()->random_generator(), session->connection()->clock(), extra_tags)); // If we were using the same config in many servers then we would have to // parse a QuicConfig from config_tags here. + + // Our non-crypto configuration is also expressed in the SCFG because it's + // signed. Thus |config_| needs to be consistent with that. + if (!config_.SetFromHandshakeMessage(*scfg)) { + // TODO(agl): when we aren't generating testing configs then this can be a + // CHECK at startup time. + LOG(WARNING) << "SCFG could not be parsed by QuicConfig."; + DCHECK(false); + } } QuicCryptoServerStream::~QuicCryptoServerStream() { @@ -42,35 +48,42 @@ void QuicCryptoServerStream::OnHandshakeMessage( return; } - if (message.tag != kCHLO) { + if (message.tag() != kCHLO) { CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE); return; } string error_details; - QuicErrorCode error = config_.ProcessPeerHandshake( - message, CryptoUtils::LOCAL_PRIORITY, &negotiated_params_, - &error_details); - if (error != QUIC_NO_ERROR) { - CloseConnectionWithDetails(error, "negotiated params"); - return; - } + CryptoHandshakeMessage reply; + crypto_config_.ProcessClientHello( + message, session()->connection()->guid(), &reply, + &crypto_negotiated_params_, &error_details); + + if (reply.tag() == kSHLO) { + // If we are returning a SHLO then we accepted the handshake. + QuicErrorCode error = config_.ProcessFinalPeerHandshake( + message, CryptoUtils::LOCAL_PRIORITY, &negotiated_params_, + &error_details); + if (error != QUIC_NO_ERROR) { + CloseConnectionWithDetails(error, error_details); + return; + } - CryptoHandshakeMessage shlo; - CryptoUtils::GenerateNonce(session()->connection()->clock(), - session()->connection()->random_generator(), - &server_nonce_); - crypto_config_.ProcessClientHello(message, server_nonce_, &shlo, - &crypto_negotiated_params_, &error_details); - if (!error_details.empty()) { - DLOG(INFO) << "Rejecting CHLO: " << error_details; + SetHandshakeComplete(QUIC_NO_ERROR); } - config_.ToHandshakeMessage(&shlo); - SendHandshakeMessage(shlo); - // TODO(rch): correctly validate the message - SetHandshakeComplete(QUIC_NO_ERROR); + SendHandshakeMessage(reply); return; } +const QuicNegotiatedParameters& +QuicCryptoServerStream::negotiated_params() const { + return negotiated_params_; +} + +const QuicCryptoNegotiatedParameters& +QuicCryptoServerStream::crypto_negotiated_params() const { + return crypto_negotiated_params_; +} + } // namespace net diff --git a/net/quic/quic_crypto_server_stream.h b/net/quic/quic_crypto_server_stream.h index ac63495..7841172 100644 --- a/net/quic/quic_crypto_server_stream.h +++ b/net/quic/quic_crypto_server_stream.h @@ -13,7 +13,6 @@ namespace net { class QuicSession; -struct CryptoHandshakeMessage; namespace test { class CryptoTestUtils; @@ -28,6 +27,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { virtual void OnHandshakeMessage( const CryptoHandshakeMessage& message) OVERRIDE; + const QuicNegotiatedParameters& negotiated_params() const; + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const; + private: friend class test::CryptoTestUtils; @@ -39,7 +41,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { std::string server_nonce_; QuicNegotiatedParameters negotiated_params_; - QuicCryptoNegotiatedParams crypto_negotiated_params_; + QuicCryptoNegotiatedParameters crypto_negotiated_params_; }; } // namespace net diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc index b95af91..1d2b11d 100644 --- a/net/quic/quic_crypto_server_stream_test.cc +++ b/net/quic/quic_crypto_server_stream_test.cc @@ -104,13 +104,13 @@ TEST_F(QuicCryptoServerStreamTest, MessageAfterHandshake) { CompleteCryptoHandshake(); EXPECT_CALL(*connection_, SendConnectionClose( QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE)); - message_.tag = kCHLO; + message_.set_tag(kCHLO); ConstructHandshakeMessage(); stream_.ProcessData(message_data_->data(), message_data_->length()); } TEST_F(QuicCryptoServerStreamTest, BadMessageType) { - message_.tag = kSHLO; + message_.set_tag(kSHLO); ConstructHandshakeMessage(); EXPECT_CALL(*connection_, SendConnectionClose( QUIC_INVALID_CRYPTO_MESSAGE_TYPE)); diff --git a/net/quic/quic_crypto_stream.cc b/net/quic/quic_crypto_stream.cc index d34e6ec..79c912a 100644 --- a/net/quic/quic_crypto_stream.cc +++ b/net/quic/quic_crypto_stream.cc @@ -67,27 +67,60 @@ QuicNegotiatedParameters::QuicNegotiatedParameters() } QuicConfig::QuicConfig() - : idle_connection_state_lifetime(QuicTime::Delta::Zero()), - keepalive_timeout(QuicTime::Delta::Zero()) { + : idle_connection_state_lifetime_(QuicTime::Delta::Zero()), + keepalive_timeout_(QuicTime::Delta::Zero()) { } QuicConfig::~QuicConfig() { } void QuicConfig::SetDefaults() { - idle_connection_state_lifetime = QuicTime::Delta::FromSeconds(300); - keepalive_timeout = QuicTime::Delta::Zero(); - congestion_control.push_back(kQBIC); + idle_connection_state_lifetime_ = QuicTime::Delta::FromSeconds(300); + keepalive_timeout_ = QuicTime::Delta::Zero(); + congestion_control_.clear(); + congestion_control_.push_back(kQBIC); +} + +bool QuicConfig::SetFromHandshakeMessage(const CryptoHandshakeMessage& scfg) { + const CryptoTag* cgst; + size_t num_cgst; + QuicErrorCode error; + + error = scfg.GetTaglist(kCGST, &cgst, &num_cgst); + if (error != QUIC_NO_ERROR) { + return false; + } + + congestion_control_.assign(cgst, cgst + num_cgst); + + uint32 idle; + error = scfg.GetUint32(kICSL, &idle); + if (error != QUIC_NO_ERROR) { + return false; + } + + idle_connection_state_lifetime_ = QuicTime::Delta::FromSeconds(idle); + + keepalive_timeout_ = QuicTime::Delta::Zero(); + + uint32 keepalive; + error = scfg.GetUint32(kKATO, &keepalive); + // KATO is optional. + if (error == QUIC_NO_ERROR) { + keepalive_timeout_ = QuicTime::Delta::FromSeconds(keepalive); + } + + return true; } void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { out->SetValue( - kICSL, static_cast<uint32>(idle_connection_state_lifetime.ToSeconds())); - out->SetValue(kKATO, static_cast<uint32>(keepalive_timeout.ToSeconds())); - out->SetVector(kCGST, congestion_control); + kICSL, static_cast<uint32>(idle_connection_state_lifetime_.ToSeconds())); + out->SetValue(kKATO, static_cast<uint32>(keepalive_timeout_.ToSeconds())); + out->SetVector(kCGST, congestion_control_); } -QuicErrorCode QuicConfig::ProcessPeerHandshake( +QuicErrorCode QuicConfig::ProcessFinalPeerHandshake( const CryptoHandshakeMessage& msg, CryptoUtils::Priority priority, QuicNegotiatedParameters* out_params, @@ -105,7 +138,7 @@ QuicErrorCode QuicConfig::ProcessPeerHandshake( return error; } - if (!CryptoUtils::FindMutualTag(congestion_control, + if (!CryptoUtils::FindMutualTag(congestion_control_, their_congestion_controls, num_their_congestion_controls, priority, @@ -127,7 +160,7 @@ QuicErrorCode QuicConfig::ProcessPeerHandshake( } out_params->idle_connection_state_lifetime = QuicTime::Delta::FromSeconds( - std::min(static_cast<uint32>(idle_connection_state_lifetime.ToSeconds()), + std::min(static_cast<uint32>(idle_connection_state_lifetime_.ToSeconds()), idle)); uint32 keepalive; @@ -135,7 +168,7 @@ QuicErrorCode QuicConfig::ProcessPeerHandshake( switch (error) { case QUIC_NO_ERROR: out_params->keepalive_timeout = QuicTime::Delta::FromSeconds( - std::min(static_cast<uint32>(keepalive_timeout.ToSeconds()), + std::min(static_cast<uint32>(keepalive_timeout_.ToSeconds()), keepalive)); break; case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: diff --git a/net/quic/quic_crypto_stream.h b/net/quic/quic_crypto_stream.h index fb2f6c0..43667c0 100644 --- a/net/quic/quic_crypto_stream.h +++ b/net/quic/quic_crypto_stream.h @@ -12,8 +12,8 @@ namespace net { +class CryptoHandshakeMessage; class QuicSession; -struct CryptoHandshakeMessage; // Crypto handshake messages in QUIC take place over a reserved // reliable stream with the id 1. Each endpoint (client and server) @@ -79,22 +79,31 @@ class NET_EXPORT_PRIVATE QuicConfig { // SetDefaults sets the members to sensible, default values. void SetDefaults(); + // SetFromMessage extracts the non-crypto configuration from |msg| and sets + // the members of this object to match. This is expected to be called in the + // case of a server which is loading a server config. The server config + // contains the non-crypto parameters and so the server will need to keep its + // QuicConfig in sync with the server config that it'll be sending to + // clients. + bool SetFromHandshakeMessage(const CryptoHandshakeMessage& scfg); + // ToHandshakeMessage serializes the settings in this object as a series of // tags /value pairs and adds them to |out|. void ToHandshakeMessage(CryptoHandshakeMessage* out) const; - QuicErrorCode ProcessPeerHandshake( + QuicErrorCode ProcessFinalPeerHandshake( const CryptoHandshakeMessage& peer_handshake, CryptoUtils::Priority priority, QuicNegotiatedParameters* out_params, string* error_details) const; + private: // Congestion control feedback type. - CryptoTagVector congestion_control; + CryptoTagVector congestion_control_; // Idle connection state lifetime - QuicTime::Delta idle_connection_state_lifetime; + QuicTime::Delta idle_connection_state_lifetime_; // Keepalive timeout, or 0 to turn off keepalive probes - QuicTime::Delta keepalive_timeout; + QuicTime::Delta keepalive_timeout_; }; } // namespace net diff --git a/net/quic/quic_crypto_stream_test.cc b/net/quic/quic_crypto_stream_test.cc index 98d3fb2..8f8028b 100644 --- a/net/quic/quic_crypto_stream_test.cc +++ b/net/quic/quic_crypto_stream_test.cc @@ -9,6 +9,8 @@ #include "base/memory/scoped_ptr.h" #include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -48,9 +50,9 @@ class QuicCryptoStreamTest : public ::testing::Test { connection_(new MockConnection(1, addr_, false)), session_(connection_, true), stream_(&session_) { - message_.tag = kSHLO; - message_.tag_value_map[1] = "abc"; - message_.tag_value_map[2] = "def"; + message_.set_tag(kSHLO); + message_.SetStringPiece(1, "abc"); + message_.SetStringPiece(2, "def"); ConstructHandshakeMessage(); } @@ -86,10 +88,11 @@ TEST_F(QuicCryptoStreamTest, ProcessData) { stream_.ProcessData(message_data_->data(), message_data_->length())); ASSERT_EQ(1u, stream_.messages()->size()); - EXPECT_EQ(kSHLO, (*stream_.messages())[0].tag); - EXPECT_EQ(2u, (*stream_.messages())[0].tag_value_map.size()); - EXPECT_EQ("abc", (*stream_.messages())[0].tag_value_map[1]); - EXPECT_EQ("def", (*stream_.messages())[0].tag_value_map[2]); + const CryptoHandshakeMessage& message = (*stream_.messages())[0]; + EXPECT_EQ(kSHLO, message.tag()); + EXPECT_EQ(2u, message.tag_value_map().size()); + EXPECT_EQ("abc", CryptoTestUtils::GetValueForTag(message, 1)); + EXPECT_EQ("def", CryptoTestUtils::GetValueForTag(message, 2)); } TEST_F(QuicCryptoStreamTest, ProcessBadData) { diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 462172c..0b8e27b 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -1067,7 +1067,8 @@ size_t QuicFramer::ComputeFrameLength(const QuicFrame& frame) { DCHECK(false); return 0; } - // Not reachable, but some Chrome compilers can't figure that out. + + // Not reachable, but some Chrome compilers can't figure that out. *sigh* DCHECK(false); return 0; } diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index 497b91a..12d6b2f 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -59,8 +59,7 @@ size_t GetFecGroupOffset(bool include_version) { // Index into the nonce proof of the public reset packet. const size_t kPublicResetPacketNonceProofOffset = - kPublicFlagsOffset + - kPublicFlagsSize; + kPublicFlagsOffset + kPublicFlagsSize; // Index into the rejected sequence number of the public reset packet. const size_t kPublicResetPacketRejectedSequenceNumberOffset = kPublicResetPacketNonceProofOffset + kPublicResetNonceSize; diff --git a/net/quic/quic_packet_generator.h b/net/quic/quic_packet_generator.h index 99f6098..73e310f 100644 --- a/net/quic/quic_packet_generator.h +++ b/net/quic/quic_packet_generator.h @@ -6,7 +6,7 @@ // Packets are serialized just-in-time. Control frames are queued. // Ack and Feedback frames will be requested from the Connection // just-in-time. When a packet needs to be sent, the Generator -// will serialized a packet and pass it to QuicConnection::SendOrQueuePacket() +// will serialize a packet and pass it to QuicConnection::SendOrQueuePacket() // // The Generator's mode of operation is controlled by two conditions: // diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index 2a297ba..c72c7a7 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -195,6 +195,15 @@ enum QuicErrorCode { // A crypto message was received that contained a parameter with too few // values. QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND, + // An internal error occured in crypto processing. + QUIC_CRYPTO_INTERNAL_ERROR, + // A crypto handshake message specified an unsupported version. + QUIC_VERSION_NOT_SUPPORTED, + // There was no intersection between the crypto primitives supported by the + // peer and ourselves. + QUIC_CRYPTO_NO_SUPPORT, + + QUIC_LAST_ERROR, }; // Version and Crypto tags are written to the wire with a big-endian diff --git a/net/quic/quic_stats.cc b/net/quic/quic_stats.cc index 162e093..7404d92 100644 --- a/net/quic/quic_stats.cc +++ b/net/quic/quic_stats.cc @@ -6,22 +6,18 @@ namespace net { -QuicConnectionStats::QuicConnectionStats() { - bytes_sent = 0; - packets_sent = 0; - - bytes_received = 0; - packets_received = 0; - - bytes_retransmitted = 0; - packets_retransmitted = 0; - - packets_revived = 0; - packets_dropped = 0; - rto_count = 0; - - rtt = 0; - estimated_bandwidth = 0; +QuicConnectionStats::QuicConnectionStats() + : bytes_sent(0), + packets_sent(0), + bytes_received(0), + packets_received(0), + bytes_retransmitted(0), + packets_retransmitted(0), + packets_revived(0), + packets_dropped(0), + rto_count(0), + rtt(0), + estimated_bandwidth(0) { } QuicConnectionStats::~QuicConnectionStats() {} diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index 0f6750b..d28bc63 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc @@ -61,6 +61,9 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_CRYPTO_TOO_MANY_ENTRIES); RETURN_STRING_LITERAL(QUIC_CRYPTO_INVALID_VALUE_LENGTH) RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE); + RETURN_STRING_LITERAL(QUIC_CRYPTO_INTERNAL_ERROR); + RETURN_STRING_LITERAL(QUIC_VERSION_NOT_SUPPORTED); + RETURN_STRING_LITERAL(QUIC_CRYPTO_NO_SUPPORT); RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_TYPE); RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER); RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND); @@ -71,6 +74,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET); RETURN_STRING_LITERAL(QUIC_INVALID_VERSION); RETURN_STRING_LITERAL(QUIC_CONNECTION_TIMED_OUT); + RETURN_STRING_LITERAL(QUIC_LAST_ERROR); // Intentionally have no default case, so we'll break the build // if we add errors and don't put them here. } diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc index 71f3234..fcb04ab 100644 --- a/net/quic/test_tools/crypto_test_utils.cc +++ b/net/quic/test_tools/crypto_test_utils.cc @@ -14,6 +14,7 @@ #include "net/quic/test_tools/simple_quic_framer.h" using base::StringPiece; +using std::string; namespace net { namespace test { @@ -106,6 +107,15 @@ void CryptoTestUtils::HandshakeWithFakeClient( } // static +string CryptoTestUtils::GetValueForTag(const CryptoHandshakeMessage& message, + CryptoTag tag) { + CryptoTagValueMap::const_iterator it = message.tag_value_map().find(tag); + if (it == message.tag_value_map().end()) { + return string(); + } + return it->second; +} + void CryptoTestUtils::CompareClientAndServerKeys( QuicCryptoClientStream* client, QuicCryptoServerStream* server) { diff --git a/net/quic/test_tools/crypto_test_utils.h b/net/quic/test_tools/crypto_test_utils.h index a205074..5ece1db 100644 --- a/net/quic/test_tools/crypto_test_utils.h +++ b/net/quic/test_tools/crypto_test_utils.h @@ -29,6 +29,10 @@ class CryptoTestUtils { static void HandshakeWithFakeClient(PacketSavingConnection* server_conn, QuicCryptoServerStream* server); + // Returns the value for the tag |tag| in the tag value map of |message|. + static std::string GetValueForTag(const CryptoHandshakeMessage& message, + CryptoTag tag); + private: static void CompareClientAndServerKeys(QuicCryptoClientStream* client, QuicCryptoServerStream* server); diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc index 1e02ead..ce726e1 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -292,7 +292,7 @@ static QuicPacket* ConstructPacketFromHandshakeMessage( QuicPacket* ConstructHandshakePacket(QuicGuid guid, CryptoTag tag) { CryptoHandshakeMessage message; - message.tag = tag; + message.set_tag(tag); return ConstructPacketFromHandshakeMessage(guid, message, false); } diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index 9a012ac..523daaf 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -186,9 +186,11 @@ class MockHelper : public QuicConnectionHelperInterface { QuicRandom* GetRandomGenerator(); MOCK_METHOD2(WritePacketToWire, int(const QuicEncryptedPacket& packet, int* error)); + MOCK_METHOD0(IsWriteBlockedDataBuffered, bool()); + MOCK_METHOD1(IsWriteBlocked, bool(int)); MOCK_METHOD1(SetRetransmissionAlarm, void(QuicTime::Delta delay)); MOCK_METHOD1(SetAckAlarm, void(QuicTime::Delta delay)); - MOCK_METHOD1(SetSendAlarm, void(QuicTime::Delta delay)); + MOCK_METHOD1(SetSendAlarm, void(QuicTime alarm_time)); MOCK_METHOD1(SetTimeoutAlarm, void(QuicTime::Delta delay)); MOCK_METHOD0(IsSendAlarmSet, bool()); MOCK_METHOD0(UnregisterSendAlarmIfRegistered, void()); @@ -212,7 +214,8 @@ class MockConnection : public QuicConnection { const IPEndPoint& peer_address, const QuicEncryptedPacket& packet)); MOCK_METHOD1(SendConnectionClose, void(QuicErrorCode error)); - + MOCK_METHOD2(SendConnectionCloseWithDetails, void(QuicErrorCode error, + const string& details)); MOCK_METHOD2(SendRstStream, void(QuicStreamId id, QuicErrorCode error)); MOCK_METHOD3(SendGoAway, void(QuicErrorCode error, @@ -307,7 +310,6 @@ class TestEntropyCalculator : virtual QuicPacketEntropyHash ReceivedEntropyHash( QuicPacketSequenceNumber sequence_number) const OVERRIDE; - }; } // namespace test diff --git a/net/quic/test_tools/simple_quic_framer.h b/net/quic/test_tools/simple_quic_framer.h index dc74e69..07a443f 100644 --- a/net/quic/test_tools/simple_quic_framer.h +++ b/net/quic/test_tools/simple_quic_framer.h @@ -13,7 +13,7 @@ namespace net { -struct CryptoHandshakeMessage; +class CryptoHandshakeMessage; struct QuicAckFrame; class QuicConnection; class QuicConnectionVisitorInterface; |