%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/quic/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/quic/tokens.h |
#pragma once
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC
#include <memory_tracker.h>
#include <ngtcp2/ngtcp2_crypto.h>
#include <node_internals.h>
#include <node_sockaddr.h>
#include "cid.h"
namespace node {
namespace quic {
// TokenSecrets are used to generate things like stateless reset tokens,
// retry tokens, and token packets. They are always QUIC_TOKENSECRET_LEN
// bytes in length.
//
// In the default case, token secrets will always be generated randomly.
// User code will be given the option to provide a secret directly
// however.
class TokenSecret final : public MemoryRetainer {
public:
static constexpr int QUIC_TOKENSECRET_LEN = 16;
// Generate a random secret.
TokenSecret();
// Copy the given secret. The uint8_t* is assumed
// to be QUIC_TOKENSECRET_LEN in length. Note that
// the length is not verified so care must be taken
// when this constructor is used.
explicit TokenSecret(const uint8_t* secret);
~TokenSecret();
TokenSecret(const TokenSecret&) = default;
TokenSecret& operator=(const TokenSecret&) = default;
TokenSecret(TokenSecret&&) = delete;
TokenSecret& operator=(TokenSecret&&) = delete;
operator const uint8_t*() const;
uint8_t operator[](int pos) const;
std::string ToString() const;
SET_NO_MEMORY_INFO()
SET_MEMORY_INFO_NAME(TokenSecret)
SET_SELF_SIZE(TokenSecret)
private:
operator const char*() const;
uint8_t buf_[QUIC_TOKENSECRET_LEN];
};
// A stateless reset token is used when a QUIC endpoint receives a QUIC packet
// with a short header but the associated connection ID cannot be matched to any
// known Session. In such cases, the receiver may choose to send a subtle opaque
// indication to the sending peer that state for the Session has apparently been
// lost. For any on- or off- path attacker, a stateless reset packet resembles
// any other QUIC packet with a short header. In order to be successfully
// handled as a stateless reset, the peer must have already seen a reset token
// issued to it associated with the given CID. The token itself is opaque to the
// peer that receives is but must be possible to statelessly recreate by the
// peer that originally created it. The actual implementation is Node.js
// specific but we currently defer to a utility function provided by ngtcp2.
//
// QUIC leaves the generation of stateless session tokens up to the
// implementation to figure out. The idea, however, is that it ought to be
// possible to generate a stateless reset token reliably even when all state
// for a connection has been lost. We use the cid as it is the only reliably
// consistent bit of data we have when a session is destroyed.
//
// StatlessResetTokens are always kStatelessTokenLen bytes,
// as are the secrets used to generate the token.
class StatelessResetToken final : public MemoryRetainer {
public:
static constexpr int kStatelessTokenLen = NGTCP2_STATELESS_RESET_TOKENLEN;
StatelessResetToken();
// Generates a stateless reset token using HKDF with the cid and token secret
// as input. The token secret is either provided by user code when an Endpoint
// is created or is generated randomly.
StatelessResetToken(const TokenSecret& secret, const CID& cid);
// Generates a stateless reset token using the given token storage.
// The StatelessResetToken wraps the token and does not take ownership.
// The token storage must be at least kStatelessTokenLen bytes in length.
// The length is not verified so care must be taken when using this
// constructor.
StatelessResetToken(uint8_t* token,
const TokenSecret& secret,
const CID& cid);
// Wraps the given token. Does not take over ownership of the token storage.
// The token must be at least kStatelessTokenLen bytes in length.
// The length is not verified so care must be taken when using this
// constructor.
explicit StatelessResetToken(const uint8_t* token);
StatelessResetToken(const StatelessResetToken& other);
StatelessResetToken(StatelessResetToken&&) = delete;
std::string ToString() const;
operator const uint8_t*() const;
operator bool() const;
bool operator==(const StatelessResetToken& other) const;
bool operator!=(const StatelessResetToken& other) const;
struct Hash {
size_t operator()(const StatelessResetToken& token) const;
};
SET_NO_MEMORY_INFO()
SET_MEMORY_INFO_NAME(StatelessResetToken)
SET_SELF_SIZE(StatelessResetToken)
template <typename T>
using Map =
std::unordered_map<StatelessResetToken, T, StatelessResetToken::Hash>;
static StatelessResetToken kInvalid;
private:
operator const char*() const;
const uint8_t* ptr_;
uint8_t buf_[NGTCP2_STATELESS_RESET_TOKENLEN];
};
// A RETRY packet communicates a retry token to the client. Retry tokens are
// generated only by QUIC servers for the purpose of validating the network path
// between a client and server. The content payload of the RETRY packet is
// opaque to the clientand must not be guessable by on- or off-path attackers.
//
// A QUIC server sends a RETRY token as a way of initiating explicit path
// validation in response to an initial QUIC packet. The client, upon receiving
// a RETRY, must abandon the initial connection attempt and try again with the
// received retry token included with the new initial packet sent to the server.
// If the server is performing explicit validation, it will look for the
// presence of the retry token and attempt to validate it if found. The internal
// structure of the retry token must be meaningful to the server, and the server
// must be able to validate that the token is correct without relying on any
// state left over from the previous connection attempt. We use an
// implementation that is provided by ngtcp2.
//
// The token secret must be kept private on the QUIC server that generated the
// retry. When multiple QUIC servers are used in a cluster, it cannot be
// guaranteed that the same QUIC server instance will receive the subsequent new
// Initial packet. Therefore, all QUIC servers in the cluster should either
// share or be aware of the same token secret or a mechanism needs to be
// implemented to ensure that subsequent packets are routed to the same QUIC
// server instance.
class RetryToken final : public MemoryRetainer {
public:
// The token prefix that is used to differentiate between a retry token
// and a regular token.
static constexpr uint8_t kTokenMagic = NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY;
static constexpr int kRetryTokenLen = NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN;
static constexpr uint64_t QUIC_DEFAULT_RETRYTOKEN_EXPIRATION =
10 * NGTCP2_SECONDS;
static constexpr uint64_t QUIC_MIN_RETRYTOKEN_EXPIRATION = 1 * NGTCP2_SECONDS;
// Generates a new retry token.
RetryToken(uint32_t version,
const SocketAddress& address,
const CID& retry_cid,
const CID& odcid,
const TokenSecret& token_secret);
// Wraps the given retry token
RetryToken(const uint8_t* token, size_t length);
// Validates the retry token given the input. If the token is valid,
// the embedded original CID will be extracted from the token an
// returned. If the token is invalid, std::nullopt will be returned.
std::optional<CID> Validate(
uint32_t version,
const SocketAddress& address,
const CID& cid,
const TokenSecret& token_secret,
uint64_t verification_expiration = QUIC_DEFAULT_RETRYTOKEN_EXPIRATION);
operator const ngtcp2_vec&() const;
operator const ngtcp2_vec*() const;
operator bool() const;
std::string ToString() const;
SET_NO_MEMORY_INFO()
SET_MEMORY_INFO_NAME(RetryToken)
SET_SELF_SIZE(RetryToken)
private:
operator const char*() const;
uint8_t buf_[kRetryTokenLen];
const ngtcp2_vec ptr_;
};
// A NEW_TOKEN packet communicates a regular token to a client that the server
// would like the client to send in the header of an initial packet for a
// future connection. It is similar to RETRY and used for the same purpose,
// except a NEW_TOKEN is used in advance of the client establishing a new
// connection and a RETRY is sent in response to the client trying to open
// a new connection.
class RegularToken final : public MemoryRetainer {
public:
// The token prefix that is used to differentiate between a retry token
// and a regular token.
static constexpr uint8_t kTokenMagic = NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR;
static constexpr int kRegularTokenLen = NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN;
static constexpr uint64_t QUIC_DEFAULT_REGULARTOKEN_EXPIRATION =
10 * NGTCP2_SECONDS;
static constexpr uint64_t QUIC_MIN_REGULARTOKEN_EXPIRATION =
1 * NGTCP2_SECONDS;
RegularToken();
// Generates a new retry token.
RegularToken(uint32_t version,
const SocketAddress& address,
const TokenSecret& token_secret);
// Wraps the given retry token
RegularToken(const uint8_t* token, size_t length);
// Validates the retry token given the input.
bool Validate(
uint32_t version,
const SocketAddress& address,
const TokenSecret& token_secret,
uint64_t verification_expiration = QUIC_DEFAULT_REGULARTOKEN_EXPIRATION);
operator const ngtcp2_vec&() const;
operator const ngtcp2_vec*() const;
operator bool() const;
std::string ToString() const;
SET_NO_MEMORY_INFO()
SET_MEMORY_INFO_NAME(RetryToken)
SET_SELF_SIZE(RetryToken)
private:
operator const char*() const;
uint8_t buf_[kRegularTokenLen];
const ngtcp2_vec ptr_;
};
} // namespace quic
} // namespace node
#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS