@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
@@ -24,6 +24,12 @@
24
24
  * which HMAC does not keep secret; a received tag is compared with
25
25
  * `timingSafeEqual`, never `===`.
26
26
  *
27
+ * `HmacSha256Scratch` and `HmacSha384Scratch` are the same computation in
28
+ * hashers the caller allocates once: `begin` keys them again in place and
29
+ * `finishInto` writes the tag into the caller's array, so a loop of them
30
+ * stores nothing of its own and an arena scope around it releases all it
31
+ * allocated (see "Caller-owned scratch" below).
32
+ *
27
33
  * Written from RFC 2104, not ported from another implementation.
28
34
  */
29
35
  import { SHA256_BLOCK, SHA256_SIZE, Sha256, sha256 } from "nish/crypto/sha256"
@@ -40,25 +46,33 @@ const HMAC_OPAD: i32 = 0x5c
40
46
  const HMAC_FROM: i32 = 0
41
47
 
42
48
  /**
43
- * `key XOR pad` over a whole block of `blockSize` bytes, with `key` already no
44
- * longer than a block: the key's bytes XOR `pad`, then `pad` alone where the
45
- * zero padding of RFC 2104 §2 step (1) would be.
49
+ * `key[off .. off + len) XOR pad` over a whole block, `out`, with the key
50
+ * already no longer than the block: the key's bytes XOR `pad`, then `pad`
51
+ * alone where the zero padding of RFC 2104 §2 step (1) would be.
46
52
  */
