blob: cb4521c50b05e8ca49249187ca8336dc2eb6a333 [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.
#ifndef QUICHE_QUIC_LOAD_BALANCER_LOAD_BALANCER_CONFIG_H_
#define QUICHE_QUIC_LOAD_BALANCER_LOAD_BALANCER_CONFIG_H_
#include <cstdint>
#include <optional>
#include "absl/base/attributes.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "openssl/aes.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/load_balancer/load_balancer_server_id.h"
#include "quiche/quic/platform/api/quic_export.h"
namespace quic {
namespace test {
class LoadBalancerConfigPeer;
} // namespace test
// The number of bits in the first byte used for the config ID
inline constexpr uint8_t kConfigIdBits = 3;
// The number of bits in the first byte used for the connection ID length, if
// the encoder uses this option. Otherwise, by spec it's random bits.
inline constexpr uint8_t kConnectionIdLengthBits = 8 - kConfigIdBits;
// One codepoint is reserved for unroutable connection IDs, so subtract one to
// find the maximum number of configs.
inline constexpr uint8_t kNumLoadBalancerConfigs = (1 << kConfigIdBits) - 1;
inline constexpr uint8_t kLoadBalancerKeyLen = 16;
// The spec says nonces can be 18 bytes, but 16 lets it be a uint128.
inline constexpr uint8_t kLoadBalancerMaxNonceLen = 16;
inline constexpr uint8_t kLoadBalancerMinNonceLen = 4;
inline constexpr uint8_t kNumLoadBalancerCryptoPasses = 4;
// This the base class for QUIC-LB configuration. It contains configuration
// elements usable by both encoders (servers) and decoders (load balancers).
// Confusingly, it is called "LoadBalancerConfig" because it pertains to objects
// that both servers and load balancers use to interact with each other.
class QUIC_EXPORT_PRIVATE LoadBalancerConfig {
public:
// This factory function initializes an encrypted LoadBalancerConfig and
// returns it in std::optional, which is empty if the config is invalid.
// config_id: The first two bits of the Connection Id. Must be no larger than
// 2.
// server_id_len: Expected length of the server ids associated with this
// config. Must be greater than 0 and less than 16.
// nonce_len: Length of the nonce. Must be at least 4 and no larger than 16.
// Further the server_id_len + nonce_len must be no larger than 19.
// key: The encryption key must be 16B long.
static std::optional<LoadBalancerConfig> Create(uint8_t config_id,
uint8_t server_id_len,
uint8_t nonce_len,
absl::string_view key);
// Creates an unencrypted config.
static std::optional<LoadBalancerConfig> CreateUnencrypted(
uint8_t config_id, uint8_t server_id_len, uint8_t nonce_len);
// Returns an invalid Server ID if ciphertext is too small, or needed keys are
// missing. |ciphertext| contains the full connection ID minus the first byte.
//
// IMPORTANT: The decoder data path is likely the most performance-sensitive
// part of the load balancer design, and this code has been carefully
// optimized for performance. Please do not make changes without running the
// benchmark tests to ensure there is no regression.
bool FourPassDecrypt(absl::Span<const uint8_t> ciphertext,
LoadBalancerServerId& server_id) const;
// Returns an empty connection ID if the plaintext is too small, or needed
// keys are missing. |plaintext| contains the full unencrypted connection ID,
// including the first byte.
QuicConnectionId FourPassEncrypt(absl::Span<uint8_t> plaintext) const;
// Use the key to do a block encryption, which is used both in all cases of
// encrypted configs. Returns false if there's no key. Type char is
// convenient because that's what QuicConnectionId uses.
ABSL_MUST_USE_RESULT bool BlockEncrypt(
const uint8_t plaintext[kLoadBalancerBlockSize],
uint8_t ciphertext[kLoadBalancerBlockSize]) const;
// Returns false if the config does not require block decryption.
ABSL_MUST_USE_RESULT bool BlockDecrypt(
const uint8_t ciphertext[kLoadBalancerBlockSize],
uint8_t plaintext[kLoadBalancerBlockSize]) const;
uint8_t config_id() const { return config_id_; }
uint8_t server_id_len() const { return server_id_len_; }
uint8_t nonce_len() const { return nonce_len_; }
// Returns length of all but the first octet.
uint8_t plaintext_len() const { return server_id_len_ + nonce_len_; }
// Returns length of the entire connection ID.
uint8_t total_len() const { return server_id_len_ + nonce_len_ + 1; }
bool IsEncrypted() const { return key_.has_value(); }
private:
friend class test::LoadBalancerConfigPeer;
// Constructor is private because it doesn't validate input.
LoadBalancerConfig(uint8_t config_id, uint8_t server_id_len,
uint8_t nonce_len, absl::string_view key);
// Initialize state for 4-pass encryption passes, using the connection ID
// provided in |input|. Returns true if the plaintext is an odd number of
// bytes. |half_len| is half the length of the plaintext, rounded up.
bool InitializeFourPass(const uint8_t* input, uint8_t* left, uint8_t* right,
uint8_t* half_len) const;
// Handles one pass of 4-pass encryption for both encrypt and decrypt.
void EncryptionPass(uint8_t index, uint8_t half_len, bool is_length_odd,
uint8_t* left, uint8_t* right) const;
uint8_t config_id_;
uint8_t server_id_len_;
uint8_t nonce_len_;
// All Connection ID encryption and decryption uses the AES_encrypt function
// at root, so there is a single key for all of it. This is empty if the
// config is not encrypted.
std::optional<AES_KEY> key_;
// The one exception is that when total_len == 16, connection ID decryption
// uses AES_decrypt. The bytes that comprise the key are the same, but
// AES_decrypt requires an AES_KEY that is initialized differently. In all
// other cases, block_decrypt_key_ is empty.
std::optional<AES_KEY> block_decrypt_key_;
};
} // namespace quic
#endif // QUICHE_QUIC_LOAD_BALANCER_LOAD_BALANCER_CONFIG_H_