@amritk/nish-x86_64-linux 0.16.0 → 0.18.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 (58) hide show
  1. package/INSTALL.md +43 -34
  2. package/bin/nish +0 -0
  3. package/package.json +1 -1
  4. package/runtime/nish.d.ts +117 -8
  5. package/runtime/nish.h +50 -13
  6. package/runtime/nish.mjs +71 -7
  7. package/runtime/runtime-host.c +43 -24
  8. package/runtime/runtime-net.c +57 -10
  9. package/runtime/runtime-os.c +62 -20
  10. package/runtime/runtime-simd.c +203 -0
  11. package/runtime/runtime-wasm.c +12 -0
  12. package/runtime/runtime.c +66 -18
  13. package/runtime/shim.mjs +114 -5
  14. package/scripts/build.sh +14 -12
  15. package/std/README.md +168 -36
  16. package/std/crypto/aes.ts +51 -8
  17. package/std/crypto/base64url.ts +25 -12
  18. package/std/crypto/chacha20poly1305.ts +43 -29
  19. package/std/crypto/ct.ts +6 -1
  20. package/std/crypto/hkdf.ts +364 -3
  21. package/std/crypto/hmac.ts +307 -7
  22. package/std/crypto/p256.ts +618 -34
  23. package/std/crypto/sha1.ts +147 -0
  24. package/std/crypto/sha256.ts +3 -2
  25. package/std/crypto/x25519.ts +88 -36
  26. package/std/crypto/x509.ts +130 -47
  27. package/std/json.ts +297 -41
  28. package/std/net/hpack.ts +1094 -0
  29. package/std/net/http-fields.ts +604 -0
  30. package/std/net/http1-server.ts +1389 -0
  31. package/std/net/http1.ts +1492 -0
  32. package/std/net/http2-frame.ts +745 -0
  33. package/std/net/http2-tls.ts +216 -0
  34. package/std/net/http2.ts +1433 -0
  35. package/std/net/http3-frame.ts +457 -0
  36. package/std/net/http3-server.ts +911 -0
  37. package/std/net/http3.ts +2145 -0
  38. package/std/net/qpack.ts +1163 -0
  39. package/std/net/quic-conn-ack.ts +206 -0
  40. package/std/net/quic-conn-cid.ts +425 -0
  41. package/std/net/quic-conn-params.ts +462 -0
  42. package/std/net/quic-datagram.ts +130 -0
  43. package/std/net/quic-frame.ts +1206 -0
  44. package/std/net/quic-listener.ts +929 -0
  45. package/std/net/quic-packet.ts +1698 -0
  46. package/std/net/quic-recovery.ts +849 -0
  47. package/std/net/quic-stream.ts +1609 -0
  48. package/std/net/quic.ts +3578 -0
  49. package/std/net/tls/codec.ts +1089 -0
  50. package/std/net/tls/record-server.ts +1173 -0
  51. package/std/net/tls/record.ts +621 -0
  52. package/std/net/tls/schedule.ts +429 -0
  53. package/std/net/tls-tcp.ts +414 -0
  54. package/std/net/tls.ts +1437 -0
  55. package/std/net/websocket.ts +823 -0
  56. package/std/net/webtransport.ts +1334 -0
  57. package/std/secret.ts +62 -0
  58. package/std/text.ts +77 -0
