blob: 436c68306b1563a1d1c254d1139034f59d07f9e5 [file] [log] [blame]
/** @file
*
* A brief file description
*
* @section license License
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "QUICPacketFactory.h"
#include "QUICPacketProtectionKeyInfo.h"
#include "QUICDebugNames.h"
using namespace std::literals;
static constexpr std::string_view tag = "quic_packet"sv;
static constexpr std::string_view tag_v = "v_quic_packet"sv;
#define QUICDebug(dcid, scid, fmt, ...) \
Debug(tag.data(), "[%08" PRIx32 "-%08" PRIx32 "] " fmt, dcid.h32(), scid.h32(), ##__VA_ARGS__);
#define QUICVDebug(dcid, scid, fmt, ...) \
Debug(tag_v.data(), "[%08" PRIx32 "-%08" PRIx32 "] " fmt, dcid.h32(), scid.h32(), ##__VA_ARGS__);
//
// QUICPacketNumberGenerator
//
QUICPacketNumberGenerator::QUICPacketNumberGenerator() {}
QUICPacketNumber
QUICPacketNumberGenerator::next()
{
// TODO Increment the number at least one but not only always one
return this->_current++;
}
void
QUICPacketNumberGenerator::reset()
{
this->_current = 0;
}
//
// QUICPacketFactory
//
QUICPacketUPtr
QUICPacketFactory::create_null_packet()
{
return {nullptr, &QUICPacketDeleter::delete_null_packet};
}
QUICPacketUPtr
QUICPacketFactory::create(uint8_t *packet_buf, UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, ats_unique_buf buf,
size_t len, QUICPacketNumber base_packet_number, QUICPacketCreationResult &result)
{
QUICPacket *packet = nullptr;
// FIXME This is temporal. Receive IOBufferBlock from the caller.
Ptr<IOBufferBlock> whole_data = make_ptr<IOBufferBlock>(new_IOBufferBlock());
whole_data->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K));
memcpy(whole_data->start(), buf.get(), len);
whole_data->fill(len);
QUICPacketType type;
QUICVersion version;
QUICConnectionId dcid;
QUICConnectionId scid;
QUICPacketNumber packet_number;
QUICKeyPhase key_phase;
if (QUICPacketR::read_essential_info(whole_data, type, version, dcid, scid, packet_number, base_packet_number, key_phase)) {
QUICVDebug(scid, dcid, "Decrypting %s packet #%" PRIu64 " using %s", QUICDebugNames::packet_type(type), packet_number,
QUICDebugNames::key_phase(key_phase));
if (type != QUICPacketType::PROTECTED && !QUICTypeUtil::is_supported_version(version)) {
if (type == QUICPacketType::VERSION_NEGOTIATION) {
packet = new QUICVersionNegotiationPacketR(udp_con, from, to, whole_data);
result = QUICPacketCreationResult::SUCCESS;
} else {
// We can't decrypt packets that have unknown versions
// What we can use is invariant field of Long Header - version, dcid, and scid
result = QUICPacketCreationResult::UNSUPPORTED;
}
} else {
Ptr<IOBufferBlock> plain;
switch (type) {
case QUICPacketType::STATELESS_RESET:
packet = new (packet_buf) QUICStatelessResetPacketR(udp_con, from, to, whole_data);
result = QUICPacketCreationResult::SUCCESS;
break;
case QUICPacketType::RETRY:
packet = new (packet_buf) QUICRetryPacketR(udp_con, from, to, whole_data);
result = QUICPacketCreationResult::SUCCESS;
break;
case QUICPacketType::PROTECTED:
packet = new (packet_buf) QUICShortHeaderPacketR(udp_con, from, to, whole_data, base_packet_number);
if (this->_pp_key_info.is_decryption_key_available(packet->key_phase())) {
plain = this->_pp_protector.unprotect(packet->header_block(), packet->payload_block(), packet->packet_number(),
packet->key_phase());
if (plain != nullptr) {
static_cast<QUICShortHeaderPacketR *>(packet)->attach_payload(plain, true);
result = QUICPacketCreationResult::SUCCESS;
} else {
result = QUICPacketCreationResult::FAILED;
}
} else {
result = QUICPacketCreationResult::NOT_READY;
}
break;
case QUICPacketType::INITIAL:
packet = new (packet_buf) QUICInitialPacketR(udp_con, from, to, whole_data, base_packet_number);
if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::INITIAL)) {
plain = this->_pp_protector.unprotect(packet->header_block(), packet->payload_block(), packet->packet_number(),
packet->key_phase());
if (plain != nullptr) {
static_cast<QUICInitialPacketR *>(packet)->attach_payload(plain, true);
result = QUICPacketCreationResult::SUCCESS;
} else {
result = QUICPacketCreationResult::FAILED;
}
} else {
result = QUICPacketCreationResult::IGNORED;
}
break;
case QUICPacketType::HANDSHAKE:
packet = new (packet_buf) QUICHandshakePacketR(udp_con, from, to, whole_data, base_packet_number);
if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::HANDSHAKE)) {
plain = this->_pp_protector.unprotect(packet->header_block(), packet->payload_block(), packet->packet_number(),
packet->key_phase());
if (plain != nullptr) {
static_cast<QUICHandshakePacketR *>(packet)->attach_payload(plain, true);
result = QUICPacketCreationResult::SUCCESS;
} else {
result = QUICPacketCreationResult::FAILED;
}
} else {
result = QUICPacketCreationResult::IGNORED;
}
break;
case QUICPacketType::ZERO_RTT_PROTECTED:
packet = new (packet_buf) QUICZeroRttPacketR(udp_con, from, to, whole_data, base_packet_number);
if (this->_pp_key_info.is_decryption_key_available(QUICKeyPhase::ZERO_RTT)) {
plain = this->_pp_protector.unprotect(packet->header_block(), packet->payload_block(), packet->packet_number(),
packet->key_phase());
if (plain != nullptr) {
static_cast<QUICZeroRttPacketR *>(packet)->attach_payload(plain, true);
result = QUICPacketCreationResult::SUCCESS;
} else {
result = QUICPacketCreationResult::IGNORED;
}
} else {
result = QUICPacketCreationResult::NOT_READY;
}
break;
default:
result = QUICPacketCreationResult::FAILED;
break;
}
}
} else {
Debug(tag.data(), "Failed to read essential field");
uint8_t *buf = reinterpret_cast<uint8_t *>(whole_data->start());
if (len > 16) {
Debug(tag_v.data(), "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2],
buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
}
if (len > 32) {
Debug(tag_v.data(), "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", buf[16 + 0],
buf[16 + 1], buf[16 + 2], buf[16 + 3], buf[16 + 4], buf[16 + 5], buf[16 + 6], buf[16 + 7], buf[16 + 8], buf[16 + 9],
buf[16 + 10], buf[16 + 11], buf[16 + 12], buf[16 + 13], buf[16 + 14], buf[16 + 15]);
}
if (len > 48) {
Debug(tag_v.data(), "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", buf[32 + 0],
buf[32 + 1], buf[32 + 2], buf[32 + 3], buf[32 + 4], buf[32 + 5], buf[32 + 6], buf[32 + 7], buf[32 + 8], buf[32 + 9],
buf[32 + 10], buf[32 + 11], buf[32 + 12], buf[32 + 13], buf[32 + 14], buf[32 + 15]);
}
result = QUICPacketCreationResult::FAILED;
}
if (result != QUICPacketCreationResult::SUCCESS && result != QUICPacketCreationResult::UNSUPPORTED) {
packet = nullptr;
}
return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free);
}
QUICPacketUPtr
QUICPacketFactory::create_version_negotiation_packet(QUICConnectionId dcid, QUICConnectionId scid)
{
return QUICPacketUPtr(new QUICVersionNegotiationPacket(dcid, scid, QUIC_SUPPORTED_VERSIONS, countof(QUIC_SUPPORTED_VERSIONS)),
&QUICPacketDeleter::delete_packet_new);
}
QUICPacketUPtr
QUICPacketFactory::create_initial_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid,
QUICPacketNumber base_packet_number, Ptr<IOBufferBlock> payload, size_t length,
bool ack_eliciting, bool probing, bool crypto, ats_unique_buf token, size_t token_len)
{
QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::INITIAL);
QUICPacketNumber pn = this->_packet_number_generator[static_cast<int>(index)].next();
QUICInitialPacket *packet = new (packet_buf) QUICInitialPacket(this->_version, destination_cid, source_cid, token_len,
std::move(token), length, pn, ack_eliciting, probing, crypto);
packet->attach_payload(payload, true); // Attach a cleartext payload with extra headers
Ptr<IOBufferBlock> protected_payload =
this->_pp_protector.protect(packet->header_block(), packet->payload_block(), packet->packet_number(), packet->key_phase());
if (protected_payload != nullptr) {
packet->attach_payload(protected_payload, false); // Replace its payload with the protected payload
} else {
QUICDebug(destination_cid, source_cid, "Failed to encrypt a packet");
packet = nullptr;
}
return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free);
}
QUICPacketUPtr
QUICPacketFactory::create_retry_packet(QUICConnectionId destination_cid, QUICConnectionId source_cid, QUICRetryToken &token)
{
return QUICPacketUPtr(new QUICRetryPacket(QUIC_SUPPORTED_VERSIONS[0], destination_cid, source_cid, token),
&QUICPacketDeleter::delete_packet_new);
}
QUICPacketUPtr
QUICPacketFactory::create_handshake_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid,
QUICPacketNumber base_packet_number, Ptr<IOBufferBlock> payload, size_t length,
bool ack_eliciting, bool probing, bool crypto)
{
QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::HANDSHAKE);
QUICPacketNumber pn = this->_packet_number_generator[static_cast<int>(index)].next();
QUICHandshakePacket *packet =
new (packet_buf) QUICHandshakePacket(this->_version, destination_cid, source_cid, length, pn, ack_eliciting, probing, crypto);
packet->attach_payload(payload, true); // Attach a cleartext payload with extra headers
Ptr<IOBufferBlock> protected_payload =
this->_pp_protector.protect(packet->header_block(), packet->payload_block(), packet->packet_number(), packet->key_phase());
if (protected_payload != nullptr) {
packet->attach_payload(protected_payload, false); // Replace its payload with the protected payload
} else {
QUICDebug(destination_cid, source_cid, "Failed to encrypt a packet");
packet = nullptr;
}
return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free);
}
QUICPacketUPtr
QUICPacketFactory::create_zero_rtt_packet(uint8_t *packet_buf, QUICConnectionId destination_cid, QUICConnectionId source_cid,
QUICPacketNumber base_packet_number, Ptr<IOBufferBlock> payload, size_t length,
bool ack_eliciting, bool probing)
{
QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::ZERO_RTT);
QUICPacketNumber pn = this->_packet_number_generator[static_cast<int>(index)].next();
QUICZeroRttPacket *packet =
new (packet_buf) QUICZeroRttPacket(this->_version, destination_cid, source_cid, length, pn, ack_eliciting, probing);
packet->attach_payload(payload, true); // Attach a cleartext payload with extra headers
Ptr<IOBufferBlock> protected_payload =
this->_pp_protector.protect(packet->header_block(), packet->payload_block(), packet->packet_number(), packet->key_phase());
if (protected_payload != nullptr) {
packet->attach_payload(protected_payload, false); // Replace its payload with the protected payload
} else {
QUICDebug(destination_cid, source_cid, "Failed to encrypt a packet");
packet = nullptr;
}
return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free);
}
QUICPacketUPtr
QUICPacketFactory::create_short_header_packet(uint8_t *packet_buf, QUICConnectionId destination_cid,
QUICPacketNumber base_packet_number, Ptr<IOBufferBlock> payload, size_t length,
bool ack_eliciting, bool probing)
{
QUICPacketNumberSpace index = QUICTypeUtil::pn_space(QUICEncryptionLevel::ONE_RTT);
QUICPacketNumber pn = this->_packet_number_generator[static_cast<int>(index)].next();
// TODO Key phase should be picked up from QUICHandshakeProtocol, probably
QUICShortHeaderPacket *packet =
new (packet_buf) QUICShortHeaderPacket(destination_cid, pn, base_packet_number, QUICKeyPhase::PHASE_0, ack_eliciting, probing);
packet->attach_payload(payload, true); // Attach a cleartext payload with extra headers
Ptr<IOBufferBlock> protected_payload =
this->_pp_protector.protect(packet->header_block(), packet->payload_block(), packet->packet_number(), packet->key_phase());
if (protected_payload != nullptr) {
packet->attach_payload(protected_payload, false); // Replace its payload with the protected payload
} else {
QUICDebug(destination_cid, QUICConnectionId::ZERO(), "Failed to encrypt a packet");
packet = nullptr;
}
return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_dont_free);
}
QUICPacketUPtr
QUICPacketFactory::create_stateless_reset_packet(QUICStatelessResetToken stateless_reset_token, size_t maximum_size)
{
return QUICPacketUPtr(new QUICStatelessResetPacket(stateless_reset_token, maximum_size), &QUICPacketDeleter::delete_packet_new);
}
void
QUICPacketFactory::set_version(QUICVersion negotiated_version)
{
this->_version = negotiated_version;
}
bool
QUICPacketFactory::is_ready_to_create_protected_packet()
{
return this->_pp_key_info.is_encryption_key_available(QUICKeyPhase::PHASE_0) ||
this->_pp_key_info.is_encryption_key_available(QUICKeyPhase::PHASE_1);
}
void
QUICPacketFactory::reset()
{
for (auto i = 0; i < kPacketNumberSpace; i++) {
this->_packet_number_generator[i].reset();
}
}