Skip to content
This repository has been archived by the owner on Aug 11, 2020. It is now read-only.

Commit

Permalink
quic: update for draft-25
Browse files Browse the repository at this point in the history
PR-URL: #305
Reviewed-By: Anna Henningsen <[email protected]>
  • Loading branch information
jasnell committed Feb 3, 2020
1 parent bfda347 commit 4686b28
Show file tree
Hide file tree
Showing 23 changed files with 308 additions and 330 deletions.
9 changes: 9 additions & 0 deletions doc/api/quic.md
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,15 @@ added: REPLACEME

Set to `true` if the TLS handshake has completed.

#### quicsession.handshakeConfirmed
<!-- YAML
added: REPLACEME
-->

* Type: {boolean}

Set to `true` when the TLS handshake completion has been confirmed.

#### quicsession.handshakeDuration
<!-- YAML
added: REPLACEME
Expand Down
8 changes: 7 additions & 1 deletion lib/internal/quic/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ const {
IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI,
IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI,
IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT,
IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED,
IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT,
IDX_QUIC_SESSION_STATS_CREATED_AT,
IDX_QUIC_SESSION_STATS_HANDSHAKE_START_AT,
Expand Down Expand Up @@ -1846,6 +1847,11 @@ class QuicSession extends EventEmitter {
return this.#handshakeComplete;
}

get handshakeConfirmed() {
return Boolean(this[kHandle] ?
this[kHandle].state[IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED] : 0);
}

get alpnProtocol() {
return this.#alpn;
}
Expand Down Expand Up @@ -2026,7 +2032,7 @@ class QuicSession extends EventEmitter {
// Initiates a key update for the connection.
if (this.#destroyed || this.#closing)
throw new ERR_QUICSESSION_DESTROYED('updateKey');
if (!this.handshakeComplete)
if (!this.handshakeConfirmed)
throw new ERR_QUICSESSION_UPDATEKEY();
return this[kHandle].updateKey();
}
Expand Down
4 changes: 2 additions & 2 deletions lib/internal/quic/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const {
IDX_QUIC_SESSION_MAX_DATA,
IDX_QUIC_SESSION_MAX_STREAMS_BIDI,
IDX_QUIC_SESSION_MAX_STREAMS_UNI,
IDX_QUIC_SESSION_IDLE_TIMEOUT,
IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT,
IDX_QUIC_SESSION_MAX_ACK_DELAY,
IDX_QUIC_SESSION_MAX_PACKET_SIZE,
IDX_QUIC_SESSION_MAX_ACTIVE_CONNECTION_ID_LIMIT,
Expand Down Expand Up @@ -561,7 +561,7 @@ function setTransportParams(config) {
IDX_QUIC_SESSION_MAX_STREAMS_UNI) |
setConfigField(sessionConfig,
idleTimeout,
IDX_QUIC_SESSION_IDLE_TIMEOUT) |
IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT) |
setConfigField(sessionConfig,
maxAckDelay,
IDX_QUIC_SESSION_MAX_ACK_DELAY) |
Expand Down
2 changes: 1 addition & 1 deletion src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ constexpr size_t kFsStatsBufferLength =
V(promise_string, "promise") \
V(pubkey_string, "pubkey") \
V(query_string, "query") \
V(quic_alpn_string, "h3-24") \
V(quic_alpn_string, "h3-25") \
V(rate_string, "rate") \
V(raw_string, "raw") \
V(read_host_object_string, "_readHostObject") \
Expand Down
5 changes: 5 additions & 0 deletions src/node_http_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include "v8.h"
#include <string>

namespace node {

Expand Down Expand Up @@ -333,6 +334,10 @@ class NgRcBufPointer : public MemoryRetainer {
return v.len;
}

std::string str() const {
return std::string(reinterpret_cast<const char*>(data()), len());
}

void reset(rcbuf_t* ptr = nullptr, bool internalizable = false) {
this->~NgRcBufPointer();
buf_ = ptr;
Expand Down
4 changes: 4 additions & 0 deletions src/node_sockaddr.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ class SocketAddress : public MemoryRetainer {
return reinterpret_cast<const sockaddr*>(&address_);
}

const uint8_t* raw() const {
return reinterpret_cast<const uint8_t*>(&address_);
}

sockaddr* storage() {
return reinterpret_cast<sockaddr*>(&address_);
}
Expand Down
3 changes: 2 additions & 1 deletion src/quic/node_quic.cc
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ void Initialize(Local<Object> target,
V(IDX_HTTP3_MAX_HEADER_LENGTH) \
V(IDX_HTTP3_CONFIG_COUNT) \
V(IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT) \
V(IDX_QUIC_SESSION_IDLE_TIMEOUT) \
V(IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT) \
V(IDX_QUIC_SESSION_MAX_DATA) \
V(IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL) \
V(IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE) \
Expand All @@ -171,6 +171,7 @@ void Initialize(Local<Object> target,
V(IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI) \
V(IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT) \
V(IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT) \
V(IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED) \
V(MAX_RETRYTOKEN_EXPIRATION) \
V(MIN_RETRYTOKEN_EXPIRATION) \
V(NGTCP2_APP_NOERROR) \
Expand Down
123 changes: 75 additions & 48 deletions src/quic/node_quic_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@ constexpr char kQuicServerHandshakeTrafficSecret[] =
"QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET";
constexpr char kQuicServerTrafficSecret[] =
"QUIC_SERVER_TRAFFIC_SECRET_0";
} // namespace

namespace {
// Used solely to derive the keys used to generate retry tokens.
bool DeriveTokenKey(
uint8_t* token_key,
Expand Down Expand Up @@ -100,26 +98,11 @@ void GenerateRandData(uint8_t* buf, size_t len) {
CHECK_LE(len, arraysize(md));
std::copy_n(std::begin(md), len, buf);
}
} // namespace

bool GenerateResetToken(
uint8_t* token,
const uint8_t* secret,
const QuicCID& cid) {
ngtcp2_crypto_ctx ctx;
ngtcp2_crypto_ctx_initial(&ctx);
return NGTCP2_OK(ngtcp2_crypto_generate_stateless_reset_token(
token,
&ctx.md,
secret,
NGTCP2_STATELESS_RESET_TOKENLEN,
cid.cid()));
}

bool GenerateRetryToken(
uint8_t* token,
size_t* tokenlen,
const sockaddr* addr,
const SocketAddress& addr,
const QuicCID& ocid,
const uint8_t* token_secret) {
std::array<uint8_t, 4096> plaintext;
Expand All @@ -129,15 +112,12 @@ bool GenerateRetryToken(

ngtcp2_crypto_ctx ctx;
ngtcp2_crypto_ctx_initial(&ctx);

const size_t addrlen = SocketAddress::GetLength(addr);
size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(&ctx.aead);

uint64_t now = uv_hrtime();

auto p = std::begin(plaintext);
p = std::copy_n(reinterpret_cast<const uint8_t*>(addr), addrlen, p);
p = std::copy_n(reinterpret_cast<uint8_t*>(&now), sizeof(now), p);
p = std::copy_n(addr.raw(), addr.length(), p);
p = std::copy_n(reinterpret_cast<uint8_t*>(&now), sizeof(uint64_t), p);
p = std::copy_n(ocid->data, ocid->datalen, p);

GenerateRandData(rand_data, kTokenRandLen);
Expand All @@ -161,8 +141,8 @@ bool GenerateRetryToken(
token_key,
token_iv,
ivlen,
reinterpret_cast<const uint8_t *>(addr),
addrlen))) {
addr.raw(),
addr.length()))) {
return false;
}

Expand All @@ -171,28 +151,78 @@ bool GenerateRetryToken(
*tokenlen += kTokenRandLen;
return true;
}
} // namespace

bool GenerateResetToken(
uint8_t* token,
const uint8_t* secret,
const QuicCID& cid) {
ngtcp2_crypto_ctx ctx;
ngtcp2_crypto_ctx_initial(&ctx);
return NGTCP2_OK(ngtcp2_crypto_generate_stateless_reset_token(
token,
&ctx.md,
secret,
NGTCP2_STATELESS_RESET_TOKENLEN,
cid.cid()));
}


std::unique_ptr<QuicPacket> GenerateRetryPacket(
const uint8_t* token_secret,
const QuicCID& dcid,
const QuicCID& scid,
const SocketAddress& local_addr,
const SocketAddress& remote_addr) {

uint8_t token[256];
size_t tokenlen = sizeof(token);

if (!GenerateRetryToken(token, &tokenlen, remote_addr, dcid, token_secret))
return {};

QuicCID cid;
EntropySource(cid.data(), kScidLen);
cid.set_length(kScidLen);

size_t pktlen = tokenlen + (2 * NGTCP2_MAX_CIDLEN) + scid.length() + 8;
CHECK_LE(pktlen, NGTCP2_MAX_PKT_SIZE);

auto packet = QuicPacket::Create("retry", pktlen);
ssize_t nwrite =
ngtcp2_crypto_write_retry(
packet->data(),
NGTCP2_MAX_PKTLEN_IPV4,
scid.cid(),
cid.cid(),
dcid.cid(),
token,
tokenlen);
if (nwrite <= 0)
return {};
packet->set_length(nwrite);
return std::move(packet);
}

// True if the received retry token is invalid.
bool InvalidRetryToken(
const uint8_t* token,
size_t tokenlen,
const sockaddr* addr,
const ngtcp2_pkt_hd& hd,
const SocketAddress& addr,
QuicCID* ocid,
const uint8_t* token_secret,
uint64_t verification_expiration) {

if (tokenlen < kTokenRandLen)
if (hd.tokenlen < kTokenRandLen)
return true;

ngtcp2_crypto_ctx ctx;
ngtcp2_crypto_ctx_initial(&ctx);

size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(&ctx.aead);
const size_t addrlen = SocketAddress::GetLength(addr);

size_t ciphertextlen = tokenlen - kTokenRandLen;
const uint8_t* ciphertext = token;
const uint8_t* rand_data = token + ciphertextlen;
size_t ciphertextlen = hd.tokenlen - kTokenRandLen;
const uint8_t* ciphertext = hd.token;
const uint8_t* rand_data = hd.token + ciphertextlen;

uint8_t token_key[kCryptoTokenKeylen];
uint8_t token_iv[kCryptoTokenIvlen];
Expand All @@ -207,44 +237,43 @@ bool InvalidRetryToken(
return true;
}

std::array<uint8_t, 4096> plaintext;
uint8_t plaintext[4096];

if (NGTCP2_ERR(ngtcp2_crypto_decrypt(
plaintext.data(),
plaintext,
&ctx.aead,
ciphertext,
ciphertextlen,
token_key,
token_iv,
ivlen,
reinterpret_cast<const uint8_t*>(addr), addrlen))) {
addr.raw(),
addr.length()))) {
return true;
}

size_t plaintextlen = ciphertextlen - ngtcp2_crypto_aead_taglen(&ctx.aead);
if (plaintextlen < addrlen + sizeof(uint64_t))
if (plaintextlen < addr.length() + sizeof(uint64_t))
return true;

ssize_t cil = plaintextlen - addrlen - sizeof(uint64_t);
ssize_t cil = plaintextlen - addr.length() - sizeof(uint64_t);
if ((cil != 0 && (cil < NGTCP2_MIN_CIDLEN || cil > NGTCP2_MAX_CIDLEN)) ||
memcmp(plaintext.data(), addr, addrlen) != 0) {
memcmp(plaintext, addr.raw(), addr.length()) != 0) {
return true;
}

uint64_t t;
memcpy(&t, plaintext.data() + addrlen, sizeof(uint64_t));

uint64_t now = uv_hrtime();
memcpy(&t, plaintext + addr.length(), sizeof(uint64_t));

// 10-second window by default, but configurable for each
// QuicSocket instance with a MIN_RETRYTOKEN_EXPIRATION second
// minimum and a MAX_RETRYTOKEN_EXPIRATION second maximum.
if (t + verification_expiration * NGTCP2_SECONDS < now)
if (t + verification_expiration * NGTCP2_SECONDS < uv_hrtime())
return true;

ngtcp2_cid_init(
ocid->cid(),
plaintext.data() + addrlen + sizeof(uint64_t),
plaintext + addr.length() + sizeof(uint64_t),
cil);

return false;
Expand Down Expand Up @@ -865,10 +894,8 @@ uint32_t GenerateFlowLabel(
ngtcp2_crypto_ctx_initial(&ctx);

auto p = std::begin(plaintext);
p = std::copy_n(reinterpret_cast<const uint8_t*>(local.data()),
local.length(), p);
p = std::copy_n(reinterpret_cast<const uint8_t*>(remote.data()),
remote.length(), p);
p = std::copy_n(local.raw(), local.length(), p);
p = std::copy_n(remote.raw(), remote.length(), p);
p = std::copy_n(cid->data, cid->datalen, p);

ngtcp2_crypto_hkdf_expand(
Expand Down
18 changes: 9 additions & 9 deletions src/quic/node_quic_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace quic {

// Forward declaration
class QuicSession;
class QuicPacket;

#define NGTCP2_ERR(V) (V != 0)
#define NGTCP2_OK(V) (V == 0)
Expand Down Expand Up @@ -78,12 +79,12 @@ bool GenerateResetToken(
// * be specific to the client address
// * be specific to the original cid
// * contain random data.
bool GenerateRetryToken(
uint8_t* token,
size_t* tokenlen,
const sockaddr* addr,
const QuicCID& ocid,
const uint8_t* token_secret);
std::unique_ptr<QuicPacket> GenerateRetryPacket(
const uint8_t* token_secret,
const QuicCID& dcid,
const QuicCID& scid,
const SocketAddress& local_addr,
const SocketAddress& remote_addr);

uint32_t GenerateFlowLabel(
const SocketAddress& local,
Expand All @@ -95,9 +96,8 @@ uint32_t GenerateFlowLabel(
// Verifies the validity of a retry token. Returns true if the
// token is not valid, false otherwise.
bool InvalidRetryToken(
const uint8_t* token,
size_t tokenlen,
const sockaddr* addr,
const ngtcp2_pkt_hd& hd,
const SocketAddress& addr,
QuicCID* ocid,
const uint8_t* token_secret,
uint64_t verification_expiration);
Expand Down
8 changes: 2 additions & 6 deletions src/quic/node_quic_http3_application.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,11 @@ MaybeLocal<String> Http3Header::GetValue(QuicApplication* app) const {

std::string Http3Header::name() const {
const char* header_name = ToHttpHeaderName(token_);
return header_name != nullptr ?
std::string(header_name) :
std::string(reinterpret_cast<const char*>(name_.data()), name_.len());
return header_name != nullptr ? std::string(header_name) : name_.str();
}

std::string Http3Header::value() const {
return std::string(
reinterpret_cast<const char*>(value_.data()),
value_.len());
return value_.str();
}

size_t Http3Header::length() const {
Expand Down
Loading

0 comments on commit 4686b28

Please sign in to comment.