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

Commit 4686b28

Browse files
committed
quic: update for draft-25
PR-URL: #305 Reviewed-By: Anna Henningsen <[email protected]>
1 parent bfda347 commit 4686b28

23 files changed

+308
-330
lines changed

doc/api/quic.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,15 @@ added: REPLACEME
875875

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

878+
#### quicsession.handshakeConfirmed
879+
<!-- YAML
880+
added: REPLACEME
881+
-->
882+
883+
* Type: {boolean}
884+
885+
Set to `true` when the TLS handshake completion has been confirmed.
886+
878887
#### quicsession.handshakeDuration
879888
<!-- YAML
880889
added: REPLACEME

lib/internal/quic/core.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ const {
126126
IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI,
127127
IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI,
128128
IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT,
129+
IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED,
129130
IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT,
130131
IDX_QUIC_SESSION_STATS_CREATED_AT,
131132
IDX_QUIC_SESSION_STATS_HANDSHAKE_START_AT,
@@ -1846,6 +1847,11 @@ class QuicSession extends EventEmitter {
18461847
return this.#handshakeComplete;
18471848
}
18481849

1850+
get handshakeConfirmed() {
1851+
return Boolean(this[kHandle] ?
1852+
this[kHandle].state[IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED] : 0);
1853+
}
1854+
18491855
get alpnProtocol() {
18501856
return this.#alpn;
18511857
}
@@ -2026,7 +2032,7 @@ class QuicSession extends EventEmitter {
20262032
// Initiates a key update for the connection.
20272033
if (this.#destroyed || this.#closing)
20282034
throw new ERR_QUICSESSION_DESTROYED('updateKey');
2029-
if (!this.handshakeComplete)
2035+
if (!this.handshakeConfirmed)
20302036
throw new ERR_QUICSESSION_UPDATEKEY();
20312037
return this[kHandle].updateKey();
20322038
}

lib/internal/quic/util.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const {
3838
IDX_QUIC_SESSION_MAX_DATA,
3939
IDX_QUIC_SESSION_MAX_STREAMS_BIDI,
4040
IDX_QUIC_SESSION_MAX_STREAMS_UNI,
41-
IDX_QUIC_SESSION_IDLE_TIMEOUT,
41+
IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT,
4242
IDX_QUIC_SESSION_MAX_ACK_DELAY,
4343
IDX_QUIC_SESSION_MAX_PACKET_SIZE,
4444
IDX_QUIC_SESSION_MAX_ACTIVE_CONNECTION_ID_LIMIT,
@@ -561,7 +561,7 @@ function setTransportParams(config) {
561561
IDX_QUIC_SESSION_MAX_STREAMS_UNI) |
562562
setConfigField(sessionConfig,
563563
idleTimeout,
564-
IDX_QUIC_SESSION_IDLE_TIMEOUT) |
564+
IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT) |
565565
setConfigField(sessionConfig,
566566
maxAckDelay,
567567
IDX_QUIC_SESSION_MAX_ACK_DELAY) |

src/env.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ constexpr size_t kFsStatsBufferLength =
330330
V(promise_string, "promise") \
331331
V(pubkey_string, "pubkey") \
332332
V(query_string, "query") \
333-
V(quic_alpn_string, "h3-24") \
333+
V(quic_alpn_string, "h3-25") \
334334
V(rate_string, "rate") \
335335
V(raw_string, "raw") \
336336
V(read_host_object_string, "_readHostObject") \

src/node_http_common.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
55

66
#include "v8.h"
7+
#include <string>
78

89
namespace node {
910

@@ -333,6 +334,10 @@ class NgRcBufPointer : public MemoryRetainer {
333334
return v.len;
334335
}
335336

337+
std::string str() const {
338+
return std::string(reinterpret_cast<const char*>(data()), len());
339+
}
340+
336341
void reset(rcbuf_t* ptr = nullptr, bool internalizable = false) {
337342
this->~NgRcBufPointer();
338343
buf_ = ptr;

src/node_sockaddr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ class SocketAddress : public MemoryRetainer {
6969
return reinterpret_cast<const sockaddr*>(&address_);
7070
}
7171

72+
const uint8_t* raw() const {
73+
return reinterpret_cast<const uint8_t*>(&address_);
74+
}
75+
7276
sockaddr* storage() {
7377
return reinterpret_cast<sockaddr*>(&address_);
7478
}

src/quic/node_quic.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ void Initialize(Local<Object> target,
149149
V(IDX_HTTP3_MAX_HEADER_LENGTH) \
150150
V(IDX_HTTP3_CONFIG_COUNT) \
151151
V(IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT) \
152-
V(IDX_QUIC_SESSION_IDLE_TIMEOUT) \
152+
V(IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT) \
153153
V(IDX_QUIC_SESSION_MAX_DATA) \
154154
V(IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL) \
155155
V(IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE) \
@@ -171,6 +171,7 @@ void Initialize(Local<Object> target,
171171
V(IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI) \
172172
V(IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT) \
173173
V(IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT) \
174+
V(IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED) \
174175
V(MAX_RETRYTOKEN_EXPIRATION) \
175176
V(MIN_RETRYTOKEN_EXPIRATION) \
176177
V(NGTCP2_APP_NOERROR) \

src/quic/node_quic_crypto.cc

Lines changed: 75 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,7 @@ constexpr char kQuicServerHandshakeTrafficSecret[] =
5252
"QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET";
5353
constexpr char kQuicServerTrafficSecret[] =
5454
"QUIC_SERVER_TRAFFIC_SECRET_0";
55-
} // namespace
5655

57-
namespace {
5856
// Used solely to derive the keys used to generate retry tokens.
5957
bool DeriveTokenKey(
6058
uint8_t* token_key,
@@ -100,26 +98,11 @@ void GenerateRandData(uint8_t* buf, size_t len) {
10098
CHECK_LE(len, arraysize(md));
10199
std::copy_n(std::begin(md), len, buf);
102100
}
103-
} // namespace
104-
105-
bool GenerateResetToken(
106-
uint8_t* token,
107-
const uint8_t* secret,
108-
const QuicCID& cid) {
109-
ngtcp2_crypto_ctx ctx;
110-
ngtcp2_crypto_ctx_initial(&ctx);
111-
return NGTCP2_OK(ngtcp2_crypto_generate_stateless_reset_token(
112-
token,
113-
&ctx.md,
114-
secret,
115-
NGTCP2_STATELESS_RESET_TOKENLEN,
116-
cid.cid()));
117-
}
118101

119102
bool GenerateRetryToken(
120103
uint8_t* token,
121104
size_t* tokenlen,
122-
const sockaddr* addr,
105+
const SocketAddress& addr,
123106
const QuicCID& ocid,
124107
const uint8_t* token_secret) {
125108
std::array<uint8_t, 4096> plaintext;
@@ -129,15 +112,12 @@ bool GenerateRetryToken(
129112

130113
ngtcp2_crypto_ctx ctx;
131114
ngtcp2_crypto_ctx_initial(&ctx);
132-
133-
const size_t addrlen = SocketAddress::GetLength(addr);
134115
size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(&ctx.aead);
135-
136116
uint64_t now = uv_hrtime();
137117

138118
auto p = std::begin(plaintext);
139-
p = std::copy_n(reinterpret_cast<const uint8_t*>(addr), addrlen, p);
140-
p = std::copy_n(reinterpret_cast<uint8_t*>(&now), sizeof(now), p);
119+
p = std::copy_n(addr.raw(), addr.length(), p);
120+
p = std::copy_n(reinterpret_cast<uint8_t*>(&now), sizeof(uint64_t), p);
141121
p = std::copy_n(ocid->data, ocid->datalen, p);
142122

143123
GenerateRandData(rand_data, kTokenRandLen);
@@ -161,8 +141,8 @@ bool GenerateRetryToken(
161141
token_key,
162142
token_iv,
163143
ivlen,
164-
reinterpret_cast<const uint8_t *>(addr),
165-
addrlen))) {
144+
addr.raw(),
145+
addr.length()))) {
166146
return false;
167147
}
168148

@@ -171,28 +151,78 @@ bool GenerateRetryToken(
171151
*tokenlen += kTokenRandLen;
172152
return true;
173153
}
154+
} // namespace
155+
156+
bool GenerateResetToken(
157+
uint8_t* token,
158+
const uint8_t* secret,
159+
const QuicCID& cid) {
160+
ngtcp2_crypto_ctx ctx;
161+
ngtcp2_crypto_ctx_initial(&ctx);
162+
return NGTCP2_OK(ngtcp2_crypto_generate_stateless_reset_token(
163+
token,
164+
&ctx.md,
165+
secret,
166+
NGTCP2_STATELESS_RESET_TOKENLEN,
167+
cid.cid()));
168+
}
169+
170+
171+
std::unique_ptr<QuicPacket> GenerateRetryPacket(
172+
const uint8_t* token_secret,
173+
const QuicCID& dcid,
174+
const QuicCID& scid,
175+
const SocketAddress& local_addr,
176+
const SocketAddress& remote_addr) {
177+
178+
uint8_t token[256];
179+
size_t tokenlen = sizeof(token);
180+
181+
if (!GenerateRetryToken(token, &tokenlen, remote_addr, dcid, token_secret))
182+
return {};
183+
184+
QuicCID cid;
185+
EntropySource(cid.data(), kScidLen);
186+
cid.set_length(kScidLen);
187+
188+
size_t pktlen = tokenlen + (2 * NGTCP2_MAX_CIDLEN) + scid.length() + 8;
189+
CHECK_LE(pktlen, NGTCP2_MAX_PKT_SIZE);
190+
191+
auto packet = QuicPacket::Create("retry", pktlen);
192+
ssize_t nwrite =
193+
ngtcp2_crypto_write_retry(
194+
packet->data(),
195+
NGTCP2_MAX_PKTLEN_IPV4,
196+
scid.cid(),
197+
cid.cid(),
198+
dcid.cid(),
199+
token,
200+
tokenlen);
201+
if (nwrite <= 0)
202+
return {};
203+
packet->set_length(nwrite);
204+
return std::move(packet);
205+
}
174206

175207
// True if the received retry token is invalid.
176208
bool InvalidRetryToken(
177-
const uint8_t* token,
178-
size_t tokenlen,
179-
const sockaddr* addr,
209+
const ngtcp2_pkt_hd& hd,
210+
const SocketAddress& addr,
180211
QuicCID* ocid,
181212
const uint8_t* token_secret,
182213
uint64_t verification_expiration) {
183214

184-
if (tokenlen < kTokenRandLen)
215+
if (hd.tokenlen < kTokenRandLen)
185216
return true;
186217

187218
ngtcp2_crypto_ctx ctx;
188219
ngtcp2_crypto_ctx_initial(&ctx);
189220

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

193-
size_t ciphertextlen = tokenlen - kTokenRandLen;
194-
const uint8_t* ciphertext = token;
195-
const uint8_t* rand_data = token + ciphertextlen;
223+
size_t ciphertextlen = hd.tokenlen - kTokenRandLen;
224+
const uint8_t* ciphertext = hd.token;
225+
const uint8_t* rand_data = hd.token + ciphertextlen;
196226

197227
uint8_t token_key[kCryptoTokenKeylen];
198228
uint8_t token_iv[kCryptoTokenIvlen];
@@ -207,44 +237,43 @@ bool InvalidRetryToken(
207237
return true;
208238
}
209239

210-
std::array<uint8_t, 4096> plaintext;
240+
uint8_t plaintext[4096];
211241

212242
if (NGTCP2_ERR(ngtcp2_crypto_decrypt(
213-
plaintext.data(),
243+
plaintext,
214244
&ctx.aead,
215245
ciphertext,
216246
ciphertextlen,
217247
token_key,
218248
token_iv,
219249
ivlen,
220-
reinterpret_cast<const uint8_t*>(addr), addrlen))) {
250+
addr.raw(),
251+
addr.length()))) {
221252
return true;
222253
}
223254

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

228-
ssize_t cil = plaintextlen - addrlen - sizeof(uint64_t);
259+
ssize_t cil = plaintextlen - addr.length() - sizeof(uint64_t);
229260
if ((cil != 0 && (cil < NGTCP2_MIN_CIDLEN || cil > NGTCP2_MAX_CIDLEN)) ||
230-
memcmp(plaintext.data(), addr, addrlen) != 0) {
261+
memcmp(plaintext, addr.raw(), addr.length()) != 0) {
231262
return true;
232263
}
233264

234265
uint64_t t;
235-
memcpy(&t, plaintext.data() + addrlen, sizeof(uint64_t));
236-
237-
uint64_t now = uv_hrtime();
266+
memcpy(&t, plaintext + addr.length(), sizeof(uint64_t));
238267

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

245274
ngtcp2_cid_init(
246275
ocid->cid(),
247-
plaintext.data() + addrlen + sizeof(uint64_t),
276+
plaintext + addr.length() + sizeof(uint64_t),
248277
cil);
249278

250279
return false;
@@ -865,10 +894,8 @@ uint32_t GenerateFlowLabel(
865894
ngtcp2_crypto_ctx_initial(&ctx);
866895

867896
auto p = std::begin(plaintext);
868-
p = std::copy_n(reinterpret_cast<const uint8_t*>(local.data()),
869-
local.length(), p);
870-
p = std::copy_n(reinterpret_cast<const uint8_t*>(remote.data()),
871-
remote.length(), p);
897+
p = std::copy_n(local.raw(), local.length(), p);
898+
p = std::copy_n(remote.raw(), remote.length(), p);
872899
p = std::copy_n(cid->data, cid->datalen, p);
873900

874901
ngtcp2_crypto_hkdf_expand(

src/quic/node_quic_crypto.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace quic {
1717

1818
// Forward declaration
1919
class QuicSession;
20+
class QuicPacket;
2021

2122
#define NGTCP2_ERR(V) (V != 0)
2223
#define NGTCP2_OK(V) (V == 0)
@@ -78,12 +79,12 @@ bool GenerateResetToken(
7879
// * be specific to the client address
7980
// * be specific to the original cid
8081
// * contain random data.
81-
bool GenerateRetryToken(
82-
uint8_t* token,
83-
size_t* tokenlen,
84-
const sockaddr* addr,
85-
const QuicCID& ocid,
86-
const uint8_t* token_secret);
82+
std::unique_ptr<QuicPacket> GenerateRetryPacket(
83+
const uint8_t* token_secret,
84+
const QuicCID& dcid,
85+
const QuicCID& scid,
86+
const SocketAddress& local_addr,
87+
const SocketAddress& remote_addr);
8788

8889
uint32_t GenerateFlowLabel(
8990
const SocketAddress& local,
@@ -95,9 +96,8 @@ uint32_t GenerateFlowLabel(
9596
// Verifies the validity of a retry token. Returns true if the
9697
// token is not valid, false otherwise.
9798
bool InvalidRetryToken(
98-
const uint8_t* token,
99-
size_t tokenlen,
100-
const sockaddr* addr,
99+
const ngtcp2_pkt_hd& hd,
100+
const SocketAddress& addr,
101101
QuicCID* ocid,
102102
const uint8_t* token_secret,
103103
uint64_t verification_expiration);

src/quic/node_quic_http3_application.cc

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,11 @@ MaybeLocal<String> Http3Header::GetValue(QuicApplication* app) const {
8989

9090
std::string Http3Header::name() const {
9191
const char* header_name = ToHttpHeaderName(token_);
92-
return header_name != nullptr ?
93-
std::string(header_name) :
94-
std::string(reinterpret_cast<const char*>(name_.data()), name_.len());
92+
return header_name != nullptr ? std::string(header_name) : name_.str();
9593
}
9694

9795
std::string Http3Header::value() const {
98-
return std::string(
99-
reinterpret_cast<const char*>(value_.data()),
100-
value_.len());
96+
return value_.str();
10197
}
10298

10399
size_t Http3Header::length() const {

0 commit comments

Comments
 (0)