@@ -0,0 +1,621 @@
1
+ /**
2
+ * `nish/net/tls/record` — the TLS 1.3 record layer (RFC 8446 §5): the
3
+ * framing of a byte stream into records, the protection of one direction of
4
+ * it under a traffic secret, and the next secret a KeyUpdate moves to.
5
+ *
6
+ * import { TlsRecordProtection, TlsRecordReader } from "nish/net/tls/record";
7
+ *
8
+ * const write = new TlsRecordProtection();
9
+ * write.install(TLS_AES_128_GCM_SHA256, serverApplicationSecret);
10
+ * const n: i32 = write.seal(TLS_CONTENT_APPLICATION_DATA, data, 0, len, 0, out, at);
11
+ *
12
+ * **Framing.** A record is a five-byte header — the content type, a legacy
13
+ * version and a 16-bit length — and that many bytes. `TlsRecordReader` holds
14
+ * the bytes of a stream that are not yet a whole record, in a buffer allocated
15
+ * once and big enough for the largest record the RFC allows (2^14 + 256 bytes
16
+ * of body), and says as soon as a header is in whether the record is one this
17
+ * layer will read: a content type it does not know is `unexpected_message`,
18
+ * and a length past the limit is `record_overflow`, before any of the body is
19
+ * waited for. The legacy version is ignored, as §5.1 says it must be.
20
+ *
21
+ * **Protection.** `TlsRecordProtection` is one direction of a connection:
22
+ * cleartext until `install` gives it a suite and a traffic secret, then
23
+ * TLSCiphertext (§5.2) under the AEAD the suite names. Each record's nonce is
24
+ * the traffic IV XORed with the 64-bit record sequence number (§5.3), and its
25
+ * additional data is the record's own header. Inside, the content is followed
26
+ * by its real type and by zero padding (§5.4); `open` finds the type by
27
+ * reading every byte of the decrypted record whatever the padding is, so how
28
+ * much padding there was is not in its timing, and refuses a record that is
29
+ * padding and nothing else. Both `seal` and `open` write into a buffer the
30
+ * caller owns and answer a count, and what they allocate on the way dies with
31
+ * the call (docs/LANGUAGE.md, "Memory model"), so a connection that has
32
+ * finished its handshake moves records without moving the arena.
33
+ *
34
+ * **Refusals are alerts.** Every function that can refuse answers the alert to
35
+ * send, negated, as a negative count: `-TLS_ALERT_BAD_RECORD_MAC` for a record
36
+ * that does not authenticate, `-TLS_ALERT_RECORD_OVERFLOW` for one past the
37
+ * limits, `-TLS_ALERT_UNEXPECTED_MESSAGE` for a type that cannot be here, and
38
+ * `-TLS_ALERT_INTERNAL_ERROR` for a mistake of the caller's — a window outside
39
+ * its array, an output too small, content over 2^14 bytes. Nothing the peer
40
+ * sends can make it panic.
41
+ *
42
+ * **Secrets.** A traffic secret arrives as plain bytes, since that is how
43
+ * `nish/net/tls` hands it over. The key, the IV and the AES key schedule
44
+ * derived from it are held in the protection's fields, because a `Secret` may
45
+ * not be one (NL2430), in arrays the protection reuses for every key it is
46
+ * given, and are zeroed when it is installed again or cleared — the key and
47
+ * IV with `secureZero`. The key and IV are derived straight into place, in an
48
+ * HKDF scratch that is wiped after each derivation, and the schedule is
49
+ * expanded into place by `aesKeyInto`. The schedule is `u64` words, which
50
+ * `secureZero` does not take, and are zeroed by ordinary stores that stand
51
+ * because it stays reachable (TLS-2 in `docs/security/tls.md`).
52
+ *
53
+ * The record layer is sans-IO: `nish/net/tls/record-server` runs it over a
54
+ * stream for `TlsServer`, and `nish/net/tls-tcp` puts that on a socket.
55
+ * Written from RFC 8446 §5, not ported from another implementation.
56
+ */
57
+ import { AesKey, aesGcmOpen, aesGcmSeal, aesKeyInto } from "nish/crypto/aes"
58
+ import { chacha20Poly1305Open, chacha20Poly1305Seal } from "nish/crypto/chacha20poly1305"
59
+ import {
60
+ TLS_ALERT_INTERNAL_ERROR,
61
+ TLS_ALERT_UNEXPECTED_MESSAGE,
62
+ TLS_LEGACY_VERSION,
63
+ } from "nish/net/tls/codec"
64
+ import { HkdfScratch, hkdfExpandLabelInto } from "nish/crypto/hkdf"
65
+ import { SHA384_SIZE } from "nish/crypto/sha512"
66
+ import {
67
+ TLS_CHACHA20_POLY1305_SHA256,
68
+ TLS_IV_SIZE,
69
+ tlsExpandLabel,
70
+ tlsSuiteHashLength,
71
+ tlsSuiteKeyLength,
72
+ tlsTrafficKeysInto,
73
+ } from "nish/net/tls/schedule"
74
+
75
+ // ---- The wire's constants ------------------------------------------------------
76
+
77
+ /** `change_cipher_spec`: the compatibility record of RFC 8446 §D.4, one byte of 1. */
78
+ export const TLS_CONTENT_CHANGE_CIPHER_SPEC: i32 = 20
79
+ /** `alert` (§6): a level and a description. */
80
+ export const TLS_CONTENT_ALERT: i32 = 21
81
+ /** `handshake` (§4). */
82
+ export const TLS_CONTENT_HANDSHAKE: i32 = 22
83
+ /** `application_data`, and the outer type of every protected record. */
84
+ export const TLS_CONTENT_APPLICATION_DATA: i32 = 23
85
+
86
+ /** The bytes before a record's body: its type, a legacy version and a 16-bit length. */
87
+ export const TLS_RECORD_HEADER_SIZE: i32 = 5
88
+ /** The most content one record carries: 2^14 bytes (§5.1). */
89
+ export const TLS_MAX_PLAINTEXT: i32 = 16384
90
+ /** The longest body a protected record may have: 2^14 + 256 bytes (§5.2). */
91
+ export const TLS_MAX_CIPHERTEXT: i32 = 16640
92
+ /** The longest whole record, header included. */
93
+ export const TLS_MAX_RECORD: i32 = 16645
94
+ /** The AEAD tag every protected record ends with; all three suites' is 16 bytes. */
95
+ export const TLS_RECORD_TAG_SIZE: i32 = 16
96
+ /** The longest a decrypted record may be: the content, its type, and its padding (§5.4). */
97
+ const TLS_MAX_INNER_PLAINTEXT: i32 = 16385
98
+
99
+ /** The KeyUpdate handshake message (§4.6.3). */
100
+ export const TLS_HANDSHAKE_KEY_UPDATE: i32 = 24
101
+
102
+ /** `close_notify` (§6.1): the sender writes nothing more. */
103
+ export const TLS_ALERT_CLOSE_NOTIFY: i32 = 0
104
+ /** `bad_record_mac`: a protected record did not authenticate. */
105
+ export const TLS_ALERT_BAD_RECORD_MAC: i32 = 20
106
+ /** `record_overflow`: a record longer than the limits allow. */
107
+ export const TLS_ALERT_RECORD_OVERFLOW: i32 = 22
108
+ /** `user_canceled` (§6.1): a warning that the peer is abandoning the handshake; a close follows. */
109
+ export const TLS_ALERT_USER_CANCELED: i32 = 90
110
+
111
+ /** An alert's level byte for `close_notify` and `user_canceled`. TLS 1.3 reads nothing into it (§6). */
112
+ export const TLS_ALERT_LEVEL_WARNING: i32 = 1
113
+ /** An alert's level byte for every error alert. */
114
+ export const TLS_ALERT_LEVEL_FATAL: i32 = 2
115
+
116
+ /**
117
+ * How many records one key protects before a writer moves to the next with a
118
+ * KeyUpdate: 2^24. AES-GCM's limit is 2^24.5 full-size records per key (RFC
119
+ * 8446 §5.5), and this is under it with room; ChaCha20-Poly1305's is far past
120
+ * it, and one rule for all three suites keeps the writer simple.
121
+ */
122
+ export const TLS_KEY_UPDATE_RECORDS: i64 = 16777216
123
+
124
+ /**
125
+ * The sequence number no record is sealed or opened at: 2^53 − 1. The counter
126
+ * must never wrap (§5.3), and stopping here, at a number no connection
127
+ * reaches, keeps the `i64` it is held in far from its own overflow.
128
+ */
129
+ export const TLS_LAST_SEQUENCE: i64 = 9007199254740991
130
+
131
+ /** A typed zero for the offsets below: a bare literal is an `f64` under `--number-mode f64`. */
132
+ const TLS_RECORD_FROM: i32 = 0
133
+
134
+ /**
135
+ * The traffic secret a KeyUpdate moves one direction to (RFC 8446 §7.2):
136
+ * `HKDF-Expand-Label(secret, "traffic upd", "", Hash.length)` over the hash
137
+ * `hashLength` names. The old secret is the caller's to wipe.
138
+ */
139
+ export const tlsNextTrafficSecret = (hashLength: i32, secret: u8[]): u8[] => {
140
+ const none: u8[] = []
141
+ return tlsExpandLabel(hashLength, secret, "traffic upd", none, hashLength)
142
+ }
143
+
144
+ /** Whether `[off, off + len)` is a window inside an array of `length` elements: every caller's range check. */
145
+ export const tlsRecordWindowFits = (length: i32, off: i32, len: i32): boolean =>
146
+ off >= 0 && len >= 0 && off <= length - len
147
+
148
+ /** The five header bytes of a record of `type` and body length `len`, written at `out[at]`. */
149
+ const tlsWriteRecordHeader = (out: u8[], at: i32, type: i32, len: i32): void => {
150
+ if (at < 0 || at > toI32(out.length) - TLS_RECORD_HEADER_SIZE) {
151
+ return
152
+ }
153
+ out[at] = toU8(type)
154
+ out[at + 1] = toU8(TLS_LEGACY_VERSION >> 8)
155
+ out[at + 2] = toU8(TLS_LEGACY_VERSION & 255)
156
+ out[at + 3] = toU8(len >> 8)
157
+ out[at + 4] = toU8(len & 255)
158
+ }
159
+
160
+ /**
161
+ * The length of the record whose header starts at `buf[off]`, header
162
+ * included, read from the `avail` bytes there: 0 while fewer than five are
163
+ * in, or the alert negated when the header announces a type this layer does
164
+ * not know (`unexpected_message`) or a body past 2^14 + 256 bytes
165
+ * (`record_overflow`, §5.2). The legacy version is not read (§5.1).
166
+ */
167
+ export const tlsRecordLength = (buf: u8[], off: i32, avail: i32): i32 => {
168
+ if (!tlsRecordWindowFits(toI32(buf.length), off, avail)) {
169
+ return -TLS_ALERT_INTERNAL_ERROR
170
+ }
171
+ if (avail < TLS_RECORD_HEADER_SIZE) {
172
+ return 0
173
+ }
174
+ const type: i32 = toI32(buf[off])
175
+ if (type < TLS_CONTENT_CHANGE_CIPHER_SPEC || type > TLS_CONTENT_APPLICATION_DATA) {
176
+ return -TLS_ALERT_UNEXPECTED_MESSAGE
177
+ }
178
+ const length: i32 = (toI32(buf[off + 3]) << 8) | toI32(buf[off + 4])
179
+ if (length > TLS_MAX_CIPHERTEXT) {
180
+ return -TLS_ALERT_RECORD_OVERFLOW
181
+ }
182
+ return TLS_RECORD_HEADER_SIZE + length
183
+ }
184
+
185
+ /**
186
+ * Zeroes an AES key schedule's round keys and H with ordinary stores, which
187
+ * stand because the schedule stays reachable for the next key (TLS-2).
188
+ */
189
+ const tlsClearAesKey = (aes: AesKey): void => {
190
+ for (let k: i32 = 0; k < toI32(aes.roundKeys.length); k++) {
191
+ aes.roundKeys[k] = toU64(0)
192
+ }
193
+ aes.hHi = toU64(0)
194
+ aes.hLo = toU64(0)
195
+ }
196
+
197
+ /**
198
+ * The bytes of a stream that are not yet a whole record. The buffer is
199
+ * allocated once, `TLS_MAX_RECORD` bytes, so a reader never grows: `push`
200
+ * takes what fits, and the bytes of a record stay until `consume` lets them
201
+ * go. The caller reads `buffer[start .. start + next())` for the record.
202
+ */
203
+ export class TlsRecordReader {
204
+ buffer: u8[]
205
+ /** The first byte not yet consumed. */
206
+ start: i32 = 0
207
+ /** One past the last byte pushed. */
208
+ end: i32 = 0
209
+
210
+ constructor() {
211
+ this.buffer = new Array<u8>(TLS_MAX_RECORD)
212
+ }
213
+
214
+ /** Forgets every byte, for a connection that starts again in the same slot. */
215
+ reset(): void {
216
+ this.start = 0
217
+ this.end = 0
218
+ }
219
+
220
+ /** How many bytes `push` would take now. */
221
+ space(): i32 {
222
+ return toI32(this.buffer.length) - (this.end - this.start)
223
+ }
224
+
225
+ /**
226
+ * Copies as many of `data[off .. off + len)` as fit and answers how many,
227
+ * after moving what is held to the front. A window outside `data` is
228
+ * `-TLS_ALERT_INTERNAL_ERROR`, and nothing is copied.
229
+ */
230
+ push(data: u8[], off: i32, len: i32): i32 {
231
+ if (!tlsRecordWindowFits(toI32(data.length), off, len)) {
232
+ return -TLS_ALERT_INTERNAL_ERROR
233
+ }
234
+ const held: i32 = this.end - this.start
235
+ // The held bytes move to the front only when the new ones would not fit after them.
236
+ if (this.start > 0 && toI32(this.buffer.length) - this.end < len) {
237
+ for (let k: i32 = 0; k < held; k++) {
238
+ this.buffer[k] = this.buffer[this.start + k]
239
+ }
240
+ this.start = 0
241
+ this.end = held
242
+ }
243
+ const room: i32 = toI32(this.buffer.length) - this.end
244
+ const take: i32 = len < room ? len : room
245
+ for (let k: i32 = 0; k < take; k++) {
246
+ this.buffer[this.end + k] = data[off + k]
247
+ }
248
+ this.end = this.end + take
249
+ return take
250
+ }
251
+
252
+ /**
253
+ * The length of the whole record at `start`, header included, once every
254
+ * byte of it is in; 0 before; or the alert negated for a header this layer
255
+ * refuses, as `tlsRecordLength` says.
256
+ */
257
+ next(): i32 {
258
+ const avail: i32 = this.end - this.start
259
+ const length: i32 = tlsRecordLength(this.buffer, this.start, avail)
260
+ return length > avail ? 0 : length
261
+ }
262
+
263
+ /** Lets the first `n` held bytes go: the record `next` answered, once it is handled. */
264
+ consume(n: i32): void {
265
+ const held: i32 = this.end - this.start
266
+ let take: i32 = n < held ? n : held
267
+ if (take < 0) {
268
+ take = 0
269
+ }
270
+ this.start = this.start + take
271
+ if (this.start === this.end) {
272
+ this.start = 0
273
+ this.end = 0
274
+ }
275
+ }
276
+ }
277
+
278
+ /**
279
+ * One direction of a connection's record protection. Cleartext (`suite` 0)
280
+ * until `install`; then every record is sealed or opened under the suite's
281
+ * AEAD with the key and IV of the traffic secret it was given, and its
282
+ * sequence number counts from 0. `contentType` is the type the last `open`
283
+ * found inside a protected record, or the header's type for a cleartext one.
284
+ *
285
+ * Every array it fills is made by the constructor: a key of each length, an
286
+ * AES key schedule of each size, the IV, the next traffic secret of each
287
+ * hash, and the HKDF scratch the keys are derived in. `key` and `aes` point
288
+ * at the ones the installed suite uses, so changing suites allocates nothing.
289
+ */
290
+ export class TlsRecordProtection {
291
+ /** The AEAD key (§7.3): the suite's key length, zeroed while the direction is cleartext. */
292
+ key: u8[]
293
+ /** The 12-byte traffic IV (§7.3), zeroed while the direction is cleartext. */
294
+ iv: u8[]
295
+ /** The AES key schedule for the two AES-GCM suites, the one of the installed key's size. */
296
+ aes: AesKey | null = null
297
+ /** The next record's sequence number (§5.3). */
298
+ sequence: i64 = 0
299
+ /**
300
+ * The sequence number at which a writer moves to the next key. Readable and
301
+ * writable, so a test can bring the key update forward; a reader ignores it.
302
+ */
303
+ recordLimit: i64 = 0
304
+ /** The negotiated suite, or 0 while the direction is cleartext. */
305
+ suite: i32 = 0
306
+ /** The real content type of the last record `open` answered. */
307
+ contentType: i32 = 0
308
+ /** The arrays `key` and `aes` point into: AES-128's and ChaCha20's or AES-256's. */
309
+ key16: u8[]
310
+ key32: u8[]
311
+ aes128: AesKey
312
+ aes256: AesKey
313
+ /** Where `advance` derives the next traffic secret: HashLen bytes of it. */
314
+ next: u8[]
315
+ /** Where the keys are derived. */
316
+ kdf: HkdfScratch
317
+
318
+ constructor() {
319
+ this.key16 = new Array<u8>(16)
320
+ this.key32 = new Array<u8>(32)
321
+ this.key = this.key16
322
+ this.iv = new Array<u8>(TLS_IV_SIZE)
323
+ this.aes128 = new AesKey(10, new Array<u64>(88))
324
+ this.aes256 = new AesKey(14, new Array<u64>(120))
325
+ this.next = new Array<u8>(SHA384_SIZE)
326
+ this.kdf = new HkdfScratch()
327
+ this.recordLimit = TLS_KEY_UPDATE_RECORDS
328
+ }
329
+
330
+ /** Whether records in this direction are protected. */
331
+ isProtected(): boolean {
332
+ return this.suite !== 0
333
+ }
334
+
335
+ /**
336
+ * Back to cleartext, with the sequence number at 0 and both keys, the IV
337
+ * and both AES key schedules zeroed: the keys and IV with `secureZero`,
338
+ * and the schedules' `u64` words with ordinary stores, which stand because
339
+ * the schedules stay reachable for the next `install` to fill (TLS-2).
340
+ */
341
+ clear(): void {
342
+ secureZero(this.key16)
343
+ secureZero(this.key32)
344
+ secureZero(this.iv)
345
+ tlsClearAesKey(this.aes128)
346
+ tlsClearAesKey(this.aes256)
347
+ this.suite = 0
348
+ this.sequence = 0
349
+ this.contentType = 0
350
+ }
351
+
352
+ /**
353
+ * Protects this direction under `secret` from the next record on: the
354
+ * suite's key and IV (§7.3), the sequence number back to 0, and what this
355
+ * replaces zeroed by `clear`. `false`, leaving the direction cleartext, for
356
+ * a suite `nish/net/tls/schedule` does not know or a secret that is not its
357
+ * hash's length.
358
+ *
359
+ * The key, the IV and the AES key schedule are written into the arrays this
360
+ * direction made when it was constructed, so an install allocates nothing
361
+ * that outlives it (TLS-3).
362
+ */
363
+ install(suite: i32, secret: u8[]): boolean {
364
+ this.clear()
365
+ const hashLength: i32 = tlsSuiteHashLength(suite)
366
+ if (hashLength === 0 || toI32(secret.length) !== hashLength) {
367
+ return false
368
+ }
369
+ this.reserve(suite)
370
+ this.derive(suite, secret)
371
+ this.suite = suite
372
+ return true
373
+ }
374
+
375
+ /** Points `key`, and for AES `aes`, at the arrays of the size `suite` needs. */
376
+ reserve(suite: i32): void {
377
+ this.key = tlsSuiteKeyLength(suite) === 16 ? this.key16 : this.key32
378
+ if (suite !== TLS_CHACHA20_POLY1305_SHA256) {
379
+ this.aes = tlsSuiteKeyLength(suite) === 16 ? this.aes128 : this.aes256
380
+ }
381
+ }
382
+
383
+ /**
384
+ * Fills the key, the IV and the AES key schedule from `secret`, into the
385
+ * arrays `reserve` chose: the key and IV derived straight into place in the
386
+ * direction's own HKDF scratch, which is wiped after, and the schedule
387
+ * expanded into this direction's `AesKey` by `aesKeyInto`. All of it runs
388
+ * inside an arena block, so a key install leaves the arena where it was.
389
+ * ChaCha20 has no schedule.
390
+ */
391
+ derive(suite: i32, secret: u8[]): void {
392
+ using _scope = arena()
393
+ tlsTrafficKeysInto(this.kdf, suite, secret, this.key, this.iv)
394
+ const aes: AesKey | null = this.aes
395
+ if (aes !== null && suite !== TLS_CHACHA20_POLY1305_SHA256) {
396
+ aesKeyInto(this.key, aes)
397
+ }
398
+ }
399
+
400
+ /**
401
+ * The AES key schedule when the suite is an AES-GCM one, `null` for
402
+ * ChaCha20: a protection that carried AES keeps its schedule for the next
403
+ * AES key, and must not use it for ChaCha20.
404
+ */
405
+ aesForSuite(): AesKey | null {
406
+ return this.suite === TLS_CHACHA20_POLY1305_SHA256 ? null : this.aes
407
+ }
408
+
409
+ /**
410
+ * Moves this direction to the next traffic secret (§7.2), the KeyUpdate
411
+ * step: `secret`, the current one, is overwritten in place by the next,
412
+ * and the keys are installed from it; the next secret's own copy is wiped.
413
+ * The next secret is derived in place inside an arena block, as `derive`'s
414
+ * keys are. False, as `install` answers, when the keys do not install.
415
+ */
416
+ advance(secret: u8[]): boolean {
417
+ const suite: i32 = this.suite
418
+ const hashLength: i32 = tlsSuiteHashLength(suite)
419
+ if (hashLength === 0) {
420
+ return false
421
+ }
422
+ const next: u8[] = this.next
423
+ {
424
+ using _scope = arena()
425
+ const none: u8[] = []
426
+ hkdfExpandLabelInto(
427
+ this.kdf,
428
+ hashLength,
429
+ secret,
430
+ "traffic upd",
431
+ none,
432
+ TLS_RECORD_FROM,
433
+ TLS_RECORD_FROM,
434
+ next,
435
+ TLS_RECORD_FROM,
436
+ hashLength
437
+ )
438
+ }
439
+ for (let k: i32 = 0; k < toI32(secret.length) && k < hashLength && k < toI32(next.length); k++) {
440
+ secret[k] = next[k]
441
+ }
442
+ secureZero(next)
443
+ return this.install(suite, secret)
444
+ }
445
+
446
+ /** The per-record nonce (§5.3): the IV with the sequence number XORed into its last eight bytes, big-endian. */
447
+ nonceInto(nonce: u8[]): void {
448
+ for (let k: i32 = 0; k < TLS_IV_SIZE && k < toI32(nonce.length) && k < toI32(this.iv.length); k++) {
449
+ const shift: i32 = (TLS_IV_SIZE - 1 - k) << 3
450
+ const byte: i32 = shift < 64 ? toI32((this.sequence >> toI64(shift)) & toI64(255)) : 0
451
+ nonce[k] = toU8(toI32(this.iv[k]) ^ byte)
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Writes one record of content type `type` holding `data[off .. off + len)`
457
+ * at `out[at]` and answers its length, header included. Cleartext, it is a
458
+ * TLSPlaintext, `padding` must be 0 and `type` may not be
459
+ * `application_data`; protected, it is a TLSCiphertext
460
+ * whose inner plaintext carries `type` and then `padding` zero bytes
461
+ * (§5.4), and the sequence number moves on. `-TLS_ALERT_INTERNAL_ERROR`,
462
+ * with nothing written, for content over 2^14 bytes, a padding that would
463
+ * take the inner plaintext past 2^14 + 1, a window outside `data`, too
464
+ * little room at `out[at]`, or a sequence number that would wrap.
465
+ */
466
+ seal(type: i32, data: u8[], off: i32, len: i32, padding: i32, out: u8[], at: i32): i32 {
467
+ if (!tlsRecordWindowFits(toI32(data.length), off, len) || len > TLS_MAX_PLAINTEXT || padding < 0) {
468
+ return -TLS_ALERT_INTERNAL_ERROR
469
+ }
470
+ if (this.suite === 0) {
471
+ // Application data is never sent in the clear, as `open` never reads
472
+ // it so: a cleartext direction asked for some has lost its keys.
473
+ if (
474
+ type === TLS_CONTENT_APPLICATION_DATA ||
475
+ padding !== 0 ||
476
+ !tlsRecordWindowFits(toI32(out.length), at, TLS_RECORD_HEADER_SIZE + len)
477
+ ) {
478
+ return -TLS_ALERT_INTERNAL_ERROR
479
+ }
480
+ tlsWriteRecordHeader(out, at, type, len)
481
+ for (let k: i32 = 0; k < len; k++) {
482
+ out[at + TLS_RECORD_HEADER_SIZE + k] = data[off + k]
483
+ }
484
+ return TLS_RECORD_HEADER_SIZE + len
485
+ }
486
+ if (padding > TLS_MAX_INNER_PLAINTEXT - 1 - len || this.sequence === TLS_LAST_SEQUENCE) {
487
+ return -TLS_ALERT_INTERNAL_ERROR
488
+ }
489
+ const inner: i32 = len + 1 + padding
490
+ const body: i32 = inner + TLS_RECORD_TAG_SIZE
491
+ if (!tlsRecordWindowFits(toI32(out.length), at, TLS_RECORD_HEADER_SIZE + body)) {
492
+ return -TLS_ALERT_INTERNAL_ERROR
493
+ }
494
+ const plaintext: u8[] = new Array<u8>(inner)
495
+ for (let k: i32 = 0; k < len && k < toI32(plaintext.length); k++) {
496
+ plaintext[k] = data[off + k]
497
+ }
498
+ plaintext[len] = toU8(type)
499
+ const aad: u8[] = new Array<u8>(5)
500
+ tlsWriteRecordHeader(aad, TLS_RECORD_FROM, TLS_CONTENT_APPLICATION_DATA, body)
501
+ const nonce: u8[] = new Array<u8>(12)
502
+ this.nonceInto(nonce)
503
+ const aes: AesKey | null = this.aesForSuite()
504
+ const sealed: u8[] | null =
505
+ aes !== null
506
+ ? aesGcmSeal(aes, nonce, aad, plaintext)
507
+ : chacha20Poly1305Seal(this.key, nonce, aad, plaintext)
508
+ // The nonce is the IV under a public sequence number: wiped as the IV is.
509
+ secureZero(nonce)
510
+ if (sealed === null || toI32(sealed.length) !== body) {
511
+ return -TLS_ALERT_INTERNAL_ERROR
512
+ }
513
+ tlsWriteRecordHeader(out, at, TLS_CONTENT_APPLICATION_DATA, body)
514
+ for (let k: i32 = 0; k < body && k < toI32(sealed.length); k++) {
515
+ out[at + TLS_RECORD_HEADER_SIZE + k] = sealed[k]
516
+ }
517
+ this.sequence = this.sequence + toI64(1)
518
+ return TLS_RECORD_HEADER_SIZE + body
519
+ }
520
+
521
+ /**
522
+ * Reads the whole record `record[off .. off + len)`, header included, and
523
+ * writes its content at `out[at]`: answers the content's length and sets
524
+ * `contentType`. Cleartext, the content is the body and the type the
525
+ * header's: `application_data` is `unexpected_message`, since it is never
526
+ * sent in the clear, and a body over 2^14 bytes is `record_overflow`.
527
+ * Protected, the header's type must be `application_data`
528
+ * (`unexpected_message` otherwise), the body must authenticate under this
529
+ * record's nonce with the header as additional data (`bad_record_mac`), the
530
+ * decrypted record may be no longer than 2^14 + 1 bytes
531
+ * (`record_overflow`), and its last non-zero byte is the real type — one
532
+ * that is all zeros is `unexpected_message` (§5.4). Every refusal is the
533
+ * alert negated; a window outside `record`, a `len` that is not the
534
+ * header's, or too little room at `out[at]` is `-TLS_ALERT_INTERNAL_ERROR`.
535
+ */
536
+ open(record: u8[], off: i32, len: i32, out: u8[], at: i32): i32 {
537
+ if (!tlsRecordWindowFits(toI32(record.length), off, len) || len < TLS_RECORD_HEADER_SIZE) {
538
+ return -TLS_ALERT_INTERNAL_ERROR
539
+ }
540
+ const declared: i32 = tlsRecordLength(record, off, len)
541
+ if (declared < 0) {
542
+ return declared
543
+ }
544
+ if (declared !== len) {
545
+ return -TLS_ALERT_INTERNAL_ERROR
546
+ }
547
+ const type: i32 = toI32(record[off])
548
+ const body: i32 = len - TLS_RECORD_HEADER_SIZE
549
+ if (this.suite === 0) {
550
+ if (type === TLS_CONTENT_APPLICATION_DATA) {
551
+ return -TLS_ALERT_UNEXPECTED_MESSAGE
552
+ }
553
+ if (body > TLS_MAX_PLAINTEXT) {
554
+ return -TLS_ALERT_RECORD_OVERFLOW
555
+ }
556
+ if (!tlsRecordWindowFits(toI32(out.length), at, body)) {
557
+ return -TLS_ALERT_INTERNAL_ERROR
558
+ }
559
+ for (let k: i32 = 0; k < body; k++) {
560
+ out[at + k] = record[off + TLS_RECORD_HEADER_SIZE + k]
561
+ }
562
+ this.contentType = type
563
+ return body
564
+ }
565
+ if (type !== TLS_CONTENT_APPLICATION_DATA) {
566
+ return -TLS_ALERT_UNEXPECTED_MESSAGE
567
+ }
568
+ if (body < TLS_RECORD_TAG_SIZE + 1) {
569
+ return -TLS_ALERT_BAD_RECORD_MAC
570
+ }
571
+ if (this.sequence === TLS_LAST_SEQUENCE) {
572
+ return -TLS_ALERT_INTERNAL_ERROR
573
+ }
574
+ const sealed: u8[] = new Array<u8>(body)
575
+ for (let k: i32 = 0; k < body && k < toI32(sealed.length); k++) {
576
+ sealed[k] = record[off + TLS_RECORD_HEADER_SIZE + k]
577
+ }
578
+ const aad: u8[] = new Array<u8>(5)
579
+ for (let k: i32 = 0; k < TLS_RECORD_HEADER_SIZE && k < toI32(aad.length); k++) {
580
+ aad[k] = record[off + k]
581
+ }
582
+ const nonce: u8[] = new Array<u8>(12)
583
+ this.nonceInto(nonce)
584
+ const aes: AesKey | null = this.aesForSuite()
585
+ const inner: u8[] | null =
586
+ aes !== null ? aesGcmOpen(aes, nonce, aad, sealed) : chacha20Poly1305Open(this.key, nonce, aad, sealed)
587
+ secureZero(nonce)
588
+ if (inner === null) {
589
+ return -TLS_ALERT_BAD_RECORD_MAC
590
+ }
591
+ // The record authenticated, so it is the peer's, and its number is spent
592
+ // whatever its content turns out to be.
593
+ this.sequence = this.sequence + toI64(1)
594
+ const innerLength: i32 = toI32(inner.length)
595
+ if (innerLength > TLS_MAX_INNER_PLAINTEXT) {
596
+ return -TLS_ALERT_RECORD_OVERFLOW
597
+ }
598
+ // The last non-zero byte and where it is, found by reading every byte
599
+ // and keeping each non-zero one by mask, so neither the time nor the
600
+ // addresses read depend on how much of the record was padding.
601
+ let last: i32 = -1
602
+ let found: i32 = 0
603
+ for (let k: i32 = 0; k < innerLength; k++) {
604
+ const b: i32 = toI32(inner[k])
605
+ const keep: i32 = (0 - b) >> 31
606
+ last = (k & keep) | (last & ~keep)
607
+ found = (b & keep) | (found & ~keep)
608
+ }
609
+ if (last < 0) {
610
+ return -TLS_ALERT_UNEXPECTED_MESSAGE
611
+ }
612
+ if (!tlsRecordWindowFits(toI32(out.length), at, last)) {
613
+ return -TLS_ALERT_INTERNAL_ERROR
614
+ }
615
+ for (let k: i32 = 0; k < last && k < innerLength; k++) {
616
+ out[at + k] = inner[k]
617
+ }
618
+ this.contentType = found
619
+ return last
620
+ }
621
+ }