Skip to content

Commit

Permalink
quic: updates to implement for h3-29
Browse files Browse the repository at this point in the history
PR-URL: #34033
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
  • Loading branch information
jasnell committed Jun 24, 2020
1 parent ec7ad1d commit 86e67aa
Show file tree
Hide file tree
Showing 24 changed files with 227 additions and 337 deletions.
8 changes: 6 additions & 2 deletions doc/api/quic.md
Expand Up @@ -1539,9 +1539,11 @@ added: REPLACEME
`object.passphrase` if provided, or `options.passphrase` if it is not.
* `activeConnectionIdLimit` {number} Must be a value between `2` and `8`
(inclusive). Default: `2`.
* `congestionAlgorithm` {string} Must be either `'reno'` or `'cubic'`.
**Default**: `'reno'`.
* `maxAckDelay` {number}
* `maxData` {number}
* `maxPacketSize` {number}
* `maxUdpPayloadSize` {number}
* `maxStreamDataBidiLocal` {number}
* `maxStreamDataBidiRemote` {number}
* `maxStreamDataUni` {number}
Expand Down Expand Up @@ -1706,9 +1708,11 @@ added: REPLACEME
`object.passphrase` is optional. Encrypted keys will be decrypted with
`object.passphrase` if provided, or `options.passphrase` if it is not.
* `activeConnectionIdLimit` {number}
* `congestionAlgorithm` {string} Must be either `'reno'` or `'cubic'`.
**Default**: `'reno'`.
* `maxAckDelay` {number}
* `maxData` {number}
* `maxPacketSize` {number}
* `maxUdpPayloadSize` {number}
* `maxStreamsBidi` {number}
* `maxStreamsUni` {number}
* `maxStreamDataBidiLocal` {number}
Expand Down
4 changes: 2 additions & 2 deletions lib/internal/quic/core.js
Expand Up @@ -130,7 +130,7 @@ const {
constants: {
AF_INET,
AF_INET6,
IDX_QUIC_SESSION_MAX_PACKET_SIZE_DEFAULT,
NGTCP2_DEFAULT_MAX_PKTLEN,
IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI,
IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI,
IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT,
Expand Down Expand Up @@ -1633,7 +1633,7 @@ class QuicSession extends EventEmitter {
#earlyData = false;
#handshakeComplete = false;
#idleTimeout = false;
#maxPacketLength = IDX_QUIC_SESSION_MAX_PACKET_SIZE_DEFAULT;
#maxPacketLength = NGTCP2_DEFAULT_MAX_PKTLEN;
#servername = undefined;
#socket = undefined;
#statelessReset = false;
Expand Down
48 changes: 37 additions & 11 deletions lib/internal/quic/util.js
Expand Up @@ -35,7 +35,7 @@ const {
constants: {
AF_INET,
AF_INET6,
NGTCP2_ALPN_H3,
NGHTTP3_ALPN_H3,
DEFAULT_RETRYTOKEN_EXPIRATION,
DEFAULT_MAX_CONNECTIONS,
DEFAULT_MAX_CONNECTIONS_PER_HOST,
Expand All @@ -49,7 +49,8 @@ const {
IDX_QUIC_SESSION_MAX_STREAMS_UNI,
IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT,
IDX_QUIC_SESSION_MAX_ACK_DELAY,
IDX_QUIC_SESSION_MAX_PACKET_SIZE,
IDX_QUIC_SESSION_MAX_UDP_PAYLOAD_SIZE,
IDX_QUIC_SESSION_CC_ALGO,
IDX_QUIC_SESSION_CONFIG_COUNT,
IDX_QUIC_SESSION_STATE_CERT_ENABLED,
IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED,
Expand All @@ -68,6 +69,8 @@ const {
NGTCP2_NO_ERROR,
NGTCP2_MAX_CIDLEN,
NGTCP2_MIN_CIDLEN,
NGTCP2_CC_ALGO_CUBIC,
NGTCP2_CC_ALGO_RENO,
QUIC_PREFERRED_ADDRESS_IGNORE,
QUIC_PREFERRED_ADDRESS_USE,
QUIC_ERROR_APPLICATION,
Expand Down Expand Up @@ -167,11 +170,12 @@ function validateTransportParams(params) {
maxStreamsBidi,
maxStreamsUni,
idleTimeout,
maxPacketSize,
maxUdpPayloadSize,
maxAckDelay,
preferredAddress,
rejectUnauthorized,
requestCert,
congestionAlgorithm = 'reno',
h3: {
qpackMaxTableCapacity,
qpackBlockedStreams,
Expand Down Expand Up @@ -231,10 +235,10 @@ function validateTransportParams(params) {
'options.idleTimeout',
/* min */ 0);
}
if (maxPacketSize !== undefined) {
if (maxUdpPayloadSize !== undefined) {
validateInteger(
maxPacketSize,
'options.maxPacketSize',
maxUdpPayloadSize,
'options.maxUdpPayloadSize',
/* min */ 0);
}
if (maxAckDelay !== undefined) {
Expand Down Expand Up @@ -279,6 +283,23 @@ function validateTransportParams(params) {
'options.h3.maxHeaderLength',
/* min */ 0);
}
let ccAlgo = NGTCP2_CC_ALGO_RENO;
if (congestionAlgorithm !== undefined) {
validateString(congestionAlgorithm, 'options.conjestionAlgorithm');
switch (congestionAlgorithm) {
case 'reno':
// This is the default
break;
case 'cubic':
ccAlgo = NGTCP2_CC_ALGO_CUBIC;
break;
default:
throw new ERR_INVALID_ARG_VALUE(
'options.congestionAlgorithm',
congestionAlgorithm,
'is not supported');
}
}

validatePreferredAddress(preferredAddress);

Expand All @@ -291,11 +312,12 @@ function validateTransportParams(params) {
maxStreamsBidi,
maxStreamsUni,
idleTimeout,
maxPacketSize,
maxUdpPayloadSize,
maxAckDelay,
preferredAddress,
rejectUnauthorized,
requestCert,
congestionAlgorithm: ccAlgo,
h3: {
qpackMaxTableCapacity,
qpackBlockedStreams,
Expand Down Expand Up @@ -549,7 +571,7 @@ function validateQuicSocketOptions(options = {}) {
function validateQuicSocketListenOptions(options = {}) {
validateObject(options);
const {
alpn = NGTCP2_ALPN_H3,
alpn = NGHTTP3_ALPN_H3,
defaultEncoding,
highWaterMark,
requestCert,
Expand Down Expand Up @@ -656,8 +678,9 @@ function setTransportParams(config) {
maxStreamsBidi,
maxStreamsUni,
idleTimeout,
maxPacketSize,
maxUdpPayloadSize,
maxAckDelay,
congestionAlgorithm,
h3: {
qpackMaxTableCapacity,
qpackBlockedStreams,
Expand Down Expand Up @@ -698,8 +721,11 @@ function setTransportParams(config) {
maxAckDelay,
IDX_QUIC_SESSION_MAX_ACK_DELAY) |
setConfigField(sessionConfig,
maxPacketSize,
IDX_QUIC_SESSION_MAX_PACKET_SIZE);
maxUdpPayloadSize,
IDX_QUIC_SESSION_MAX_UDP_PAYLOAD_SIZE) |
setConfigField(sessionConfig,
congestionAlgorithm,
IDX_QUIC_SESSION_CC_ALGO);

sessionConfig[IDX_QUIC_SESSION_CONFIG_COUNT] = flags;

Expand Down
2 changes: 1 addition & 1 deletion src/env.h
Expand Up @@ -335,7 +335,7 @@ constexpr size_t kFsStatsBufferLength =
V(psk_string, "psk") \
V(pubkey_string, "pubkey") \
V(query_string, "query") \
V(quic_alpn_string, "h3-27") \
V(http3_alpn_string, "h3-29") \
V(rate_string, "rate") \
V(raw_string, "raw") \
V(read_host_object_string, "_readHostObject") \
Expand Down
11 changes: 8 additions & 3 deletions src/quic/node_quic.cc
Expand Up @@ -12,6 +12,7 @@
#include "node_quic_state.h"
#include "node_quic_util-inl.h"
#include "node_sockaddr-inl.h"
#include "nghttp3/nghttp3.h"

#include <memory>
#include <utility>
Expand Down Expand Up @@ -169,10 +170,11 @@ void Initialize(Local<Object> target,
V(IDX_QUIC_SESSION_MAX_STREAM_DATA_UNI) \
V(IDX_QUIC_SESSION_MAX_STREAMS_BIDI) \
V(IDX_QUIC_SESSION_MAX_STREAMS_UNI) \
V(IDX_QUIC_SESSION_MAX_PACKET_SIZE) \
V(IDX_QUIC_SESSION_MAX_UDP_PAYLOAD_SIZE) \
V(IDX_QUIC_SESSION_ACK_DELAY_EXPONENT) \
V(IDX_QUIC_SESSION_DISABLE_MIGRATION) \
V(IDX_QUIC_SESSION_MAX_ACK_DELAY) \
V(IDX_QUIC_SESSION_CC_ALGO) \
V(IDX_QUIC_SESSION_CONFIG_COUNT) \
V(IDX_QUIC_SESSION_STATE_CERT_ENABLED) \
V(IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED) \
Expand All @@ -190,6 +192,9 @@ void Initialize(Local<Object> target,
V(NGTCP2_APP_NOERROR) \
V(NGTCP2_PATH_VALIDATION_RESULT_FAILURE) \
V(NGTCP2_PATH_VALIDATION_RESULT_SUCCESS) \
V(NGTCP2_DEFAULT_MAX_PKTLEN) \
V(NGTCP2_CC_ALGO_CUBIC) \
V(NGTCP2_CC_ALGO_RENO) \
V(QUIC_ERROR_APPLICATION) \
V(QUIC_ERROR_CRYPTO) \
V(QUIC_ERROR_SESSION) \
Expand Down Expand Up @@ -238,8 +243,8 @@ void Initialize(Local<Object> target,
NODE_DEFINE_CONSTANT(constants, AF_INET);
NODE_DEFINE_CONSTANT(constants, AF_INET6);
NODE_DEFINE_STRING_CONSTANT(constants,
NODE_STRINGIFY_HELPER(NGTCP2_ALPN_H3),
NGTCP2_ALPN_H3);
NODE_STRINGIFY_HELPER(NGHTTP3_ALPN_H3),
NGHTTP3_ALPN_H3);

target->Set(context, env->constants_string(), constants).FromJust();
}
Expand Down
46 changes: 10 additions & 36 deletions src/quic/node_quic_crypto.cc
Expand Up @@ -13,6 +13,7 @@

#include <ngtcp2/ngtcp2.h>
#include <ngtcp2/ngtcp2_crypto.h>
#include <nghttp3/nghttp3.h> // NGHTTP3_ALPN_H3
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
Expand Down Expand Up @@ -231,7 +232,6 @@ std::unique_ptr<QuicPacket> GenerateRetryPacket(
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 =
Expand All @@ -258,23 +258,23 @@ std::unique_ptr<QuicPacket> GenerateRetryPacket(
// is successful, ocid will be updated to the original connection ID encoded
// in the encrypted token.
bool InvalidRetryToken(
const ngtcp2_pkt_hd& hd,
const ngtcp2_vec& token,
const SocketAddress& addr,
QuicCID* ocid,
const uint8_t* token_secret,
uint64_t verification_expiration) {

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

ngtcp2_crypto_ctx ctx;
ngtcp2_crypto_ctx_initial(&ctx);

size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(&ctx.aead);

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

uint8_t token_key[kCryptoTokenKeylen];
uint8_t token_iv[kCryptoTokenIvlen];
Expand Down Expand Up @@ -559,10 +559,10 @@ Local<Value> GetALPNProtocol(const QuicSession& session) {
QuicCryptoContext* ctx = session.crypto_context();
Environment* env = session.env();
std::string alpn = ctx->selected_alpn();
// This supposed to be `NGTCP2_ALPN_H3 + 1`
// This supposed to be `NGHTTP3_ALPN_H3 + 1`
// Details see https://github.com/nodejs/node/issues/33959
if (alpn == &NGTCP2_ALPN_H3[1]) {
return env->quic_alpn_string();
if (alpn == &NGHTTP3_ALPN_H3[1]) {
return env->http3_alpn_string();
} else {
return ToV8Value(
env->context(),
Expand Down Expand Up @@ -800,6 +800,7 @@ void InitializeTLS(QuicSession* session, const crypto::SSLPointer& ssl) {
UNREACHABLE();
}

ngtcp2_conn_set_tls_native_handle(session->connection(), ssl.get());
SetTransportParams(session, ssl);
}

Expand Down Expand Up @@ -859,33 +860,6 @@ void InitializeSecureContext(
SSL_CTX_set_quic_method(**sc, &quic_method);
}

bool DeriveAndInstallInitialKey(
const QuicSession& session,
const QuicCID& dcid) {
uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
uint8_t rx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN];
uint8_t tx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN];
uint8_t rx_hp[NGTCP2_CRYPTO_INITIAL_KEYLEN];
uint8_t tx_hp[NGTCP2_CRYPTO_INITIAL_KEYLEN];
uint8_t rx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN];
uint8_t tx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN];
return NGTCP2_OK(ngtcp2_crypto_derive_and_install_initial_key(
session.connection(),
rx_secret,
tx_secret,
initial_secret,
rx_key,
rx_iv,
rx_hp,
tx_key,
tx_iv,
tx_hp,
dcid.cid(),
session.crypto_context()->side()));
}

ngtcp2_crypto_level from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level) {
switch (ossl_level) {
case ssl_encryption_initial:
Expand Down
9 changes: 1 addition & 8 deletions src/quic/node_quic_crypto.h
Expand Up @@ -44,13 +44,6 @@ void InitializeSecureContext(
// with the session.
void InitializeTLS(QuicSession* session, const crypto::SSLPointer& ssl);

// Called when the client QuicSession is created and
// when the server QuicSession first receives the
// client hello.
bool DeriveAndInstallInitialKey(
const QuicSession& session,
const QuicCID& dcid);

// 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 a QuicSocket is
Expand Down Expand Up @@ -101,7 +94,7 @@ uint32_t GenerateFlowLabel(
// the ocid will be updated to the original CID value encoded
// within the successfully validated, encrypted token.
bool InvalidRetryToken(
const ngtcp2_pkt_hd& hd,
const ngtcp2_vec& token,
const SocketAddress& addr,
QuicCID* ocid,
const uint8_t* token_secret,
Expand Down
4 changes: 2 additions & 2 deletions src/quic/node_quic_default_application.cc
Expand Up @@ -81,8 +81,8 @@ void DefaultApplication::ResumeStream(int64_t stream_id) {
}

bool DefaultApplication::ReceiveStreamData(
uint32_t flags,
int64_t stream_id,
int fin,
const uint8_t* data,
size_t datalen,
uint64_t offset) {
Expand Down Expand Up @@ -110,7 +110,7 @@ bool DefaultApplication::ReceiveStreamData(
}
CHECK(stream);

stream->ReceiveData(fin, data, datalen, offset);
stream->ReceiveData(flags, data, datalen, offset);
return true;
}

Expand Down
2 changes: 1 addition & 1 deletion src/quic/node_quic_default_application.h
Expand Up @@ -29,8 +29,8 @@ class DefaultApplication final : public QuicApplication {
}

bool ReceiveStreamData(
uint32_t flags,
int64_t stream_id,
int fin,
const uint8_t* data,
size_t datalen,
uint64_t offset) override;
Expand Down

0 comments on commit 86e67aa

Please sign in to comment.