summaryrefslogtreecommitdiffstats
path: root/net/quic/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'net/quic/crypto')
-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
8 files changed, 704 insertions, 295 deletions
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