%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.cc |
#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC
#include "tokens.h"
#include <crypto/crypto_util.h>
#include <ngtcp2/ngtcp2_crypto.h>
#include <node_sockaddr-inl.h>
#include <string_bytes.h>
#include <util-inl.h>
#include <algorithm>
namespace node {
namespace quic {
// ============================================================================
// TokenSecret
TokenSecret::TokenSecret() : buf_() {
// As a performance optimization later, we could consider creating an entropy
// cache here similar to what we use for random CIDs so that we do not have
// to engage CSPRNG on every call. That, however, is suboptimal for secrets.
// If someone manages to get visibility into that cache then they would know
// the secrets for a larger number of tokens, which could be bad. For now,
// generating on each call is safer, even if less performant.
CHECK(crypto::CSPRNG(buf_, QUIC_TOKENSECRET_LEN).is_ok());
}
TokenSecret::TokenSecret(const uint8_t* secret) : buf_() {
CHECK_NOT_NULL(secret);
memcpy(buf_, secret, QUIC_TOKENSECRET_LEN);
}
TokenSecret::~TokenSecret() {
memset(buf_, 0, QUIC_TOKENSECRET_LEN);
}
TokenSecret::operator const uint8_t*() const {
return buf_;
}
uint8_t TokenSecret::operator[](int pos) const {
CHECK_GE(pos, 0);
CHECK_LT(pos, QUIC_TOKENSECRET_LEN);
return buf_[pos];
}
TokenSecret::operator const char*() const {
return reinterpret_cast<const char*>(buf_);
}
std::string TokenSecret::ToString() const {
char dest[QUIC_TOKENSECRET_LEN * 2];
size_t written = StringBytes::hex_encode(
*this, QUIC_TOKENSECRET_LEN, dest, arraysize(dest));
DCHECK_EQ(written, arraysize(dest));
return std::string(dest, written);
}
// ============================================================================
// StatelessResetToken
StatelessResetToken::StatelessResetToken() : ptr_(nullptr), buf_() {}
StatelessResetToken::StatelessResetToken(const uint8_t* token) : ptr_(token) {}
StatelessResetToken::StatelessResetToken(const TokenSecret& secret,
const CID& cid)
: ptr_(buf_) {
CHECK_EQ(ngtcp2_crypto_generate_stateless_reset_token(
buf_, secret, kStatelessTokenLen, cid),
0);
}
StatelessResetToken::StatelessResetToken(uint8_t* token,
const TokenSecret& secret,
const CID& cid)
: ptr_(token) {
CHECK_EQ(ngtcp2_crypto_generate_stateless_reset_token(
token, secret, kStatelessTokenLen, cid),
0);
}
StatelessResetToken::StatelessResetToken(const StatelessResetToken& other)
: ptr_(buf_) {
if (other) {
memcpy(buf_, other.ptr_, kStatelessTokenLen);
} else {
ptr_ = nullptr;
}
}
StatelessResetToken::operator const uint8_t*() const {
return ptr_ != nullptr ? ptr_ : buf_;
}
StatelessResetToken::operator const char*() const {
return reinterpret_cast<const char*>(ptr_ != nullptr ? ptr_ : buf_);
}
StatelessResetToken::operator bool() const {
return ptr_ != nullptr;
}
bool StatelessResetToken::operator==(const StatelessResetToken& other) const {
if (ptr_ == other.ptr_) return true;
if ((ptr_ == nullptr && other.ptr_ != nullptr) ||
(ptr_ != nullptr && other.ptr_ == nullptr)) {
return false;
}
return memcmp(ptr_, other.ptr_, kStatelessTokenLen) == 0;
}
bool StatelessResetToken::operator!=(const StatelessResetToken& other) const {
return !(*this == other);
}
std::string StatelessResetToken::ToString() const {
if (ptr_ == nullptr) return std::string();
char dest[kStatelessTokenLen * 2];
size_t written =
StringBytes::hex_encode(*this, kStatelessTokenLen, dest, arraysize(dest));
DCHECK_EQ(written, arraysize(dest));
return std::string(dest, written);
}
size_t StatelessResetToken::Hash::operator()(
const StatelessResetToken& token) const {
size_t hash = 0;
if (token.ptr_ == nullptr) return hash;
for (size_t n = 0; n < kStatelessTokenLen; n++)
hash ^= std::hash<uint8_t>{}(token.ptr_[n]) + 0x9e3779b9 + (hash << 6) +
(hash >> 2);
return hash;
}
StatelessResetToken StatelessResetToken::kInvalid;
// ============================================================================
// RetryToken and RegularToken
namespace {
ngtcp2_vec GenerateRetryToken(uint8_t* buffer,
uint32_t version,
const SocketAddress& address,
const CID& retry_cid,
const CID& odcid,
const TokenSecret& token_secret) {
ssize_t ret =
ngtcp2_crypto_generate_retry_token(buffer,
token_secret,
TokenSecret::QUIC_TOKENSECRET_LEN,
version,
address.data(),
address.length(),
retry_cid,
odcid,
uv_hrtime());
DCHECK_GE(ret, 0);
DCHECK_LE(ret, RetryToken::kRetryTokenLen);
DCHECK_EQ(buffer[0], RetryToken::kTokenMagic);
// This shouldn't be possible but we handle it anyway just to be safe.
if (ret == 0) return {nullptr, 0};
return {buffer, static_cast<size_t>(ret)};
}
ngtcp2_vec GenerateRegularToken(uint8_t* buffer,
uint32_t version,
const SocketAddress& address,
const TokenSecret& token_secret) {
ssize_t ret =
ngtcp2_crypto_generate_regular_token(buffer,
token_secret,
TokenSecret::QUIC_TOKENSECRET_LEN,
address.data(),
address.length(),
uv_hrtime());
DCHECK_GE(ret, 0);
DCHECK_LE(ret, RegularToken::kRegularTokenLen);
DCHECK_EQ(buffer[0], RegularToken::kTokenMagic);
// This shouldn't be possible but we handle it anyway just to be safe.
if (ret == 0) return {nullptr, 0};
return {buffer, static_cast<size_t>(ret)};
}
} // namespace
RetryToken::RetryToken(uint32_t version,
const SocketAddress& address,
const CID& retry_cid,
const CID& odcid,
const TokenSecret& token_secret)
: buf_(),
ptr_(GenerateRetryToken(
buf_, version, address, retry_cid, odcid, token_secret)) {}
RetryToken::RetryToken(const uint8_t* token, size_t size)
: ptr_(ngtcp2_vec{const_cast<uint8_t*>(token), size}) {
DCHECK_LE(size, RetryToken::kRetryTokenLen);
DCHECK_IMPLIES(token == nullptr, size = 0);
}
std::optional<CID> RetryToken::Validate(uint32_t version,
const SocketAddress& addr,
const CID& dcid,
const TokenSecret& token_secret,
uint64_t verification_expiration) {
if (ptr_.base == nullptr || ptr_.len == 0) return std::nullopt;
ngtcp2_cid ocid;
int ret = ngtcp2_crypto_verify_retry_token(
&ocid,
ptr_.base,
ptr_.len,
token_secret,
TokenSecret::QUIC_TOKENSECRET_LEN,
version,
addr.data(),
addr.length(),
dcid,
std::min(verification_expiration, QUIC_MIN_RETRYTOKEN_EXPIRATION),
uv_hrtime());
if (ret != 0) return std::nullopt;
return std::optional<CID>(ocid);
}
RetryToken::operator const ngtcp2_vec&() const {
return ptr_;
}
RetryToken::operator const ngtcp2_vec*() const {
return &ptr_;
}
std::string RetryToken::ToString() const {
if (ptr_.base == nullptr) return std::string();
MaybeStackBuffer<char, 32> dest(ptr_.len * 2);
size_t written =
StringBytes::hex_encode(*this, ptr_.len, dest.out(), dest.length());
DCHECK_EQ(written, dest.length());
return std::string(dest.out(), written);
}
RetryToken::operator const char*() const {
return reinterpret_cast<const char*>(ptr_.base);
}
RetryToken::operator bool() const {
return ptr_.base != nullptr && ptr_.len > 0;
}
RegularToken::RegularToken() : buf_(), ptr_(ngtcp2_vec{nullptr, 0}) {}
RegularToken::RegularToken(uint32_t version,
const SocketAddress& address,
const TokenSecret& token_secret)
: buf_(),
ptr_(GenerateRegularToken(buf_, version, address, token_secret)) {}
RegularToken::RegularToken(const uint8_t* token, size_t size)
: ptr_(ngtcp2_vec{const_cast<uint8_t*>(token), size}) {
DCHECK_LE(size, RegularToken::kRegularTokenLen);
DCHECK_IMPLIES(token == nullptr, size = 0);
}
RegularToken::operator bool() const {
return ptr_.base != nullptr && ptr_.len > 0;
}
bool RegularToken::Validate(uint32_t version,
const SocketAddress& addr,
const TokenSecret& token_secret,
uint64_t verification_expiration) {
if (ptr_.base == nullptr || ptr_.len == 0) return false;
return ngtcp2_crypto_verify_regular_token(
ptr_.base,
ptr_.len,
token_secret,
TokenSecret::QUIC_TOKENSECRET_LEN,
addr.data(),
addr.length(),
std::min(verification_expiration,
QUIC_MIN_REGULARTOKEN_EXPIRATION),
uv_hrtime()) == 0;
}
RegularToken::operator const ngtcp2_vec&() const {
return ptr_;
}
RegularToken::operator const ngtcp2_vec*() const {
return &ptr_;
}
std::string RegularToken::ToString() const {
if (ptr_.base == nullptr) return std::string();
MaybeStackBuffer<char, 32> dest(ptr_.len * 2);
size_t written =
StringBytes::hex_encode(*this, ptr_.len, dest.out(), dest.length());
DCHECK_EQ(written, dest.length());
return std::string(dest.out(), written);
}
RegularToken::operator const char*() const {
return reinterpret_cast<const char*>(ptr_.base);
}
} // namespace quic
} // namespace node
#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC