@amritk/nish-x86_64-linux 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL.md +43 -25
- package/bin/nish +0 -0
- package/package.json +2 -2
- package/runtime/nish.d.ts +55 -5
- package/runtime/nish.h +215 -18
- package/runtime/nish.mjs +16 -0
- package/runtime/runtime-aes.c +490 -0
- package/runtime/runtime-cross.c +366 -0
- package/runtime/runtime-host.c +7 -6
- package/runtime/runtime-net.c +93 -34
- package/runtime/runtime-os.c +213 -15
- package/runtime/runtime-parallel.c +590 -107
- package/runtime/runtime-simd.c +4 -4
- package/runtime/runtime-wasm.c +5 -3
- package/runtime/runtime.c +69 -13
- package/runtime/shim.mjs +71 -1
- package/scripts/build.sh +223 -22
- package/std/README.md +81 -51
- package/std/bytes.ts +163 -0
- package/std/crypto/LICENSE-libsecp256k1 +19 -0
- package/std/crypto/aes.ts +238 -76
- package/std/crypto/chacha20poly1305.ts +43 -9
- package/std/crypto/ed25519.ts +840 -0
- package/std/crypto/hkdf.ts +304 -12
- package/std/crypto/hmac.ts +245 -53
- package/std/crypto/p256.ts +932 -61
- package/std/crypto/x25519.ts +55 -37
- package/std/crypto/x509-random.ts +71 -0
- package/std/crypto/x509.ts +9 -9
- package/std/json-tokenizer.ts +1296 -0
- package/std/json-typed.ts +1014 -0
- package/std/json-value.ts +1163 -0
- package/std/json.ts +1120 -34
- package/std/net/buffer-pool.ts +151 -0
- package/std/net/hpack.ts +416 -112
- package/std/net/http-fields.ts +93 -0
- package/std/net/http1-server.ts +417 -55
- package/std/net/http1.ts +31 -6
- package/std/net/http2-tls.ts +112 -24
- package/std/net/http2.ts +333 -75
- package/std/net/http3-server.ts +211 -31
- package/std/net/http3.ts +187 -28
- package/std/net/prefork.ts +412 -0
- package/std/net/quic-listener.ts +88 -7
- package/std/net/quic-packet.ts +243 -82
- package/std/net/quic-recovery.ts +361 -30
- package/std/net/quic-stream.ts +424 -24
- package/std/net/quic.ts +519 -167
- package/std/net/router.ts +676 -0
- package/std/net/tls/codec.ts +426 -27
- package/std/net/tls/record-server.ts +478 -132
- package/std/net/tls/record.ts +133 -42
- package/std/net/tls/schedule.ts +464 -55
- package/std/net/tls/ticket.ts +610 -0
- package/std/net/tls-tcp.ts +196 -53
- package/std/net/tls.ts +1018 -260
- package/std/net/url.ts +571 -0
- package/std/random.ts +94 -0
- package/std/threads.ts +438 -14
package/std/net/tls/codec.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* `nish/net/tls/codec` — the TLS 1.3 handshake messages a server reads and
|
|
3
3
|
* writes (RFC 8446 §4), as bytes.
|
|
4
4
|
*
|
|
5
|
-
* One reader and
|
|
5
|
+
* One reader and eight writers. `tlsParseClientHello` reads a ClientHello body
|
|
6
6
|
* into a `TlsClientHello`, checking its structure: every length against the
|
|
7
7
|
* bytes it claims, every vector against its RFC 8446 bounds, and no extension
|
|
8
8
|
* twice (§4.2). A message that breaks one answers the alert a server sends for
|
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
* allocates nothing, and `tlsParseClientHello` copies a view's windows out.
|
|
18
18
|
*
|
|
19
19
|
* The writers each answer one whole handshake message, header included:
|
|
20
|
-
* ServerHello,
|
|
21
|
-
*
|
|
20
|
+
* ServerHello, a resumed ServerHello that names the PSK it accepts,
|
|
21
|
+
* HelloRetryRequest, EncryptedExtensions, Certificate, CertificateVerify,
|
|
22
|
+
* Finished and NewSessionTicket. Their inputs come from the server and the
|
|
22
23
|
* caller's configuration rather than from the peer, so they check nothing.
|
|
23
24
|
* Each is a `tlsWrite*` function that writes into the caller's array at an
|
|
24
25
|
* offset — run over an empty array, it measures — and a `tlsEncode*` one
|
|
@@ -27,8 +28,11 @@
|
|
|
27
28
|
* The extensions read are `server_name` (RFC 6066 §3), `supported_groups`,
|
|
28
29
|
* `signature_algorithms`, `supported_versions` and `key_share` (RFC 8446
|
|
29
30
|
* §4.2), `application_layer_protocol_negotiation` (RFC 7301 §3.1) and
|
|
30
|
-
* `quic_transport_parameters` (RFC 9001 §8.2)
|
|
31
|
-
*
|
|
31
|
+
* `quic_transport_parameters` (RFC 9001 §8.2), and `pre_shared_key` and
|
|
32
|
+
* `psk_key_exchange_modes` (RFC 8446 §4.2.11, §4.2.9), whose identities and
|
|
33
|
+
* binders the reader checks against their syntax but does not open: a ticket
|
|
34
|
+
* and its binder are the server's to judge. Every other extension is skipped,
|
|
35
|
+
* which is what RFC 8446 §4.2 asks of a server that does not know it.
|
|
32
36
|
*
|
|
33
37
|
* Written from the RFCs, not ported from another implementation. Private
|
|
34
38
|
* names carry the `tls` prefix because a `std/` module's private functions
|
|
@@ -40,6 +44,7 @@
|
|
|
40
44
|
|
|
41
45
|
export const TLS_HANDSHAKE_CLIENT_HELLO: i32 = 1
|
|
42
46
|
export const TLS_HANDSHAKE_SERVER_HELLO: i32 = 2
|
|
47
|
+
export const TLS_HANDSHAKE_NEW_SESSION_TICKET: i32 = 4
|
|
43
48
|
export const TLS_HANDSHAKE_ENCRYPTED_EXTENSIONS: i32 = 8
|
|
44
49
|
export const TLS_HANDSHAKE_CERTIFICATE: i32 = 11
|
|
45
50
|
export const TLS_HANDSHAKE_CERTIFICATE_VERIFY: i32 = 15
|
|
@@ -50,8 +55,12 @@ export const TLS_HANDSHAKE_MESSAGE_HASH: i32 = 254
|
|
|
50
55
|
// ---- Extension types (RFC 8446 §4.2, RFC 7301, RFC 9001) ------------------
|
|
51
56
|
|
|
52
57
|
export const TLS_EXT_SERVER_NAME: i32 = 0
|
|
53
|
-
/** `pre_shared_key` (§4.2.11):
|
|
58
|
+
/** `pre_shared_key` (§4.2.11): the client's identities and binders, which must be the last extension. */
|
|
54
59
|
export const TLS_EXT_PRE_SHARED_KEY: i32 = 41
|
|
60
|
+
/** `early_data` (§4.2.10): read past here, since the server takes no 0-RTT data. */
|
|
61
|
+
export const TLS_EXT_EARLY_DATA: i32 = 42
|
|
62
|
+
/** `psk_key_exchange_modes` (§4.2.9): which modes the client will resume in. */
|
|
63
|
+
export const TLS_EXT_PSK_KEY_EXCHANGE_MODES: i32 = 45
|
|
55
64
|
export const TLS_EXT_SUPPORTED_GROUPS: i32 = 10
|
|
56
65
|
export const TLS_EXT_SIGNATURE_ALGORITHMS: i32 = 13
|
|
57
66
|
export const TLS_EXT_ALPN: i32 = 16
|
|
@@ -95,6 +104,11 @@ export const TLS_ALERT_MISSING_EXTENSION: i32 = 109
|
|
|
95
104
|
/** ALPN offered no protocol the server accepts (RFC 7301 §3.2, RFC 9001 §8.1). */
|
|
96
105
|
export const TLS_ALERT_NO_APPLICATION_PROTOCOL: i32 = 120
|
|
97
106
|
|
|
107
|
+
/** `psk_ke` (§4.2.9): resumption from the PSK alone, without forward secrecy, which the server never selects. */
|
|
108
|
+
export const TLS_PSK_KE: i32 = 0
|
|
109
|
+
/** `psk_dhe_ke` (§4.2.9): resumption with an (EC)DHE exchange, the one mode the server resumes in. */
|
|
110
|
+
export const TLS_PSK_DHE_KE: i32 = 1
|
|
111
|
+
|
|
98
112
|
/** The size of a hello's `random` (§4.1.2). */
|
|
99
113
|
export const TLS_RANDOM_SIZE: i32 = 32
|
|
100
114
|
|
|
@@ -195,6 +209,12 @@ const tlsReadSkipTo = (r: TlsReader, to: i32): void => {
|
|
|
195
209
|
export class TlsClientHelloView {
|
|
196
210
|
/** The buffer the windows point into: the one the last hello was read from. */
|
|
197
211
|
data: u8[]
|
|
212
|
+
/** One bit per extension type read, for the duplicate check (§4.2); cleared by each read. */
|
|
213
|
+
extensionSeen: u32[]
|
|
214
|
+
/** One bit per group a share was offered for, for the duplicate check (§4.2.8). */
|
|
215
|
+
shareSeen: u32[]
|
|
216
|
+
/** The reader each read runs, kept so that a read allocates none. */
|
|
217
|
+
reader: TlsReader
|
|
198
218
|
/** 0 when the message parsed, or the alert it earns: `decode_error` or `illegal_parameter`. */
|
|
199
219
|
alert: i32 = 0
|
|
200
220
|
legacyVersion: i32 = 0
|
|
@@ -232,6 +252,32 @@ export class TlsClientHelloView {
|
|
|
232
252
|
/** The extensions block, every extension in it. */
|
|
233
253
|
extensionsAt: i32 = 0
|
|
234
254
|
extensionsLength: i32 = 0
|
|
255
|
+
/** `pre_shared_key`'s `identities` list (§4.2.11): each an identity<1..2^16-1> and a 32-bit obfuscated age. */
|
|
256
|
+
pskIdentitiesAt: i32 = 0
|
|
257
|
+
pskIdentitiesLength: i32 = 0
|
|
258
|
+
pskIdentityCount: i32 = 0
|
|
259
|
+
/**
|
|
260
|
+
* Where `binders`' two-byte length starts: the ClientHello up to here, its
|
|
261
|
+
* handshake header included, is what each binder is computed over
|
|
262
|
+
* (§4.2.11.2).
|
|
263
|
+
*/
|
|
264
|
+
pskBindersPrefixAt: i32 = 0
|
|
265
|
+
/** `pre_shared_key`'s `binders` list: each a PskBinderEntry<32..255>. */
|
|
266
|
+
pskBindersAt: i32 = 0
|
|
267
|
+
pskBindersLength: i32 = 0
|
|
268
|
+
pskBinderCount: i32 = 0
|
|
269
|
+
/** `psk_key_exchange_modes`' `ke_modes`, one byte a mode (§4.2.9). */
|
|
270
|
+
pskModesAt: i32 = 0
|
|
271
|
+
pskModesLength: i32 = 0
|
|
272
|
+
/** The identity `tlsSelectPskIdentity` last chose: its bytes and its obfuscated ticket age. */
|
|
273
|
+
pskIdentityAt: i32 = 0
|
|
274
|
+
pskIdentityLength: i32 = 0
|
|
275
|
+
pskTicketAge: u32 = 0
|
|
276
|
+
/** That identity's binder, the entry of the same index in `binders`. */
|
|
277
|
+
pskBinderAt: i32 = 0
|
|
278
|
+
pskBinderLength: i32 = 0
|
|
279
|
+
/** Whether `ke_modes` lists `psk_dhe_ke`, the one mode the server resumes in. */
|
|
280
|
+
pskDheKe: boolean = false
|
|
235
281
|
/** Whether `legacy_compression_methods` is the single null method TLS 1.3 requires (§4.1.2). */
|
|
236
282
|
nullCompressionOnly: boolean = false
|
|
237
283
|
hasSupportedVersions: boolean = false
|
|
@@ -242,12 +288,8 @@ export class TlsClientHelloView {
|
|
|
242
288
|
hasServerName: boolean = false
|
|
243
289
|
hasAlpn: boolean = false
|
|
244
290
|
hasQuicTransportParameters: boolean = false
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
/** One bit per group a share was offered for, for the duplicate check (§4.2.8). */
|
|
248
|
-
shareSeen: u32[]
|
|
249
|
-
/** The reader each read runs, kept so that a read allocates none. */
|
|
250
|
-
reader: TlsReader
|
|
291
|
+
hasPreSharedKey: boolean = false
|
|
292
|
+
hasPskModes: boolean = false
|
|
251
293
|
|
|
252
294
|
constructor() {
|
|
253
295
|
this.data = []
|
|
@@ -283,6 +325,21 @@ export class TlsClientHelloView {
|
|
|
283
325
|
this.quicLength = 0
|
|
284
326
|
this.extensionsAt = 0
|
|
285
327
|
this.extensionsLength = 0
|
|
328
|
+
this.pskIdentitiesAt = 0
|
|
329
|
+
this.pskIdentitiesLength = 0
|
|
330
|
+
this.pskIdentityCount = 0
|
|
331
|
+
this.pskBindersPrefixAt = 0
|
|
332
|
+
this.pskBindersAt = 0
|
|
333
|
+
this.pskBindersLength = 0
|
|
334
|
+
this.pskBinderCount = 0
|
|
335
|
+
this.pskModesAt = 0
|
|
336
|
+
this.pskModesLength = 0
|
|
337
|
+
this.pskDheKe = false
|
|
338
|
+
this.pskIdentityAt = 0
|
|
339
|
+
this.pskIdentityLength = 0
|
|
340
|
+
this.pskTicketAge = toU32(0)
|
|
341
|
+
this.pskBinderAt = 0
|
|
342
|
+
this.pskBinderLength = 0
|
|
286
343
|
this.nullCompressionOnly = false
|
|
287
344
|
this.hasSupportedVersions = false
|
|
288
345
|
this.hasSupportedGroups = false
|
|
@@ -292,6 +349,8 @@ export class TlsClientHelloView {
|
|
|
292
349
|
this.hasServerName = false
|
|
293
350
|
this.hasAlpn = false
|
|
294
351
|
this.hasQuicTransportParameters = false
|
|
352
|
+
this.hasPreSharedKey = false
|
|
353
|
+
this.hasPskModes = false
|
|
295
354
|
for (let k: i32 = 0; k < toI32(this.extensionSeen.length); k++) {
|
|
296
355
|
this.extensionSeen[k] = toU32(0)
|
|
297
356
|
}
|
|
@@ -432,6 +491,63 @@ const tlsReadAlpn = (r: TlsReader, hello: TlsClientHelloView): void => {
|
|
|
432
491
|
r.end = outerEnd
|
|
433
492
|
}
|
|
434
493
|
|
|
494
|
+
/**
|
|
495
|
+
* `pre_shared_key` (§4.2.11): `identities<7..2^16-1>`, each an
|
|
496
|
+
* `identity<1..2^16-1>` and a 32-bit obfuscated ticket age, then
|
|
497
|
+
* `binders<33..2^16-1>`, each a `PskBinderEntry<32..255>`. A length the
|
|
498
|
+
* syntax forbids fails the reader, and so is `decode_error`; that the two
|
|
499
|
+
* lists hold as many entries as each other is `tlsReadClientHello`'s to check
|
|
500
|
+
* once the whole hello has parsed. Nothing is opened or verified here.
|
|
501
|
+
*/
|
|
502
|
+
const tlsReadPreSharedKey = (r: TlsReader, hello: TlsClientHelloView): void => {
|
|
503
|
+
hello.hasPreSharedKey = true
|
|
504
|
+
const identitiesEnd: i32 = tlsReadVector(r, 2, 7, 65535)
|
|
505
|
+
hello.pskIdentitiesAt = r.at
|
|
506
|
+
hello.pskIdentitiesLength = identitiesEnd - r.at
|
|
507
|
+
// The list's own end bounds every entry in it (see `tlsReadList`).
|
|
508
|
+
let outerEnd: i32 = tlsReadList(r, identitiesEnd)
|
|
509
|
+
while (!r.failed && r.at < identitiesEnd) {
|
|
510
|
+
const identityEnd: i32 = tlsReadVector(r, 2, 1, 65535)
|
|
511
|
+
tlsReadSkipTo(r, identityEnd)
|
|
512
|
+
// obfuscated_ticket_age, which the server reads only for the identity it selects.
|
|
513
|
+
tlsReadSkipTo(r, r.at + 4)
|
|
514
|
+
if (!r.failed) {
|
|
515
|
+
hello.pskIdentityCount = hello.pskIdentityCount + 1
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
r.end = outerEnd
|
|
519
|
+
hello.pskBindersPrefixAt = r.at
|
|
520
|
+
const bindersEnd: i32 = tlsReadVector(r, 2, 33, 65535)
|
|
521
|
+
hello.pskBindersAt = r.at
|
|
522
|
+
hello.pskBindersLength = bindersEnd - r.at
|
|
523
|
+
outerEnd = tlsReadList(r, bindersEnd)
|
|
524
|
+
while (!r.failed && r.at < bindersEnd) {
|
|
525
|
+
const binderEnd: i32 = tlsReadVector(r, 1, 32, 255)
|
|
526
|
+
tlsReadSkipTo(r, binderEnd)
|
|
527
|
+
if (!r.failed) {
|
|
528
|
+
hello.pskBinderCount = hello.pskBinderCount + 1
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
r.end = outerEnd
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* `psk_key_exchange_modes` (§4.2.9): `ke_modes<1..255>`, one byte a mode. An
|
|
536
|
+
* empty list fails the reader; a mode the server does not know is passed
|
|
537
|
+
* over, and `psk_dhe_ke` is the one it records.
|
|
538
|
+
*/
|
|
539
|
+
const tlsReadPskModes = (r: TlsReader, hello: TlsClientHelloView): void => {
|
|
540
|
+
hello.hasPskModes = true
|
|
541
|
+
const modesEnd: i32 = tlsReadVector(r, 1, 1, 255)
|
|
542
|
+
hello.pskModesAt = r.at
|
|
543
|
+
hello.pskModesLength = modesEnd - r.at
|
|
544
|
+
while (!r.failed && r.at < modesEnd) {
|
|
545
|
+
if (tlsReadU8(r) === TLS_PSK_DHE_KE) {
|
|
546
|
+
hello.pskDheKe = true
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
435
551
|
/** Reads one extension body, `r.at .. end`, of type `type`; an unknown type is skipped. */
|
|
436
552
|
const tlsReadExtension = (r: TlsReader, type: i32, end: i32, hello: TlsClientHelloView): void => {
|
|
437
553
|
switch (type) {
|
|
@@ -459,6 +575,12 @@ const tlsReadExtension = (r: TlsReader, type: i32, end: i32, hello: TlsClientHel
|
|
|
459
575
|
case TLS_EXT_KEY_SHARE:
|
|
460
576
|
tlsReadKeyShare(r, hello)
|
|
461
577
|
break
|
|
578
|
+
case TLS_EXT_PRE_SHARED_KEY:
|
|
579
|
+
tlsReadPreSharedKey(r, hello)
|
|
580
|
+
break
|
|
581
|
+
case TLS_EXT_PSK_KEY_EXCHANGE_MODES:
|
|
582
|
+
tlsReadPskModes(r, hello)
|
|
583
|
+
break
|
|
462
584
|
case TLS_EXT_QUIC_TRANSPORT_PARAMETERS:
|
|
463
585
|
hello.hasQuicTransportParameters = true
|
|
464
586
|
hello.quicAt = r.at
|
|
@@ -486,7 +608,11 @@ const tlsReadExtension = (r: TlsReader, type: i32, end: i32, hello: TlsClientHel
|
|
|
486
608
|
* bounds or bytes left over, and `illegal_parameter` for an extension sent
|
|
487
609
|
* twice (§4.2), a `pre_shared_key` that is not the last extension (§4.2.11),
|
|
488
610
|
* two shares for one group, or a bad `server_name`. A window outside `data`
|
|
489
|
-
* is `decode_error` too, rather than a panic.
|
|
611
|
+
* is `decode_error` too, rather than a panic. A hello that parses but offers
|
|
612
|
+
* `pre_shared_key` with a different number of identities and binders is
|
|
613
|
+
* `illegal_parameter`, and one that offers it without
|
|
614
|
+
* `psk_key_exchange_modes` is `missing_extension` (§4.2.9): rows 10 to 13 of
|
|
615
|
+
* the fallback table in `docs/security/tls.md`, "Resumption". Nothing is copied: the windows
|
|
490
616
|
* point into `data`, which must outlive the use made of them.
|
|
491
617
|
*/
|
|
492
618
|
export const tlsReadClientHello = (hello: TlsClientHelloView, data: u8[], off: i32, len: i32): void => {
|
|
@@ -533,8 +659,8 @@ export const tlsReadClientHello = (hello: TlsClientHelloView, data: u8[], off: i
|
|
|
533
659
|
if (tlsMarkSeen(hello.extensionSeen, type)) {
|
|
534
660
|
tlsHelloRefuse(hello, TLS_ALERT_ILLEGAL_PARAMETER)
|
|
535
661
|
}
|
|
536
|
-
// §4.2.11: `pre_shared_key` must be the last extension,
|
|
537
|
-
//
|
|
662
|
+
// §4.2.11: `pre_shared_key` must be the last extension, checked
|
|
663
|
+
// before its body is read, so that this fault is reported first.
|
|
538
664
|
if (type === TLS_EXT_PRE_SHARED_KEY && bodyEnd !== extensionsEnd) {
|
|
539
665
|
tlsHelloRefuse(hello, TLS_ALERT_ILLEGAL_PARAMETER)
|
|
540
666
|
}
|
|
@@ -548,7 +674,70 @@ export const tlsReadClientHello = (hello: TlsClientHelloView, data: u8[], off: i
|
|
|
548
674
|
}
|
|
549
675
|
if (r.failed || r.at !== end) {
|
|
550
676
|
tlsHelloRefuse(hello, TLS_ALERT_DECODE_ERROR)
|
|
677
|
+
return
|
|
678
|
+
}
|
|
679
|
+
// Checked once the whole hello has parsed, so that a malformed one is
|
|
680
|
+
// `decode_error` whatever these say (row 13 before rows 12 and 10).
|
|
681
|
+
if (hello.hasPreSharedKey && hello.pskIdentityCount !== hello.pskBinderCount) {
|
|
682
|
+
tlsHelloRefuse(hello, TLS_ALERT_ILLEGAL_PARAMETER)
|
|
683
|
+
}
|
|
684
|
+
if (hello.hasPreSharedKey && !hello.hasPskModes) {
|
|
685
|
+
tlsHelloRefuse(hello, TLS_ALERT_MISSING_EXTENSION)
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Points `hello`'s `pskIdentity*`, `pskTicketAge` and `pskBinder*` at the
|
|
691
|
+
* offer's identity number `index` and the binder of the same number, and
|
|
692
|
+
* answers whether there is one: false for an index out of range, or a hello
|
|
693
|
+
* that offered no `pre_shared_key` or did not parse. It walks the lists
|
|
694
|
+
* `tlsReadClientHello` checked, and allocates nothing.
|
|
695
|
+
*/
|
|
696
|
+
export const tlsSelectPskIdentity = (hello: TlsClientHelloView, index: i32): boolean => {
|
|
697
|
+
hello.pskIdentityAt = 0
|
|
698
|
+
hello.pskIdentityLength = 0
|
|
699
|
+
hello.pskTicketAge = toU32(0)
|
|
700
|
+
hello.pskBinderAt = 0
|
|
701
|
+
hello.pskBinderLength = 0
|
|
702
|
+
if (hello.alert !== 0 || !hello.hasPreSharedKey || index < 0 || index >= hello.pskIdentityCount) {
|
|
703
|
+
return false
|
|
704
|
+
}
|
|
705
|
+
const data: u8[] = hello.data
|
|
706
|
+
let at: i32 = hello.pskIdentitiesAt
|
|
707
|
+
for (let k: i32 = 0; at >= 0 && at + 1 < toI32(data.length); k++) {
|
|
708
|
+
const length: i32 = (toI32(data[at]) << 8) | toI32(data[at + 1])
|
|
709
|
+
const age: i32 = at + 2 + length
|
|
710
|
+
if (age < 0 || age + 3 >= toI32(data.length)) {
|
|
711
|
+
return false
|
|
712
|
+
}
|
|
713
|
+
if (k === index) {
|
|
714
|
+
hello.pskIdentityAt = at + 2
|
|
715
|
+
hello.pskIdentityLength = length
|
|
716
|
+
hello.pskTicketAge =
|
|
717
|
+
(toU32(data[age]) << 24) |
|
|
718
|
+
(toU32(data[age + 1]) << 16) |
|
|
719
|
+
(toU32(data[age + 2]) << 8) |
|
|
720
|
+
toU32(data[age + 3])
|
|
721
|
+
break
|
|
722
|
+
}
|
|
723
|
+
at = age + 4
|
|
551
724
|
}
|
|
725
|
+
// An identity is never empty (§4.2.11), so an empty window means the walk
|
|
726
|
+
// ran off the data before reaching it.
|
|
727
|
+
if (hello.pskIdentityLength === 0) {
|
|
728
|
+
return false
|
|
729
|
+
}
|
|
730
|
+
at = hello.pskBindersAt
|
|
731
|
+
for (let k: i32 = 0; at >= 0 && at < toI32(data.length); k++) {
|
|
732
|
+
const length: i32 = toI32(data[at])
|
|
733
|
+
if (k === index) {
|
|
734
|
+
hello.pskBinderAt = at + 1
|
|
735
|
+
hello.pskBinderLength = length
|
|
736
|
+
return true
|
|
737
|
+
}
|
|
738
|
+
at = at + 1 + length
|
|
739
|
+
}
|
|
740
|
+
return false
|
|
552
741
|
}
|
|
553
742
|
|
|
554
743
|
/**
|
|
@@ -581,6 +770,19 @@ export class TlsClientHello {
|
|
|
581
770
|
quicTransportParameters: u8[]
|
|
582
771
|
/** Every extension type read, in order. */
|
|
583
772
|
extensionTypes: i32[]
|
|
773
|
+
/** `pre_shared_key`'s identities, in the client's order, and each one's obfuscated ticket age (§4.2.11). */
|
|
774
|
+
pskIdentities: u8[][]
|
|
775
|
+
pskTicketAges: u32[]
|
|
776
|
+
/** `pre_shared_key`'s binders, one an identity. */
|
|
777
|
+
pskBinders: u8[][]
|
|
778
|
+
/** `psk_key_exchange_modes`, one byte a mode. */
|
|
779
|
+
pskModes: u8[]
|
|
780
|
+
/**
|
|
781
|
+
* How many bytes of the body come before `binders`: the body truncated
|
|
782
|
+
* there, after its handshake header, is what each binder covers
|
|
783
|
+
* (§4.2.11.2).
|
|
784
|
+
*/
|
|
785
|
+
pskBindersPrefix: i32 = 0
|
|
584
786
|
/** 0 when the message parsed, or the alert it earns: `decode_error` or `illegal_parameter`. */
|
|
585
787
|
alert: i32 = 0
|
|
586
788
|
legacyVersion: i32 = 0
|
|
@@ -594,6 +796,10 @@ export class TlsClientHello {
|
|
|
594
796
|
hasServerName: boolean = false
|
|
595
797
|
hasAlpn: boolean = false
|
|
596
798
|
hasQuicTransportParameters: boolean = false
|
|
799
|
+
hasPreSharedKey: boolean = false
|
|
800
|
+
hasPskModes: boolean = false
|
|
801
|
+
/** Whether `psk_key_exchange_modes` lists `psk_dhe_ke`. */
|
|
802
|
+
pskDheKe: boolean = false
|
|
597
803
|
|
|
598
804
|
constructor() {
|
|
599
805
|
this.random = []
|
|
@@ -607,6 +813,10 @@ export class TlsClientHello {
|
|
|
607
813
|
this.alpn = []
|
|
608
814
|
this.quicTransportParameters = []
|
|
609
815
|
this.extensionTypes = []
|
|
816
|
+
this.pskIdentities = []
|
|
817
|
+
this.pskTicketAges = []
|
|
818
|
+
this.pskBinders = []
|
|
819
|
+
this.pskModes = []
|
|
610
820
|
}
|
|
611
821
|
}
|
|
612
822
|
|
|
@@ -673,8 +883,12 @@ export const tlsParseClientHello = (data: u8[], off: i32, len: i32): TlsClientHe
|
|
|
673
883
|
at < toI32(data.length) &&
|
|
674
884
|
at + 3 < toI32(data.length)
|
|
675
885
|
) {
|
|
676
|
-
|
|
677
|
-
|
|
886
|
+
// All four bytes are read before the `push`, which is a call and so
|
|
887
|
+
// forgets what the guard said about `data.length`.
|
|
888
|
+
const group: i32 = (toI32(data[at]) << 8) | toI32(data[at + 1])
|
|
889
|
+
const skip: i32 = (toI32(data[at + 2]) << 8) | toI32(data[at + 3])
|
|
890
|
+
hello.shareGroups.push(group)
|
|
891
|
+
at = at + 4 + skip
|
|
678
892
|
}
|
|
679
893
|
at = view.alpnAt
|
|
680
894
|
while (at < view.alpnAt + view.alpnLength && at >= 0 && at < toI32(data.length)) {
|
|
@@ -689,8 +903,11 @@ export const tlsParseClientHello = (data: u8[], off: i32, len: i32): TlsClientHe
|
|
|
689
903
|
at < toI32(data.length) &&
|
|
690
904
|
at + 3 < toI32(data.length)
|
|
691
905
|
) {
|
|
692
|
-
|
|
693
|
-
|
|
906
|
+
// Read before the `push`, as in the key-share walk above.
|
|
907
|
+
const kind: i32 = (toI32(data[at]) << 8) | toI32(data[at + 1])
|
|
908
|
+
const skip: i32 = (toI32(data[at + 2]) << 8) | toI32(data[at + 3])
|
|
909
|
+
hello.extensionTypes.push(kind)
|
|
910
|
+
at = at + 4 + skip
|
|
694
911
|
}
|
|
695
912
|
hello.nullCompressionOnly = view.nullCompressionOnly
|
|
696
913
|
hello.hasSupportedVersions = view.hasSupportedVersions
|
|
@@ -701,6 +918,18 @@ export const tlsParseClientHello = (data: u8[], off: i32, len: i32): TlsClientHe
|
|
|
701
918
|
hello.hasServerName = view.hasServerName
|
|
702
919
|
hello.hasAlpn = view.hasAlpn
|
|
703
920
|
hello.hasQuicTransportParameters = view.hasQuicTransportParameters
|
|
921
|
+
hello.hasPreSharedKey = view.hasPreSharedKey
|
|
922
|
+
hello.hasPskModes = view.hasPskModes
|
|
923
|
+
hello.pskDheKe = view.pskDheKe
|
|
924
|
+
hello.pskModes = tlsCopyWindow(data, view.pskModesAt, view.pskModesLength)
|
|
925
|
+
if (view.hasPreSharedKey) {
|
|
926
|
+
hello.pskBindersPrefix = view.pskBindersPrefixAt - off
|
|
927
|
+
}
|
|
928
|
+
for (let k: i32 = 0; k < view.pskIdentityCount && tlsSelectPskIdentity(view, k); k++) {
|
|
929
|
+
hello.pskIdentities.push(tlsCopyWindow(data, view.pskIdentityAt, view.pskIdentityLength))
|
|
930
|
+
hello.pskTicketAges.push(view.pskTicketAge)
|
|
931
|
+
hello.pskBinders.push(tlsCopyWindow(data, view.pskBinderAt, view.pskBinderLength))
|
|
932
|
+
}
|
|
704
933
|
return hello
|
|
705
934
|
}
|
|
706
935
|
|
|
@@ -723,6 +952,11 @@ const tlsPutU8 = (out: u8[], at: i32, v: i32): i32 => {
|
|
|
723
952
|
|
|
724
953
|
const tlsPutU16 = (out: u8[], at: i32, v: i32): i32 => tlsPutU8(out, tlsPutU8(out, at, v >> 8), v)
|
|
725
954
|
|
|
955
|
+
const tlsPutU32 = (out: u8[], at: i32, v: u32): i32 => {
|
|
956
|
+
const p: i32 = tlsPutU16(out, at, toI32((v >> 16) & 0xffff))
|
|
957
|
+
return tlsPutU16(out, p, toI32(v & 0xffff))
|
|
958
|
+
}
|
|
959
|
+
|
|
726
960
|
/** `bytes[off .. off + len)`, at `out[at]`. */
|
|
727
961
|
const tlsPutWindow = (out: u8[], at: i32, bytes: u8[], off: i32, len: i32): i32 => {
|
|
728
962
|
// Measuring over an empty array: nothing would land, so nothing is copied.
|
|
@@ -815,13 +1049,12 @@ const tlsPutHelloHead = (
|
|
|
815
1049
|
}
|
|
816
1050
|
|
|
817
1051
|
/**
|
|
818
|
-
*
|
|
819
|
-
*
|
|
820
|
-
* `
|
|
821
|
-
*
|
|
822
|
-
* `supported_versions`, the order RFC 8448 §3 shows.
|
|
1052
|
+
* A ServerHello (§4.1.3), as `tlsWriteServerHello` and
|
|
1053
|
+
* `tlsWriteServerHelloPsk` write it: with a `pre_shared_key` naming
|
|
1054
|
+
* `selectedIdentity` first when that is not negative, the order RFC 8448 §4
|
|
1055
|
+
* shows, then `key_share` and `supported_versions`.
|
|
823
1056
|
*/
|
|
824
|
-
|
|
1057
|
+
const tlsPutServerHello = (
|
|
825
1058
|
out: u8[],
|
|
826
1059
|
at: i32,
|
|
827
1060
|
random: u8[],
|
|
@@ -829,11 +1062,16 @@ export const tlsWriteServerHello = (
|
|
|
829
1062
|
sessionIdOff: i32,
|
|
830
1063
|
sessionIdLength: i32,
|
|
831
1064
|
suite: i32,
|
|
832
|
-
x25519Public: u8[]
|
|
1065
|
+
x25519Public: u8[],
|
|
1066
|
+
selectedIdentity: i32
|
|
833
1067
|
): i32 => {
|
|
834
1068
|
let p: i32 = tlsPutHelloHead(out, at + 4, random, false, sessionId, sessionIdOff, sessionIdLength, suite)
|
|
835
1069
|
const extensions: i32 = p + 2
|
|
836
|
-
p =
|
|
1070
|
+
p = extensions
|
|
1071
|
+
if (selectedIdentity >= 0) {
|
|
1072
|
+
p = tlsPutU16(out, tlsPutU16(out, tlsPutU16(out, p, TLS_EXT_PRE_SHARED_KEY), 2), selectedIdentity)
|
|
1073
|
+
}
|
|
1074
|
+
p = tlsPutU16(out, p, TLS_EXT_KEY_SHARE)
|
|
837
1075
|
const share: i32 = p + 2
|
|
838
1076
|
p = tlsPutU16(out, share, TLS_GROUP_X25519)
|
|
839
1077
|
const key: i32 = p + 2
|
|
@@ -846,6 +1084,55 @@ export const tlsWriteServerHello = (
|
|
|
846
1084
|
return p
|
|
847
1085
|
}
|
|
848
1086
|
|
|
1087
|
+
/**
|
|
1088
|
+
* Writes a ServerHello (§4.1.3) at `out[at]` and answers where it ends:
|
|
1089
|
+
* choosing `suite` and TLS 1.3, echoing the session id
|
|
1090
|
+
* `sessionId[sessionIdOff .. sessionIdOff + sessionIdLength)`, and carrying
|
|
1091
|
+
* the server's x25519 share. The extensions are `key_share` then
|
|
1092
|
+
* `supported_versions`, the order RFC 8448 §3 shows.
|
|
1093
|
+
*/
|
|
1094
|
+
export const tlsWriteServerHello = (
|
|
1095
|
+
out: u8[],
|
|
1096
|
+
at: i32,
|
|
1097
|
+
random: u8[],
|
|
1098
|
+
sessionId: u8[],
|
|
1099
|
+
sessionIdOff: i32,
|
|
1100
|
+
sessionIdLength: i32,
|
|
1101
|
+
suite: i32,
|
|
1102
|
+
x25519Public: u8[]
|
|
1103
|
+
): i32 =>
|
|
1104
|
+
tlsPutServerHello(out, at, random, sessionId, sessionIdOff, sessionIdLength, suite, x25519Public, -1)
|
|
1105
|
+
|
|
1106
|
+
/**
|
|
1107
|
+
* Writes the ServerHello of a resumed handshake (§4.1.3, §4.2.11) at
|
|
1108
|
+
* `out[at]` and answers where it ends: `tlsWriteServerHello`'s, with a
|
|
1109
|
+
* `pre_shared_key` that names the client's identity number
|
|
1110
|
+
* `selectedIdentity` (a `uint16`) ahead of `key_share`, since the server
|
|
1111
|
+
* resumes only in `psk_dhe_ke` and so always sends its share.
|
|
1112
|
+
*/
|
|
1113
|
+
export const tlsWriteServerHelloPsk = (
|
|
1114
|
+
out: u8[],
|
|
1115
|
+
at: i32,
|
|
1116
|
+
random: u8[],
|
|
1117
|
+
sessionId: u8[],
|
|
1118
|
+
sessionIdOff: i32,
|
|
1119
|
+
sessionIdLength: i32,
|
|
1120
|
+
suite: i32,
|
|
1121
|
+
x25519Public: u8[],
|
|
1122
|
+
selectedIdentity: i32
|
|
1123
|
+
): i32 =>
|
|
1124
|
+
tlsPutServerHello(
|
|
1125
|
+
out,
|
|
1126
|
+
at,
|
|
1127
|
+
random,
|
|
1128
|
+
sessionId,
|
|
1129
|
+
sessionIdOff,
|
|
1130
|
+
sessionIdLength,
|
|
1131
|
+
suite,
|
|
1132
|
+
x25519Public,
|
|
1133
|
+
selectedIdentity & 0xffff
|
|
1134
|
+
)
|
|
1135
|
+
|
|
849
1136
|
/**
|
|
850
1137
|
* Writes a HelloRetryRequest (§4.1.4) at `out[at]` and answers where it ends:
|
|
851
1138
|
* a ServerHello whose `random` is the fixed HelloRetryRequest value, choosing
|
|
@@ -950,6 +1237,39 @@ export const tlsWriteFinished = (out: u8[], at: i32, verifyData: u8[], off: i32,
|
|
|
950
1237
|
return p
|
|
951
1238
|
}
|
|
952
1239
|
|
|
1240
|
+
/**
|
|
1241
|
+
* Writes a NewSessionTicket (§4.6.1) at `out[at]` and answers where it ends:
|
|
1242
|
+
* `lifetime` in seconds, `ageAdd`, the nonce
|
|
1243
|
+
* `nonce[nonceOff .. nonceOff + nonceLength)`, the ticket
|
|
1244
|
+
* `ticket[ticketOff .. ticketOff + ticketLength)`, and `extra` as the
|
|
1245
|
+
* extensions, encoded by the caller and written as they are. The server sends
|
|
1246
|
+
* none; RFC 8448 §3's ticket carries `early_data`.
|
|
1247
|
+
*/
|
|
1248
|
+
export const tlsWriteNewSessionTicket = (
|
|
1249
|
+
out: u8[],
|
|
1250
|
+
at: i32,
|
|
1251
|
+
lifetime: u32,
|
|
1252
|
+
ageAdd: u32,
|
|
1253
|
+
nonce: u8[],
|
|
1254
|
+
nonceOff: i32,
|
|
1255
|
+
nonceLength: i32,
|
|
1256
|
+
ticket: u8[],
|
|
1257
|
+
ticketOff: i32,
|
|
1258
|
+
ticketLength: i32,
|
|
1259
|
+
extra: u8[]
|
|
1260
|
+
): i32 => {
|
|
1261
|
+
let p: i32 = tlsPutU32(out, tlsPutU32(out, at + 4, lifetime), ageAdd)
|
|
1262
|
+
p = tlsPutU8(out, p, nonceLength)
|
|
1263
|
+
p = tlsPutWindow(out, p, nonce, nonceOff, nonceLength)
|
|
1264
|
+
p = tlsPutU16(out, p, ticketLength)
|
|
1265
|
+
p = tlsPutWindow(out, p, ticket, ticketOff, ticketLength)
|
|
1266
|
+
const extensions: i32 = p + 2
|
|
1267
|
+
p = tlsPutBytes(out, extensions, extra)
|
|
1268
|
+
tlsPutLength(out, extensions, p, 2)
|
|
1269
|
+
tlsPutHeader(out, at, TLS_HANDSHAKE_NEW_SESSION_TICKET, p)
|
|
1270
|
+
return p
|
|
1271
|
+
}
|
|
1272
|
+
|
|
953
1273
|
/**
|
|
954
1274
|
* Writes what a server's CertificateVerify signs (§4.4.3) at `out[at]`: 64
|
|
955
1275
|
* spaces, the context string "TLS 1.3, server CertificateVerify", a zero byte,
|
|
@@ -1008,6 +1328,43 @@ export const tlsEncodeServerHello = (random: u8[], sessionId: u8[], suite: i32,
|
|
|
1008
1328
|
return out
|
|
1009
1329
|
}
|
|
1010
1330
|
|
|
1331
|
+
/** A resumed handshake's ServerHello naming `selectedIdentity`, in a fresh array (`tlsWriteServerHelloPsk`). */
|
|
1332
|
+
export const tlsEncodeServerHelloPsk = (
|
|
1333
|
+
random: u8[],
|
|
1334
|
+
sessionId: u8[],
|
|
1335
|
+
suite: i32,
|
|
1336
|
+
x25519Public: u8[],
|
|
1337
|
+
selectedIdentity: i32
|
|
1338
|
+
): u8[] => {
|
|
1339
|
+
const none: u8[] = []
|
|
1340
|
+
const sessionIdLength: i32 = toI32(sessionId.length)
|
|
1341
|
+
const out: u8[] = new Array<u8>(
|
|
1342
|
+
tlsWriteServerHelloPsk(
|
|
1343
|
+
none,
|
|
1344
|
+
TLS_CODEC_FROM,
|
|
1345
|
+
random,
|
|
1346
|
+
sessionId,
|
|
1347
|
+
TLS_CODEC_FROM,
|
|
1348
|
+
sessionIdLength,
|
|
1349
|
+
suite,
|
|
1350
|
+
x25519Public,
|
|
1351
|
+
selectedIdentity
|
|
1352
|
+
)
|
|
1353
|
+
)
|
|
1354
|
+
tlsWriteServerHelloPsk(
|
|
1355
|
+
out,
|
|
1356
|
+
TLS_CODEC_FROM,
|
|
1357
|
+
random,
|
|
1358
|
+
sessionId,
|
|
1359
|
+
TLS_CODEC_FROM,
|
|
1360
|
+
sessionIdLength,
|
|
1361
|
+
suite,
|
|
1362
|
+
x25519Public,
|
|
1363
|
+
selectedIdentity
|
|
1364
|
+
)
|
|
1365
|
+
return out
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1011
1368
|
/** A HelloRetryRequest (§4.1.4) choosing `suite`, echoing `sessionId` and asking for `group`, in a fresh array (`tlsWriteHelloRetryRequest`). */
|
|
1012
1369
|
export const tlsEncodeHelloRetryRequest = (sessionId: u8[], suite: i32, group: i32): u8[] => {
|
|
1013
1370
|
const none: u8[] = []
|
|
@@ -1077,6 +1434,48 @@ export const tlsEncodeFinished = (verifyData: u8[]): u8[] => {
|
|
|
1077
1434
|
return out
|
|
1078
1435
|
}
|
|
1079
1436
|
|
|
1437
|
+
/** A NewSessionTicket (§4.6.1) in a fresh array (`tlsWriteNewSessionTicket`). */
|
|
1438
|
+
export const tlsEncodeNewSessionTicket = (
|
|
1439
|
+
lifetime: u32,
|
|
1440
|
+
ageAdd: u32,
|
|
1441
|
+
nonce: u8[],
|
|
1442
|
+
ticket: u8[],
|
|
1443
|
+
extra: u8[]
|
|
1444
|
+
): u8[] => {
|
|
1445
|
+
const none: u8[] = []
|
|
1446
|
+
const nonceLength: i32 = toI32(nonce.length)
|
|
1447
|
+
const ticketLength: i32 = toI32(ticket.length)
|
|
1448
|
+
const out: u8[] = new Array<u8>(
|
|
1449
|
+
tlsWriteNewSessionTicket(
|
|
1450
|
+
none,
|
|
1451
|
+
TLS_CODEC_FROM,
|
|
1452
|
+
lifetime,
|
|
1453
|
+
ageAdd,
|
|
1454
|
+
nonce,
|
|
1455
|
+
TLS_CODEC_FROM,
|
|
1456
|
+
nonceLength,
|
|
1457
|
+
ticket,
|
|
1458
|
+
TLS_CODEC_FROM,
|
|
1459
|
+
ticketLength,
|
|
1460
|
+
extra
|
|
1461
|
+
)
|
|
1462
|
+
)
|
|
1463
|
+
tlsWriteNewSessionTicket(
|
|
1464
|
+
out,
|
|
1465
|
+
TLS_CODEC_FROM,
|
|
1466
|
+
lifetime,
|
|
1467
|
+
ageAdd,
|
|
1468
|
+
nonce,
|
|
1469
|
+
TLS_CODEC_FROM,
|
|
1470
|
+
nonceLength,
|
|
1471
|
+
ticket,
|
|
1472
|
+
TLS_CODEC_FROM,
|
|
1473
|
+
ticketLength,
|
|
1474
|
+
extra
|
|
1475
|
+
)
|
|
1476
|
+
return out
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1080
1479
|
/** What a server's CertificateVerify signs (§4.4.3), in a fresh array (`tlsWriteCertificateVerifyContent`). */
|
|
1081
1480
|
export const tlsCertificateVerifyContent = (transcriptHash: u8[]): u8[] => {
|
|
1082
1481
|
const none: u8[] = []
|