| Index: net/quic/crypto/crypto_handshake.cc
|
| diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc
|
| index 7faa21d6c76b99107cacd872cd564e3571a7be40..9d8c48f9b1ecbd9fe749105fdb6d666fb5f60bd6 100644
|
| --- a/net/quic/crypto/crypto_handshake.cc
|
| +++ b/net/quic/crypto/crypto_handshake.cc
|
| @@ -6,12 +6,15 @@
|
|
|
| #include "base/memory/scoped_ptr.h"
|
| #include "base/stl_util.h"
|
| +#include "crypto/hkdf.h"
|
| #include "crypto/secure_hash.h"
|
| #include "net/base/net_util.h"
|
| #include "net/quic/crypto/crypto_framer.h"
|
| #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/quic_decrypter.h"
|
| +#include "net/quic/crypto/quic_encrypter.h"
|
| #include "net/quic/crypto/quic_random.h"
|
| #include "net/quic/quic_protocol.h"
|
|
|
| @@ -25,6 +28,8 @@ namespace net {
|
| // implement.
|
| static const uint16 kVersion = 0;
|
|
|
| +static const char kLabel[] = "QUIC key expansion";
|
| +
|
| using crypto::SecureHash;
|
|
|
| QuicServerConfigProtobuf::QuicServerConfigProtobuf() {
|
| @@ -34,6 +39,169 @@ QuicServerConfigProtobuf::~QuicServerConfigProtobuf() {
|
| STLDeleteElements(&keys_);
|
| }
|
|
|
| +CryptoHandshakeMessage::CryptoHandshakeMessage() {}
|
| +
|
| +CryptoHandshakeMessage::CryptoHandshakeMessage(
|
| + const CryptoHandshakeMessage& other)
|
| + : 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.
|
| +}
|
| +
|
| +CryptoHandshakeMessage::~CryptoHandshakeMessage() {}
|
| +
|
| +CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
|
| + const CryptoHandshakeMessage& other) {
|
| + 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;
|
| +}
|
| +
|
| +const QuicData& CryptoHandshakeMessage::GetSerialized() const {
|
| + if (!serialized_.get()) {
|
| + serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this));
|
| + }
|
| + return *serialized_.get();
|
| +}
|
| +
|
| +void CryptoHandshakeMessage::SetTaglist(CryptoTag tag, ...) {
|
| + // Warning, if sizeof(CryptoTag) > sizeof(int) then this function will break
|
| + // because the terminating 0 will only be promoted to int.
|
| + COMPILE_ASSERT(sizeof(CryptoTag) <= sizeof(int),
|
| + crypto_tag_not_be_larger_than_int_or_varargs_will_break);
|
| +
|
| + vector<CryptoTag> tags;
|
| + va_list ap;
|
| +
|
| + va_start(ap, tag);
|
| + for (;;) {
|
| + CryptoTag tag = va_arg(ap, CryptoTag);
|
| + if (tag == 0) {
|
| + break;
|
| + }
|
| + tags.push_back(tag);
|
| + }
|
| +
|
| + // Because of the way that we keep tags in memory, we can copy the contents
|
| + // of the vector and get the correct bytes in wire format. See
|
| + // crypto_protocol.h. This assumes that the system is little-endian.
|
| + SetVector(tag, tags);
|
| +
|
| + va_end(ap);
|
| +}
|
| +
|
| +QuicErrorCode CryptoHandshakeMessage::GetTaglist(CryptoTag tag,
|
| + const CryptoTag** out_tags,
|
| + size_t* out_len) const {
|
| + CryptoTagValueMap::const_iterator it = tag_value_map.find(tag);
|
| + QuicErrorCode ret = QUIC_NO_ERROR;
|
| +
|
| + if (it == tag_value_map.end()) {
|
| + ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
|
| + } else if (it->second.size() % sizeof(CryptoTag) != 0) {
|
| + ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| + }
|
| +
|
| + if (ret != QUIC_NO_ERROR) {
|
| + *out_tags = NULL;
|
| + *out_len = 0;
|
| + return ret;
|
| + }
|
| +
|
| + *out_tags = reinterpret_cast<const CryptoTag*>(it->second.data());
|
| + *out_len = it->second.size() / sizeof(CryptoTag);
|
| + return ret;
|
| +}
|
| +
|
| +bool CryptoHandshakeMessage::GetStringPiece(CryptoTag tag,
|
| + StringPiece* out) const {
|
| + 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 {
|
| + StringPiece value;
|
| + if (!GetStringPiece(tag, &value)) {
|
| + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
|
| + }
|
| +
|
| + for (unsigned i = 0;; i++) {
|
| + if (value.empty()) {
|
| + return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND;
|
| + }
|
| + if (value.size() < 2) {
|
| + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| + }
|
| +
|
| + const unsigned char* data =
|
| + reinterpret_cast<const unsigned char*>(value.data());
|
| + size_t size = static_cast<size_t>(data[0]) |
|
| + (static_cast<size_t>(data[1]) << 8);
|
| + value.remove_prefix(2);
|
| +
|
| + if (value.size() < size) {
|
| + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| + }
|
| +
|
| + if (i == index) {
|
| + *out = StringPiece(value.data(), size);
|
| + return QUIC_NO_ERROR;
|
| + }
|
| +
|
| + value.remove_prefix(size);
|
| + }
|
| +}
|
| +
|
| +bool CryptoHandshakeMessage::GetString(CryptoTag tag, string* out) const {
|
| + CryptoTagValueMap::const_iterator it = tag_value_map.find(tag);
|
| + if (it == tag_value_map.end()) {
|
| + return false;
|
| + }
|
| + *out = it->second;
|
| + return true;
|
| +}
|
| +
|
| +QuicErrorCode CryptoHandshakeMessage::GetUint16(CryptoTag tag,
|
| + uint16* out) const {
|
| + return GetPOD(tag, out, sizeof(uint16));
|
| +}
|
| +
|
| +QuicErrorCode CryptoHandshakeMessage::GetUint32(CryptoTag tag,
|
| + uint32* out) const {
|
| + return GetPOD(tag, out, sizeof(uint32));
|
| +}
|
| +
|
| +QuicErrorCode CryptoHandshakeMessage::GetPOD(
|
| + CryptoTag tag, void* out, size_t len) const {
|
| + CryptoTagValueMap::const_iterator it = tag_value_map.find(tag);
|
| + QuicErrorCode ret = QUIC_NO_ERROR;
|
| +
|
| + if (it == tag_value_map.end()) {
|
| + ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
|
| + } else if (it->second.size() != len) {
|
| + ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| + }
|
| +
|
| + if (ret != QUIC_NO_ERROR) {
|
| + memset(out, 0, len);
|
| + return ret;
|
| + }
|
| +
|
| + memcpy(out, it->second.data(), len);
|
| + return ret;
|
| +}
|
| +
|
| QuicCryptoNegotiatedParams::QuicCryptoNegotiatedParams()
|
| : version(0),
|
| key_exchange(0),
|
| @@ -43,7 +211,6 @@ QuicCryptoNegotiatedParams::QuicCryptoNegotiatedParams()
|
| QuicCryptoNegotiatedParams::~QuicCryptoNegotiatedParams() {
|
| }
|
|
|
| -
|
| QuicCryptoConfig::QuicCryptoConfig()
|
| : version(0) {
|
| }
|
| @@ -56,7 +223,7 @@ bool QuicCryptoConfig::ProcessPeerHandshake(
|
| const CryptoHandshakeMessage& peer_msg,
|
| CryptoUtils::Priority priority,
|
| QuicCryptoNegotiatedParams* out_params,
|
| - string *error_details) const {
|
| + string* error_details) const {
|
| if (peer_msg.GetUint16(kVERS, &out_params->version) != QUIC_NO_ERROR ||
|
| out_params->version != kVersion) {
|
| if (error_details) {
|
| @@ -125,6 +292,10 @@ bool QuicCryptoConfig::ProcessPeerHandshake(
|
| return true;
|
| }
|
|
|
| +QuicCryptoClientConfig::QuicCryptoClientConfig()
|
| + : hkdf_info(kLabel, arraysize(kLabel)) {
|
| +}
|
| +
|
| void QuicCryptoClientConfig::SetDefaults(QuicRandom* rand) {
|
| // Version must be 0.
|
| version = kVersion;
|
| @@ -183,6 +354,7 @@ void QuicCryptoClientConfig::FillClientHello(const string& nonce,
|
|
|
| QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
|
| const CryptoHandshakeMessage& server_hello,
|
| + const string& nonce,
|
| QuicCryptoNegotiatedParams* out_params,
|
| string* error_details) {
|
| if (server_hello.tag != kSHLO) {
|
| @@ -198,8 +370,7 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
|
|
|
| scoped_ptr<CryptoHandshakeMessage> scfg(
|
| CryptoFramer::ParseMessage(scfg_bytes));
|
| - if (!scfg.get() ||
|
| - scfg->tag != kSCFG) {
|
| + if (!scfg.get() || scfg->tag != kSCFG) {
|
| *error_details = "Invalid SCFG";
|
| return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| }
|
| @@ -209,11 +380,29 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
|
| 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());
|
| +
|
| return QUIC_NO_ERROR;
|
| }
|
|
|
|
|
| -QuicCryptoServerConfig::QuicCryptoServerConfig() {
|
| +QuicCryptoServerConfig::QuicCryptoServerConfig()
|
| + : hkdf_info(kLabel, arraysize(kLabel)) {
|
| }
|
|
|
| QuicCryptoServerConfig::~QuicCryptoServerConfig() {
|
| @@ -400,6 +589,31 @@ bool QuicCryptoServerConfig::ProcessClientHello(
|
| return false;
|
| }
|
|
|
| + StringPiece client_nonce;
|
| + if (!client_hello.GetStringPiece(kNONC, &client_nonce)) {
|
| + return false;
|
| + }
|
| +
|
| + 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;
|
|
|