@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.
Files changed (59) hide show
  1. package/INSTALL.md +43 -25
  2. package/bin/nish +0 -0
  3. package/package.json +2 -2
  4. package/runtime/nish.d.ts +55 -5
  5. package/runtime/nish.h +215 -18
  6. package/runtime/nish.mjs +16 -0
  7. package/runtime/runtime-aes.c +490 -0
  8. package/runtime/runtime-cross.c +366 -0
  9. package/runtime/runtime-host.c +7 -6
  10. package/runtime/runtime-net.c +93 -34
  11. package/runtime/runtime-os.c +213 -15
  12. package/runtime/runtime-parallel.c +590 -107
  13. package/runtime/runtime-simd.c +4 -4
  14. package/runtime/runtime-wasm.c +5 -3
  15. package/runtime/runtime.c +69 -13
  16. package/runtime/shim.mjs +71 -1
  17. package/scripts/build.sh +223 -22
  18. package/std/README.md +81 -51
  19. package/std/bytes.ts +163 -0
  20. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  21. package/std/crypto/aes.ts +238 -76
  22. package/std/crypto/chacha20poly1305.ts +43 -9
  23. package/std/crypto/ed25519.ts +840 -0
  24. package/std/crypto/hkdf.ts +304 -12
  25. package/std/crypto/hmac.ts +245 -53
  26. package/std/crypto/p256.ts +932 -61
  27. package/std/crypto/x25519.ts +55 -37
  28. package/std/crypto/x509-random.ts +71 -0
  29. package/std/crypto/x509.ts +9 -9
  30. package/std/json-tokenizer.ts +1296 -0
  31. package/std/json-typed.ts +1014 -0
  32. package/std/json-value.ts +1163 -0
  33. package/std/json.ts +1120 -34
  34. package/std/net/buffer-pool.ts +151 -0
  35. package/std/net/hpack.ts +416 -112
  36. package/std/net/http-fields.ts +93 -0
  37. package/std/net/http1-server.ts +417 -55
  38. package/std/net/http1.ts +31 -6
  39. package/std/net/http2-tls.ts +112 -24
  40. package/std/net/http2.ts +333 -75
  41. package/std/net/http3-server.ts +211 -31
  42. package/std/net/http3.ts +187 -28
  43. package/std/net/prefork.ts +412 -0
  44. package/std/net/quic-listener.ts +88 -7
  45. package/std/net/quic-packet.ts +243 -82
  46. package/std/net/quic-recovery.ts +361 -30
  47. package/std/net/quic-stream.ts +424 -24
  48. package/std/net/quic.ts +519 -167
  49. package/std/net/router.ts +676 -0
  50. package/std/net/tls/codec.ts +426 -27
  51. package/std/net/tls/record-server.ts +478 -132
  52. package/std/net/tls/record.ts +133 -42
  53. package/std/net/tls/schedule.ts +464 -55
  54. package/std/net/tls/ticket.ts +610 -0
  55. package/std/net/tls-tcp.ts +196 -53
  56. package/std/net/tls.ts +1018 -260
  57. package/std/net/url.ts +571 -0
  58. package/std/random.ts +94 -0
  59. package/std/threads.ts +438 -14
@@ -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 six writers. `tlsParseClientHello` reads a ClientHello body
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, HelloRetryRequest, EncryptedExtensions, Certificate,
21
- * CertificateVerify and Finished. Their inputs come from the server and the
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). Every other extension is
31
- * skipped, which is what RFC 8446 §4.2 asks of a server that does not know it.
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): ignored here, but it must be the last extension. */
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
- /** One bit per extension type read, for the duplicate check (§4.2); cleared by each read. */
246
- extensionSeen: u32[]
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. Nothing is copied: the windows
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, and a server
537
- // checks that even though it does not take the PSK.
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
- hello.shareGroups.push((toI32(data[at]) << 8) | toI32(data[at + 1]))
677
- at = at + 4 + ((toI32(data[at + 2]) << 8) | toI32(data[at + 3]))
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
- hello.extensionTypes.push((toI32(data[at]) << 8) | toI32(data[at + 1]))
693
- at = at + 4 + ((toI32(data[at + 2]) << 8) | toI32(data[at + 3]))
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
- * Writes a ServerHello (§4.1.3) at `out[at]` and answers where it ends:
819
- * choosing `suite` and TLS 1.3, echoing the session id
820
- * `sessionId[sessionIdOff .. sessionIdOff + sessionIdLength)`, and carrying
821
- * the server's x25519 share. The extensions are `key_share` then
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
- export const tlsWriteServerHello = (
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 = tlsPutU16(out, extensions, TLS_EXT_KEY_SHARE)
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[] = []