From d0e406165adf1ab1180e2f1b4b1536c540a9339f Mon Sep 17 00:00:00 2001 From: "wtc@chromium.org" Date: Wed, 5 Feb 2014 01:53:18 +0000 Subject: Add net/quic/crypto/crypto_handshake_message.{cc,h}. These files are part of https://codereview.chromium.org/147763009/. They are added first so that the rest of that CL can be committed by the commit queue. TBR=rch@chromium.org BUG=none TEST=none Review URL: https://codereview.chromium.org/154643006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@248826 0039d316-1c4b-4281-b951-d872f2087c98 --- net/quic/crypto/crypto_handshake_message.cc | 313 ++++++++++++++++++++++++++++ net/quic/crypto/crypto_handshake_message.h | 135 ++++++++++++ 2 files changed, 448 insertions(+) create mode 100644 net/quic/crypto/crypto_handshake_message.cc create mode 100644 net/quic/crypto/crypto_handshake_message.h (limited to 'net') diff --git a/net/quic/crypto/crypto_handshake_message.cc b/net/quic/crypto/crypto_handshake_message.cc new file mode 100644 index 0000000..7efa0b5 --- /dev/null +++ b/net/quic/crypto/crypto_handshake_message.cc @@ -0,0 +1,313 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/crypto_handshake_message.h" + +#include "base/strings/stringprintf.h" +#include "base/strings/string_number_conversions.h" +#include "net/quic/crypto/crypto_framer.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_utils.h" + +using base::StringPiece; +using base::StringPrintf; +using std::string; +using std::vector; + +namespace net { + +CryptoHandshakeMessage::CryptoHandshakeMessage() + : tag_(0), + minimum_size_(0) {} + +CryptoHandshakeMessage::CryptoHandshakeMessage( + const CryptoHandshakeMessage& other) + : tag_(other.tag_), + tag_value_map_(other.tag_value_map_), + minimum_size_(other.minimum_size_) { + // Don't copy serialized_. scoped_ptr doesn't have a copy constructor. + // The new object can lazily reconstruct serialized_. +} + +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(); + minimum_size_ = other.minimum_size_; + return *this; +} + +void CryptoHandshakeMessage::Clear() { + tag_ = 0; + tag_value_map_.clear(); + minimum_size_ = 0; + serialized_.reset(); +} + +const QuicData& CryptoHandshakeMessage::GetSerialized() const { + if (!serialized_.get()) { + serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this)); + } + return *serialized_.get(); +} + +void CryptoHandshakeMessage::MarkDirty() { + serialized_.reset(); +} + +void CryptoHandshakeMessage::SetTaglist(QuicTag tag, ...) { + // Warning, if sizeof(QuicTag) > sizeof(int) then this function will break + // because the terminating 0 will only be promoted to int. + COMPILE_ASSERT(sizeof(QuicTag) <= sizeof(int), + crypto_tag_may_not_be_larger_than_int_or_varargs_will_break); + + vector tags; + va_list ap; + + va_start(ap, tag); + for (;;) { + QuicTag list_item = va_arg(ap, QuicTag); + if (list_item == 0) { + break; + } + tags.push_back(list_item); + } + + // 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); +} + +void CryptoHandshakeMessage::SetStringPiece(QuicTag tag, StringPiece value) { + tag_value_map_[tag] = value.as_string(); +} + +void CryptoHandshakeMessage::Erase(QuicTag tag) { + tag_value_map_.erase(tag); +} + +QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag, + const QuicTag** out_tags, + size_t* out_len) const { + QuicTagValueMap::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(QuicTag) != 0) { + ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (ret != QUIC_NO_ERROR) { + *out_tags = NULL; + *out_len = 0; + return ret; + } + + *out_tags = reinterpret_cast(it->second.data()); + *out_len = it->second.size() / sizeof(QuicTag); + return ret; +} + +bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag, + StringPiece* out) const { + QuicTagValueMap::const_iterator it = tag_value_map_.find(tag); + if (it == tag_value_map_.end()) { + return false; + } + *out = it->second; + return true; +} + +QuicErrorCode CryptoHandshakeMessage::GetNthValue24(QuicTag 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() < 3) { + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + const unsigned char* data = + reinterpret_cast(value.data()); + size_t size = static_cast(data[0]) | + (static_cast(data[1]) << 8) | + (static_cast(data[2]) << 16); + value.remove_prefix(3); + + 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); + } +} + +QuicErrorCode CryptoHandshakeMessage::GetUint16(QuicTag tag, + uint16* out) const { + return GetPOD(tag, out, sizeof(uint16)); +} + +QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag, + uint32* out) const { + return GetPOD(tag, out, sizeof(uint32)); +} + +QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag, + uint64* out) const { + return GetPOD(tag, out, sizeof(uint64)); +} + +size_t CryptoHandshakeMessage::size() const { + size_t ret = sizeof(QuicTag) + + sizeof(uint16) /* number of entries */ + + sizeof(uint16) /* padding */; + ret += (sizeof(QuicTag) + sizeof(uint32) /* end offset */) * + tag_value_map_.size(); + for (QuicTagValueMap::const_iterator i = tag_value_map_.begin(); + i != tag_value_map_.end(); ++i) { + ret += i->second.size(); + } + + return ret; +} + +void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) { + if (min_bytes == minimum_size_) { + return; + } + serialized_.reset(); + minimum_size_ = min_bytes; +} + +size_t CryptoHandshakeMessage::minimum_size() const { + return minimum_size_; +} + +string CryptoHandshakeMessage::DebugString() const { + return DebugStringInternal(0); +} + +QuicErrorCode CryptoHandshakeMessage::GetPOD( + QuicTag tag, void* out, size_t len) const { + QuicTagValueMap::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; +} + +string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { + string ret = string(2 * indent, ' ') + QuicUtils::TagToString(tag_) + "<\n"; + ++indent; + for (QuicTagValueMap::const_iterator it = tag_value_map_.begin(); + it != tag_value_map_.end(); ++it) { + ret += string(2 * indent, ' ') + QuicUtils::TagToString(it->first) + ": "; + + bool done = false; + switch (it->first) { + case kICSL: + case kIRTT: + case kKATO: + case kMSPC: + case kSWND: + // uint32 value + if (it->second.size() == 4) { + uint32 value; + memcpy(&value, it->second.data(), sizeof(value)); + ret += base::UintToString(value); + done = true; + } + break; + case kVERS: + // uint16 value + if (it->second.size() == 2) { + uint16 value; + memcpy(&value, it->second.data(), sizeof(value)); + ret += base::UintToString(value); + done = true; + } + break; + case kKEXS: + case kAEAD: + case kCGST: + case kPDMD: + case kVER: + // tag lists + if (it->second.size() % sizeof(QuicTag) == 0) { + for (size_t j = 0; j < it->second.size(); j += sizeof(QuicTag)) { + QuicTag tag; + memcpy(&tag, it->second.data() + j, sizeof(tag)); + if (j > 0) { + ret += ","; + } + ret += "'" + QuicUtils::TagToString(tag) + "'"; + } + done = true; + } + break; + case kSCFG: + // nested messages. + if (!it->second.empty()) { + scoped_ptr msg( + CryptoFramer::ParseMessage(it->second)); + if (msg.get()) { + ret += "\n"; + ret += msg->DebugStringInternal(indent + 1); + + done = true; + } + } + break; + case kPAD: + ret += StringPrintf("(%d bytes of padding)", + static_cast(it->second.size())); + done = true; + break; + } + + if (!done) { + // If there's no specific format for this tag, or the value is invalid, + // then just use hex. + ret += "0x" + base::HexEncode(it->second.data(), it->second.size()); + } + ret += "\n"; + } + --indent; + ret += string(2 * indent, ' ') + ">"; + return ret; +} + +} // namespace net diff --git a/net/quic/crypto/crypto_handshake_message.h b/net/quic/crypto/crypto_handshake_message.h new file mode 100644 index 0000000..fcb3930 --- /dev/null +++ b/net/quic/crypto/crypto_handshake_message.h @@ -0,0 +1,135 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_ +#define NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_ + +#include +#include + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "net/base/net_export.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +// An intermediate format of a handshake message that's convenient for a +// CryptoFramer to serialize from or parse into. +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; + + // MarkDirty invalidates the cache created by |GetSerialized|. + void MarkDirty(); + + // SetValue sets an element with the given tag to the raw, memory contents of + // |v|. + template void SetValue(QuicTag tag, const T& v) { + tag_value_map_[tag] = + std::string(reinterpret_cast(&v), sizeof(v)); + } + + // SetVector sets an element with the given tag to the raw contents of an + // array of elements in |v|. + template void SetVector(QuicTag tag, const std::vector& v) { + if (v.empty()) { + tag_value_map_[tag] = std::string(); + } else { + tag_value_map_[tag] = std::string(reinterpret_cast(&v[0]), + v.size() * sizeof(T)); + } + } + + // Returns the message tag. + QuicTag tag() const { return tag_; } + // Sets the message tag. + void set_tag(QuicTag tag) { tag_ = tag; } + + const QuicTagValueMap& tag_value_map() const { return tag_value_map_; } + + // 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(QuicTag tag, ...); + + void SetStringPiece(QuicTag tag, base::StringPiece value); + + // Erase removes a tag/value, if present, from the message. + void Erase(QuicTag tag); + + // 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. + // The array points into the CryptoHandshakeMessage and is valid only for as + // long as the CryptoHandshakeMessage exists and is not modified. + QuicErrorCode GetTaglist(QuicTag tag, const QuicTag** out_tags, + size_t* out_len) const; + + bool GetStringPiece(QuicTag tag, base::StringPiece* out) const; + + // GetNthValue24 interprets the value with the given tag to be a series of + // 24-bit, length prefixed values and it returns the subvalue with the given + // index. + QuicErrorCode GetNthValue24(QuicTag tag, + unsigned index, + base::StringPiece* out) const; + QuicErrorCode GetUint16(QuicTag tag, uint16* out) const; + QuicErrorCode GetUint32(QuicTag tag, uint32* out) const; + QuicErrorCode GetUint64(QuicTag tag, uint64* out) const; + + // size returns 4 (message tag) + 2 (uint16, number of entries) + + // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes. + size_t size() const; + + // set_minimum_size sets the minimum number of bytes that the message should + // consume. The CryptoFramer will add a PAD tag as needed when serializing in + // order to ensure this. Setting a value of 0 disables padding. + // + // Padding is useful in order to ensure that messages are a minimum size. A + // QUIC server can require a minimum size in order to reduce the + // amplification factor of any mirror DoS attack. + void set_minimum_size(size_t min_bytes); + + size_t minimum_size() const; + + // 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 + // |tag| exists in the message, and has a value of exactly |len| bytes then + // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out| + // are zeroed out. + // + // If used to copy integers then this assumes that the machine is + // little-endian. + QuicErrorCode GetPOD(QuicTag tag, void* out, size_t len) const; + + std::string DebugStringInternal(size_t indent) const; + + QuicTag tag_; + QuicTagValueMap tag_value_map_; + + size_t minimum_size_; + + // The serialized form of the handshake message. This member is constructed + // lasily. + mutable scoped_ptr serialized_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_ -- cgit v1.1