%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