47
- const hmacPadBlock = (key: u8[], blockSize: i32, pad: i32): u8[] => {
53
+ const hmacPadInto = (out: u8[], key: u8[], off: i32, len: i32, pad: i32): void => {
48
54
  const padByte: u8 = toU8(pad)
49
- const out: u8[] = new Array<u8>(blockSize)
50
55
  const outLength: i32 = toI32(out.length)
51
56
  const keyLength: i32 = toI32(key.length)
52
57
  // Bounded by both lengths, so both indices are proved in range.
53
58
  let i: i32 = 0
54
- while (i < keyLength && i < outLength) {
55
- out[i] = key[i] ^ padByte
59
+ while (i < len && i < outLength && off + i >= 0 && off + i < keyLength) {
60
+ out[i] = key[off + i] ^ padByte
56
61
  i += 1
57
62
  }
58
63
  while (i < outLength) {
59
64
  out[i] = padByte
60
65
  i += 1
61
66
  }
67
+ }
68
+
69
+ /**
70
+ * `key XOR pad` over a whole block of `blockSize` bytes, in a fresh array, with
71
+ * `key` already no longer than a block (`hmacPadInto`).
72
+ */
73
+ const hmacPadBlock = (key: u8[], blockSize: i32, pad: i32): u8[] => {
74
+ const out: u8[] = new Array<u8>(blockSize)
75
+ hmacPadInto(out, key, HMAC_FROM, toI32(key.length), pad)
62
76
  return out
63
77
  }
64
78
 
@@ -169,3 +183,289 @@ export const hmacSha256Verify = (key: u8[], data: u8[], tag: u8[]): boolean =>
169
183
  /** Whether `tag` is the HMAC-SHA-384 of `data` under `key`, compared as `hmacSha256Verify` does. */
170
184
  export const hmacSha384Verify = (key: u8[], data: u8[], tag: u8[]): boolean =>
171
185
  timingSafeEqual(hmacSha384(key, data), tag)
186
+
187
+ // ---- Caller-owned scratch --------------------------------------------------
188
+ //
189
+ // `HmacSha256` makes its two hashers in its constructor, and the one-shot
190
+ // functions above make an `HmacSha256` and a fresh tag on every call, so all
191
+ // of them store allocations of their own: memory no automatic arena scope and
192
+ // no `using a = arena()` block may release around them (docs/LANGUAGE.md,
193
+ // "`using a = arena()`", NL2424). The scratch below is the same computation in
194
+ // hashers the caller allocates once and keeps: `begin` keys it again by
195
+ // rewriting their state in place, and `finishInto` writes the tag into the
196
+ // caller's array. What it allocates on the way — the two digests a hasher
197
+ // answers — dies with the call that made it, so a caller may run it a
198
+ // thousand times inside one arena scope and leave the arena where it was.
199
+
200
+ /**
201
+ * Makes `to` the hasher `from` is — the same point in the same message, as
202
+ * `Sha256.copy` makes it — in a hasher the caller already holds, so that
203
+ * nothing is allocated. Copying from a hasher nothing has been fed starts `to`
204
+ * again. It is here rather than beside `Sha256` because the scratch below and
205
+ * the TLS transcript are what need it, and HMAC is the module both sit on.
206
+ */
207
+ export const hmacCopySha256 = (from: Sha256, to: Sha256): void => {
208
+ const fromState: u32[] = from.state
209
+ const toState: u32[] = to.state
210
+ for (let i: i32 = 0; i < toI32(fromState.length) && i < toI32(toState.length); i += 1) {
211
+ toState[i] = fromState[i]
212
+ }
213
+ const fromBlock: u8[] = from.block
214
+ const toBlock: u8[] = to.block
215
+ for (let i: i32 = 0; i < from.fill && i < toI32(fromBlock.length) && i < toI32(toBlock.length); i += 1) {
216
+ toBlock[i] = fromBlock[i]
217
+ }
218
+ to.fill = from.fill
219
+ to.total = from.total
220
+ to.finished = from.finished
221
+ }
222
+
223
+ /** `hmacCopySha256` for SHA-384: `to` made the hasher `from` is, in place. */
224
+ export const hmacCopySha384 = (from: Sha384, to: Sha384): void => {
225
+ const fromState: u64[] = from.engine.state
226
+ const toState: u64[] = to.engine.state
227
+ for (let i: i32 = 0; i < toI32(fromState.length) && i < toI32(toState.length); i += 1) {
228
+ toState[i] = fromState[i]
229
+ }
230
+ const fromBlock: u8[] = from.engine.block
231
+ const toBlock: u8[] = to.engine.block
232
+ for (
233
+ let i: i32 = 0;
234
+ i < from.engine.filled && i < toI32(fromBlock.length) && i < toI32(toBlock.length);
235
+ i += 1
236
+ ) {
237
+ toBlock[i] = fromBlock[i]
238
+ }
239
+ to.engine.filled = from.engine.filled
240
+ to.engine.count = from.engine.count
241
+ to.engine.finished = from.engine.finished
242
+ }
243
+
244
+ /**
245
+ * Zeroes everything `h` holds of the message it was fed: the pending block
246
+ * with `secureZero`, and the hash value and message schedule, which are words
247
+ * `secureZero` does not take, with ordinary stores. Those stand because the
248
+ * hasher stays reachable for the next `begin`. The hasher is left finished, so
249
+ * that feeding it before it is started again panics.
250
+ */
251
+ const hmacWipeSha256 = (h: Sha256): void => {
252
+ secureZero(h.block)
253
+ const state: u32[] = h.state
254
+ for (let i: i32 = 0; i < toI32(state.length); i += 1) {
255
+ state[i] = toU32(0)
256
+ }
257
+ const schedule: u32[] = h.schedule
258
+ for (let i: i32 = 0; i < toI32(schedule.length); i += 1) {
259
+ schedule[i] = toU32(0)
260
+ }
261
+ h.fill = 0
262
+ h.total = 0
263
+ h.finished = true
264
+ }
265
+
266
+ /** `hmacWipeSha256` for SHA-384. */
267
+ const hmacWipeSha384 = (h: Sha384): void => {
268
+ secureZero(h.engine.block)
269
+ const state: u64[] = h.engine.state
270
+ for (let i: i32 = 0; i < toI32(state.length); i += 1) {
271
+ state[i] = toU64(0)
272
+ }
273
+ const schedule: u64[] = h.engine.schedule
274
+ for (let i: i32 = 0; i < toI32(schedule.length); i += 1) {
275
+ schedule[i] = toU64(0)
276
+ }
277
+ h.engine.filled = 0
278
+ h.engine.count = toU64(0)
279
+ h.engine.finished = true
280
+ }
281
+
282
+ /** Whether `[off, off + len)` is a window inside an array of `length` elements. */
283
+ const hmacWindowFits = (length: i32, off: i32, len: i32): boolean =>
284
+ off >= 0 && len >= 0 && off <= length - len
285
+
286
+ /**
287
+ * HMAC-SHA-256 over hashers the caller owns: allocate one, then for each tag
288
+ * `begin` with the key, `update` with the message, and `finishInto` the
289
+ * caller's array. The tag is the one `hmacSha256` answers.
290
+ *
291
+ * const mac = new HmacSha256Scratch(); // once, outside the loop
292
+ * mac.begin(key, 0, key.length);
293
+ * mac.update(message, 0, message.length);
294
+ * mac.finishInto(tag, 0); // 32 bytes at tag[0]
295
+ *
296
+ * The key block is wiped with `secureZero` once both hashers have absorbed
297
+ * it, and so is every digest on the way; `wipe` zeroes the hashers too, for a
298
+ * caller done with the key. A key or tag window outside its array panics, as
299
+ * a message window does in `Sha256.update`.
300
+ */
301
+ export class HmacSha256Scratch {
302
+ /** H(K XOR ipad, …): keyed by `begin`, fed the message by `update`. */
303
+ inner: Sha256
304
+ /** H(K XOR opad, …): keyed by `begin`, fed the inner digest by `finishInto`. */
305
+ outer: Sha256
306
+ /** A hasher nothing is ever fed: the initial hash value `begin` copies from. */
307
+ fresh: Sha256
308
+ /** One key block XOR a pad, wiped as soon as it is absorbed. */
309
+ pad: u8[]
310
+ /** The SHA-256 of a key longer than a block, which RFC 2104 §2 keys with instead; wiped once used. */
311
+ keyHash: u8[]
312
+
313
+ constructor() {
314
+ this.inner = new Sha256()
315
+ this.outer = new Sha256()
316
+ this.fresh = new Sha256()
317
+ this.pad = new Array<u8>(SHA256_BLOCK)
318
+ this.keyHash = new Array<u8>(SHA256_SIZE)
319
+ }
320
+
321
+ /** Keys a new computation with `key[off .. off + len)`, a key longer than 64 bytes by its SHA-256 (RFC 2104 §2). */
322
+ begin(key: u8[], off: i32, len: i32): void {
323
+ if (!hmacWindowFits(toI32(key.length), off, len)) {
324
+ panic("HmacSha256Scratch: the key window is outside its array")
325
+ }
326
+ if (len <= SHA256_BLOCK) {
327
+ this.keyBlocks(key, off, len)
328
+ return
329
+ }
330
+ hmacCopySha256(this.fresh, this.inner)
331
+ this.inner.update(key, off, len)
332
+ const digest: u8[] = this.inner.digest()
333
+ for (let i: i32 = 0; i < toI32(digest.length) && i < toI32(this.keyHash.length); i += 1) {
334
+ this.keyHash[i] = digest[i]
335
+ }
336
+ secureZero(digest)
337
+ this.keyBlocks(this.keyHash, HMAC_FROM, SHA256_SIZE)
338
+ secureZero(this.keyHash)
339
+ }
340
+
341
+ /**
342
+ * Absorbs `key[off .. off + len)`, no longer than a block, XOR ipad into the
343
+ * inner hasher and XOR opad into the outer one, each started again, and
344
+ * wipes the block. A method of its own so that `begin` never keeps the
345
+ * caller's key in a local it reassigns, which would make the key look
346
+ * stored to the arena analysis.
347
+ */
348
+ keyBlocks(key: u8[], off: i32, len: i32): void {
349
+ hmacPadInto(this.pad, key, off, len, HMAC_IPAD)
350
+ hmacCopySha256(this.fresh, this.inner)
351
+ this.inner.update(this.pad, HMAC_FROM, SHA256_BLOCK)
352
+ hmacPadInto(this.pad, key, off, len, HMAC_OPAD)
353
+ hmacCopySha256(this.fresh, this.outer)
354
+ this.outer.update(this.pad, HMAC_FROM, SHA256_BLOCK)
355
+ secureZero(this.pad)
356
+ }
357
+
358
+ /** Absorbs `data[off .. off + len)`; a window outside `data` panics in `Sha256.update`. */
359
+ update(data: u8[], off: i32, len: i32): void {
360
+ this.inner.update(data, off, len)
361
+ }
362
+
363
+ /** Writes the 32-byte tag at `out[at]` and ends the computation; a window outside `out` panics. */
364
+ finishInto(out: u8[], at: i32): void {
365
+ if (!hmacWindowFits(toI32(out.length), at, SHA256_SIZE)) {
366
+ panic("HmacSha256Scratch: the tag window is outside its array")
367
+ }
368
+ const innerDigest: u8[] = this.inner.digest()
369
+ this.outer.update(innerDigest, HMAC_FROM, SHA256_SIZE)
370
+ secureZero(innerDigest)
371
+ const tag: u8[] = this.outer.digest()
372
+ for (let i: i32 = 0; i < SHA256_SIZE && i < toI32(tag.length) && at + i < toI32(out.length); i += 1) {
373
+ out[at + i] = tag[i]
374
+ }
375
+ secureZero(tag)
376
+ }
377
+
378
+ /** Zeroes both keyed hashers, for a caller done with the key; `begin` starts again. */
379
+ wipe(): void {
380
+ hmacWipeSha256(this.inner)
381
+ hmacWipeSha256(this.outer)
382
+ }
383
+ }
384
+
385
+ /**
386
+ * HMAC-SHA-384 over hashers the caller owns, with `HmacSha256Scratch`'s
387
+ * methods. The block is 128 bytes and the tag 48.
388
+ */
389
+ export class HmacSha384Scratch {
390
+ /** H(K XOR ipad, …): keyed by `begin`, fed the message by `update`. */
391
+ inner: Sha384
392
+ /** H(K XOR opad, …): keyed by `begin`, fed the inner digest by `finishInto`. */
393
+ outer: Sha384
394
+ /** A hasher nothing is ever fed: the initial hash value `begin` copies from. */
395
+ fresh: Sha384
396
+ /** One key block XOR a pad, wiped as soon as it is absorbed. */
397
+ pad: u8[]
398
+ /** The SHA-384 of a key longer than a block; wiped once used. */
399
+ keyHash: u8[]
400
+
401
+ constructor() {
402
+ this.inner = new Sha384()
403
+ this.outer = new Sha384()
404
+ this.fresh = new Sha384()
405
+ this.pad = new Array<u8>(SHA512_BLOCK)
406
+ this.keyHash = new Array<u8>(SHA384_SIZE)
407
+ }
408
+
409
+ /** Keys a new computation with `key[off .. off + len)`, a key longer than 128 bytes by its SHA-384 (RFC 2104 §2). */
410
+ begin(key: u8[], off: i32, len: i32): void {
411
+ if (!hmacWindowFits(toI32(key.length), off, len)) {
412
+ panic("HmacSha384Scratch: the key window is outside its array")
413
+ }
414
+ if (len <= SHA512_BLOCK) {
415
+ this.keyBlocks(key, off, len)
416
+ return
417
+ }
418
+ hmacCopySha384(this.fresh, this.inner)
419
+ this.inner.update(key, off, len)
420
+ const digest: u8[] = this.inner.digest()
421
+ for (let i: i32 = 0; i < toI32(digest.length) && i < toI32(this.keyHash.length); i += 1) {
422
+ this.keyHash[i] = digest[i]
423
+ }
424
+ secureZero(digest)
425
+ this.keyBlocks(this.keyHash, HMAC_FROM, SHA384_SIZE)
426
+ secureZero(this.keyHash)
427
+ }
428
+
429
+ /**
430
+ * Absorbs `key[off .. off + len)`, no longer than a block, XOR ipad into the
431
+ * inner hasher and XOR opad into the outer one, each started again, and
432
+ * wipes the block. A method of its own so that `begin` never keeps the
433
+ * caller's key in a local it reassigns, which would make the key look
434
+ * stored to the arena analysis.
435
+ */
436
+ keyBlocks(key: u8[], off: i32, len: i32): void {
437
+ hmacPadInto(this.pad, key, off, len, HMAC_IPAD)
438
+ hmacCopySha384(this.fresh, this.inner)
439
+ this.inner.update(this.pad, HMAC_FROM, SHA512_BLOCK)
440
+ hmacPadInto(this.pad, key, off, len, HMAC_OPAD)
441
+ hmacCopySha384(this.fresh, this.outer)
442
+ this.outer.update(this.pad, HMAC_FROM, SHA512_BLOCK)
443
+ secureZero(this.pad)
444
+ }
445
+
446
+ /** Absorbs `data[off .. off + len)`; a window outside `data` panics in the hasher. */
447
+ update(data: u8[], off: i32, len: i32): void {
448
+ this.inner.update(data, off, len)
449
+ }
450
+
451
+ /** Writes the 48-byte tag at `out[at]` and ends the computation; a window outside `out` panics. */
452
+ finishInto(out: u8[], at: i32): void {
453
+ if (!hmacWindowFits(toI32(out.length), at, SHA384_SIZE)) {
454
+ panic("HmacSha384Scratch: the tag window is outside its array")
455
+ }
456
+ const innerDigest: u8[] = this.inner.digest()
457
+ this.outer.update(innerDigest, HMAC_FROM, SHA384_SIZE)
458
+ secureZero(innerDigest)
459
+ const tag: u8[] = this.outer.digest()
460
+ for (let i: i32 = 0; i < SHA384_SIZE && i < toI32(tag.length) && at + i < toI32(out.length); i += 1) {
461
+ out[at + i] = tag[i]
462
+ }
463
+ secureZero(tag)
464
+ }
465
+
466
+ /** Zeroes both keyed hashers, for a caller done with the key; `begin` starts again. */
467
+ wipe(): void {
468
+ hmacWipeSha384(this.inner)
469
+ hmacWipeSha384(this.outer)
470
+ }
471
+ }