summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/quic/congestion_control/quic_congestion_manager.cc12
-rw-r--r--net/quic/crypto/crypto_framer.cc40
-rw-r--r--net/quic/crypto/crypto_framer.h8
-rw-r--r--net/quic/crypto/crypto_framer_test.cc56
-rw-r--r--net/quic/crypto/crypto_handshake.cc643
-rw-r--r--net/quic/crypto/crypto_handshake.h184
-rw-r--r--net/quic/crypto/crypto_protocol.h3
-rw-r--r--net/quic/crypto/crypto_utils.cc42
-rw-r--r--net/quic/crypto/crypto_utils.h23
-rw-r--r--net/quic/quic_client_session_test.cc2
-rw-r--r--net/quic/quic_connection.cc37
-rw-r--r--net/quic/quic_connection.h13
-rw-r--r--net/quic/quic_connection_helper.cc16
-rw-r--r--net/quic/quic_connection_helper.h4
-rw-r--r--net/quic/quic_connection_helper_test.cc6
-rw-r--r--net/quic/quic_connection_test.cc18
-rw-r--r--net/quic/quic_crypto_client_stream.cc144
-rw-r--r--net/quic/quic_crypto_client_stream.h20
-rw-r--r--net/quic/quic_crypto_client_stream_test.cc84
-rw-r--r--net/quic/quic_crypto_server_stream.cc61
-rw-r--r--net/quic/quic_crypto_server_stream.h6
-rw-r--r--net/quic/quic_crypto_server_stream_test.cc4
-rw-r--r--net/quic/quic_crypto_stream.cc57
-rw-r--r--net/quic/quic_crypto_stream.h19
-rw-r--r--net/quic/quic_crypto_stream_test.cc17
-rw-r--r--net/quic/quic_framer.cc3
-rw-r--r--net/quic/quic_framer_test.cc3
-rw-r--r--net/quic/quic_packet_generator.h2
-rw-r--r--net/quic/quic_protocol.h9
-rw-r--r--net/quic/quic_stats.cc28
-rw-r--r--net/quic/quic_utils.cc4
-rw-r--r--net/quic/test_tools/crypto_test_utils.cc10
-rw-r--r--net/quic/test_tools/crypto_test_utils.h4
-rw-r--r--net/quic/test_tools/quic_test_utils.cc2
-rw-r--r--net/quic/test_tools/quic_test_utils.h8
-rw-r--r--net/quic/test_tools/simple_quic_framer.h2
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;