diff options
Diffstat (limited to 'net/quic/crypto')
-rw-r--r-- | net/quic/crypto/crypto_framer.cc | 40 | ||||
-rw-r--r-- | net/quic/crypto/crypto_framer.h | 8 | ||||
-rw-r--r-- | net/quic/crypto/crypto_framer_test.cc | 56 | ||||
-rw-r--r-- | net/quic/crypto/crypto_handshake.cc | 643 | ||||
-rw-r--r-- | net/quic/crypto/crypto_handshake.h | 184 | ||||
-rw-r--r-- | net/quic/crypto/crypto_protocol.h | 3 | ||||
-rw-r--r-- | net/quic/crypto/crypto_utils.cc | 42 | ||||
-rw-r--r-- | net/quic/crypto/crypto_utils.h | 23 |
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 |