blob: 9fa4dfa5319c0681c6e4b411262021c1022b4094 [file] [log] [blame]
// 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 "quiche/quic/core/crypto/crypto_handshake_message.h"
#include <memory>
#include <string>
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "quiche/quic/core/crypto/crypto_framer.h"
#include "quiche/quic/core/crypto/crypto_protocol.h"
#include "quiche/quic/core/crypto/crypto_utils.h"
#include "quiche/quic/core/quic_socket_address_coder.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/common/quiche_endian.h"
namespace quic {
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_. unique_ptr doesn't have a copy constructor.
// The new object can lazily reconstruct serialized_.
}
CryptoHandshakeMessage::CryptoHandshakeMessage(CryptoHandshakeMessage&& other) =
default;
CryptoHandshakeMessage::~CryptoHandshakeMessage() {}
CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
const CryptoHandshakeMessage& other) {
tag_ = other.tag_;
tag_value_map_ = other.tag_value_map_;
// Don't copy serialized_. unique_ptr doesn't have an assignment operator.
// However, invalidate serialized_.
serialized_.reset();
minimum_size_ = other.minimum_size_;
return *this;
}
CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
CryptoHandshakeMessage&& other) = default;
bool CryptoHandshakeMessage::operator==(
const CryptoHandshakeMessage& rhs) const {
return tag_ == rhs.tag_ && tag_value_map_ == rhs.tag_value_map_ &&
minimum_size_ == rhs.minimum_size_;
}
bool CryptoHandshakeMessage::operator!=(
const CryptoHandshakeMessage& rhs) const {
return !(*this == rhs);
}
void CryptoHandshakeMessage::Clear() {
tag_ = 0;
tag_value_map_.clear();
minimum_size_ = 0;
serialized_.reset();
}
const QuicData& CryptoHandshakeMessage::GetSerialized() const {
if (!serialized_) {
serialized_ = CryptoFramer::ConstructHandshakeMessage(*this);
}
return *serialized_;
}
void CryptoHandshakeMessage::MarkDirty() { serialized_.reset(); }
void CryptoHandshakeMessage::SetVersionVector(
QuicTag tag, ParsedQuicVersionVector versions) {
QuicVersionLabelVector version_labels;
for (const ParsedQuicVersion& version : versions) {
version_labels.push_back(
quiche::QuicheEndian::HostToNet32(CreateQuicVersionLabel(version)));
}
SetVector(tag, version_labels);
}
void CryptoHandshakeMessage::SetVersion(QuicTag tag,
ParsedQuicVersion version) {
SetValue(tag,
quiche::QuicheEndian::HostToNet32(CreateQuicVersionLabel(version)));
}
void CryptoHandshakeMessage::SetStringPiece(QuicTag tag,
absl::string_view value) {
tag_value_map_[tag] = std::string(value);
}
void CryptoHandshakeMessage::Erase(QuicTag tag) { tag_value_map_.erase(tag); }
QuicErrorCode CryptoHandshakeMessage::GetTaglist(
QuicTag tag, QuicTagVector* out_tags) const {
auto 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->clear();
return ret;
}
size_t num_tags = it->second.size() / sizeof(QuicTag);
out_tags->resize(num_tags);
for (size_t i = 0; i < num_tags; ++i) {
QuicTag tag;
memcpy(&tag, it->second.data() + i * sizeof(tag), sizeof(tag));
(*out_tags)[i] = tag;
}
return ret;
}
QuicErrorCode CryptoHandshakeMessage::GetVersionLabelList(
QuicTag tag, QuicVersionLabelVector* out) const {
QuicErrorCode error = GetTaglist(tag, out);
if (error != QUIC_NO_ERROR) {
return error;
}
for (size_t i = 0; i < out->size(); ++i) {
(*out)[i] = quiche::QuicheEndian::HostToNet32((*out)[i]);
}
return QUIC_NO_ERROR;
}
QuicErrorCode CryptoHandshakeMessage::GetVersionLabel(
QuicTag tag, QuicVersionLabel* out) const {
QuicErrorCode error = GetUint32(tag, out);
if (error != QUIC_NO_ERROR) {
return error;
}
*out = quiche::QuicheEndian::HostToNet32(*out);
return QUIC_NO_ERROR;
}
bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag,
absl::string_view* out) const {
auto it = tag_value_map_.find(tag);
if (it == tag_value_map_.end()) {
return false;
}
*out = it->second;
return true;
}
bool CryptoHandshakeMessage::HasStringPiece(QuicTag tag) const {
return tag_value_map_.find(tag) != tag_value_map_.end();
}
QuicErrorCode CryptoHandshakeMessage::GetNthValue24(
QuicTag tag, unsigned index, absl::string_view* out) const {
absl::string_view 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<const unsigned char*>(value.data());
size_t size = static_cast<size_t>(data[0]) |
(static_cast<size_t>(data[1]) << 8) |
(static_cast<size_t>(data[2]) << 16);
value.remove_prefix(3);
if (value.size() < size) {
return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
}
if (i == index) {
*out = absl::string_view(value.data(), size);
return QUIC_NO_ERROR;
}
value.remove_prefix(size);
}
}
QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag,
uint32_t* out) const {
return GetPOD(tag, out, sizeof(uint32_t));
}
QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag,
uint64_t* out) const {
return GetPOD(tag, out, sizeof(uint64_t));
}
QuicErrorCode CryptoHandshakeMessage::GetStatelessResetToken(
QuicTag tag, StatelessResetToken* out) const {
return GetPOD(tag, out, kStatelessResetTokenLength);
}
size_t CryptoHandshakeMessage::size() const {
size_t ret = sizeof(QuicTag) + sizeof(uint16_t) /* number of entries */ +
sizeof(uint16_t) /* padding */;
ret += (sizeof(QuicTag) + sizeof(uint32_t) /* end offset */) *
tag_value_map_.size();
for (auto 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_; }
std::string CryptoHandshakeMessage::DebugString() const {
return DebugStringInternal(0);
}
QuicErrorCode CryptoHandshakeMessage::GetPOD(QuicTag tag, void* out,
size_t len) const {
auto 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;
}
std::string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
std::string ret =
std::string(2 * indent, ' ') + QuicTagToString(tag_) + "<\n";
++indent;
for (auto it = tag_value_map_.begin(); it != tag_value_map_.end(); ++it) {
ret += std::string(2 * indent, ' ') + QuicTagToString(it->first) + ": ";
bool done = false;
switch (it->first) {
case kICSL:
case kCFCW:
case kSFCW:
case kIRTT:
case kMIUS:
case kMIBS:
case kTCID:
case kMAD:
// uint32_t value
if (it->second.size() == 4) {
uint32_t value;
memcpy(&value, it->second.data(), sizeof(value));
absl::StrAppend(&ret, value);
done = true;
}
break;
case kKEXS:
case kAEAD:
case kCOPT:
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 += "'" + QuicTagToString(tag) + "'";
}
done = true;
}
break;
case kRREJ:
// uint32_t lists
if (it->second.size() % sizeof(uint32_t) == 0) {
for (size_t j = 0; j < it->second.size(); j += sizeof(uint32_t)) {
uint32_t value;
memcpy(&value, it->second.data() + j, sizeof(value));
if (j > 0) {
ret += ",";
}
ret += CryptoUtils::HandshakeFailureReasonToString(
static_cast<HandshakeFailureReason>(value));
}
done = true;
}
break;
case kCADR:
// IP address and port
if (!it->second.empty()) {
QuicSocketAddressCoder decoder;
if (decoder.Decode(it->second.data(), it->second.size())) {
ret += QuicSocketAddress(decoder.ip(), decoder.port()).ToString();
done = true;
}
}
break;
case kSCFG:
// nested messages.
if (!it->second.empty()) {
std::unique_ptr<CryptoHandshakeMessage> msg(
CryptoFramer::ParseMessage(it->second));
if (msg) {
ret += "\n";
ret += msg->DebugStringInternal(indent + 1);
done = true;
}
}
break;
case kPAD:
ret += absl::StrFormat("(%d bytes of padding)", it->second.size());
done = true;
break;
case kSNI:
case kUAID:
ret += "\"" + it->second + "\"";
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" + absl::BytesToHexString(it->second);
}
ret += "\n";
}
--indent;
ret += std::string(2 * indent, ' ') + ">";
return ret;
}
} // namespace quic