blob: a946b3662b9d5fa8a275f8db537537f2c1cb1f6f [file] [log] [blame]
// Copyright (c) 2022 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/load_balancer/load_balancer_decoder.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
namespace quic {
bool LoadBalancerDecoder::AddConfig(const LoadBalancerConfig& config) {
if (config_[config.config_id()].has_value()) {
return false;
}
config_[config.config_id()] = config;
return true;
}
void LoadBalancerDecoder::DeleteConfig(uint8_t config_id) {
if (config_id >= kNumLoadBalancerConfigs) {
QUIC_BUG(quic_bug_438896865_01)
<< "Decoder deleting config with invalid config_id "
<< static_cast<int>(config_id);
return;
}
config_[config_id].reset();
}
// This is the core logic to extract a server ID given a valid config and
// connection ID of sufficient length.
absl::optional<LoadBalancerServerId> LoadBalancerDecoder::GetServerId(
const QuicConnectionId& connection_id) const {
absl::optional<uint8_t> config_id = GetConfigId(connection_id);
if (!config_id.has_value()) {
return absl::optional<LoadBalancerServerId>();
}
absl::optional<LoadBalancerConfig> config = config_[*config_id];
if (!config.has_value()) {
return absl::optional<LoadBalancerServerId>();
}
if (connection_id.length() < config->total_len()) {
// Connection ID wasn't long enough
return absl::optional<LoadBalancerServerId>();
}
// The first byte is complete. Finish the rest.
const uint8_t* data =
reinterpret_cast<const uint8_t*>(connection_id.data()) + 1;
if (!config->IsEncrypted()) { // It's a Plaintext CID.
return LoadBalancerServerId::Create(
absl::Span<const uint8_t>(data, config->server_id_len()));
}
uint8_t result[kQuicMaxConnectionIdWithLengthPrefixLength];
if (config->plaintext_len() == kLoadBalancerKeyLen) { // single pass
if (!config->BlockDecrypt(data, result)) {
return absl::optional<LoadBalancerServerId>();
}
} else {
// Do 3 or 4 passes. Only 3 are necessary if the server_id is short enough
// to fit in the first half of the connection ID (the decoder doesn't need
// to extract the nonce).
memcpy(result, data, config->plaintext_len());
uint8_t end = (config->server_id_len() > config->nonce_len()) ? 1 : 2;
for (uint8_t i = kNumLoadBalancerCryptoPasses; i >= end; i--) {
if (!config->EncryptionPass(absl::Span<uint8_t>(result), i)) {
return absl::optional<LoadBalancerServerId>();
}
}
}
return LoadBalancerServerId::Create(
absl::Span<const uint8_t>(result, config->server_id_len()));
}
absl::optional<uint8_t> LoadBalancerDecoder::GetConfigId(
const QuicConnectionId& connection_id) {
if (connection_id.IsEmpty()) {
return absl::optional<uint8_t>();
}
const uint8_t first_byte = connection_id.data()[0];
uint8_t codepoint = (first_byte >> 6);
if (codepoint < kNumLoadBalancerConfigs) {
return codepoint;
}
return absl::optional<uint8_t>();
}
} // namespace quic