%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/quic/
Upload File :
Create Path :
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

Zerion Mini Shell 1.0