@amritk/nish-x86_64-linux 0.17.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 (61) 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 +231 -20
  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 +203 -0
  14. package/runtime/runtime-wasm.c +16 -3
  15. package/runtime/runtime.c +89 -23
  16. package/runtime/shim.mjs +71 -1
  17. package/scripts/build.sh +225 -20
  18. package/std/README.md +86 -55
  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 +1376 -42
  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 +1412 -70
  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 +231 -45
  42. package/std/net/http3.ts +187 -28
  43. package/std/net/prefork.ts +412 -0
  44. package/std/net/quic-conn-params.ts +86 -16
  45. package/std/net/quic-listener.ts +576 -219
  46. package/std/net/quic-packet.ts +443 -58
  47. package/std/net/quic-recovery.ts +361 -30
  48. package/std/net/quic-stream.ts +434 -28
  49. package/std/net/quic.ts +922 -206
  50. package/std/net/router.ts +676 -0
  51. package/std/net/tls/codec.ts +426 -27
  52. package/std/net/tls/record-server.ts +478 -132
  53. package/std/net/tls/record.ts +133 -42
  54. package/std/net/tls/schedule.ts +464 -55
  55. package/std/net/tls/ticket.ts +610 -0
  56. package/std/net/tls-tcp.ts +204 -57
  57. package/std/net/tls.ts +1132 -305
  58. package/std/net/url.ts +571 -0
  59. package/std/random.ts +94 -0
  60. package/std/text.ts +77 -0
  61. package/std/threads.ts +438 -14
@@ -65,6 +65,29 @@
65
65
  * length other than 32 or 48, or an output window outside its array, is the
66
66
  * caller's bug and panics.
67
67
  *
68
+ * **What the key reaches is wiped** (CLAUDE.md, "Security"). Every HMAC on
69
+ * the way wipes its key blocks and hashers (`nish/crypto/hmac`), and `expand`
70
+ * wipes each T(i) once the next has replaced it, and the last one before it
71
+ * returns. The PRK `extract` answers and the output `expand` answers are the
72
+ * caller's. A caller that holds its input keying material as a `Secret<u8[]>`
73
+ * (`nish:secret`) derives with `hkdfSha256Secret` or `hkdfSha384Secret`,
74
+ * which run both steps, hand the output back as a `Secret` and wipe the PRK
75
+ * between them, or with the `*Secret` twins of each step, HKDF-Expand-Label's
76
+ * among them:
77
+ *
78
+ * const okm: Secret<u8[]> | null = hkdfSha256Secret(salt, ikm, info, 42);
79
+ * const key: Secret<u8[]> = hkdfExpandLabelSha256Secret(prk, "key", context, 16);
80
+ *
81
+ * A caller that already holds the destination — a field of a long-lived
82
+ * object, say, that keeps one traffic secret and ratchets it — derives into
83
+ * it with the `*Held` forms, which fill as many bytes as the `Secret` holds,
84
+ * in place, and wipe the plain output they copied from. The destination may
85
+ * be the PRK itself; `hkdfExpandLabelHeldInto` writes a plain window instead,
86
+ * for output that is plain by design, a record layer's AEAD key and IV:
87
+ *
88
+ * hkdfExpandLabelSha256Held(secret, secret, "traffic upd", none);
89
+ * hkdfExpandLabelHeldInto(32, secret, "key", none, key, 0, 16);
90
+ *
68
91
  * Written from RFC 5869 and RFC 8446 §7.1, not ported from another
69
92
  * implementation.
70
93
  */
@@ -78,6 +101,7 @@ import {
78
101
  } from "nish/crypto/hmac"
79
102
  import { SHA256_SIZE } from "nish/crypto/sha256"
80
103
  import { SHA384_SIZE } from "nish/crypto/sha512"
104
+ import { Secret, expose, exposeWith, secret, wipe } from "nish:secret"
81
105
 
82
106
  /** The largest block counter, and so the most blocks `expand` can make (RFC 5869 §2.3). */
83
107
  const HKDF_MAX_BLOCKS: i32 = 255
@@ -145,10 +169,13 @@ export const hkdfExpandSha256 = (prk: u8[], info: u8[], length: i32): u8[] | nul
145
169
  mac.update(info, HKDF_FROM, toI32(info.length))
146
170
  counter[0] = toU8(i)
147
171
  mac.update(counter, HKDF_FROM, counterLength)
148
- previous = mac.digest()
172
+ const block: u8[] = mac.digest()
173
+ wipe(previous)
174
+ previous = block
149
175
  at = hkdfAppend(out, at, previous)
150
176
  i += 1
151
177
  }
178
+ wipe(previous)
152
179
  return out
153
180
  }
154
181
 
@@ -179,13 +206,115 @@ export const hkdfExpandSha384 = (prk: u8[], info: u8[], length: i32): u8[] | nul
179
206
  mac.update(info, HKDF_FROM, toI32(info.length))
180
207
  counter[0] = toU8(i)
181
208
  mac.update(counter, HKDF_FROM, counterLength)
182
- previous = mac.digest()
209
+ const block: u8[] = mac.digest()
210
+ wipe(previous)
211
+ previous = block
183
212
  at = hkdfAppend(out, at, previous)
184
213
  i += 1
185
214
  }
215
+ wipe(previous)
186
216
  return out
187
217
  }
188
218
 
219
+ // ---- Keys held as `Secret`s ------------------------------------------------
220
+ //
221
+ // The `*Secret` functions the module docs name: each reads its key only inside
222
+ // `exposeWith`, and hands the PRK and the output back as `Secret`s.
223
+
224
+ /** What `expand` needs besides the PRK, as one argument, since `exposeWith` hands on only one. */
225
+ interface HkdfExpandRequest {
226
+ info: u8[]
227
+ length: i32
228
+ }
229
+
230
+ /**
231
+ * HKDF-Extract with HMAC-SHA-256 on the plain IKM, for `exposeWith` to run.
232
+ * It keys HMAC with `salt` itself rather than through `hkdfSalt`, whose answer
233
+ * may be `salt`, because the checker does not credit that path with leaving
234
+ * `salt` unwritten (NL2450). The PRK is the same: an empty salt is padded to
235
+ * the block with zeros, as the HashLen zeros of §2.2 would be.
236
+ */
237
+ const hkdfExtractSha256Exposed = (ikm: u8[], salt: u8[]): u8[] => hmacSha256(salt, ikm)
238
+
239
+ /** HKDF-Extract with HMAC-SHA-384 on the plain IKM, for `exposeWith` to run, as the SHA-256 one is. */
240
+ const hkdfExtractSha384Exposed = (ikm: u8[], salt: u8[]): u8[] => hmacSha384(salt, ikm)
241
+
242
+ /** HKDF-Expand with HMAC-SHA-256 on the plain PRK, for `exposeWith` to run. */
243
+ const hkdfExpandSha256Exposed = (prk: u8[], request: HkdfExpandRequest): u8[] | null =>
244
+ hkdfExpandSha256(prk, request.info, request.length)
245
+
246
+ /** HKDF-Expand with HMAC-SHA-384 on the plain PRK, for `exposeWith` to run. */
247
+ const hkdfExpandSha384Exposed = (prk: u8[], request: HkdfExpandRequest): u8[] | null =>
248
+ hkdfExpandSha384(prk, request.info, request.length)
249
+
250
+ /** `hkdfExtractSha256` on input keying material held as a `Secret`: the 32-byte PRK, held as one. */
251
+ export const hkdfExtractSha256Secret = (salt: u8[], ikm: Secret<u8[]>): Secret<u8[]> => {
252
+ const prk: u8[] = exposeWith(ikm, salt, hkdfExtractSha256Exposed)
253
+ return secret(prk)
254
+ }
255
+
256
+ /** `hkdfExtractSha384` on input keying material held as a `Secret`: the 48-byte PRK, held as one. */
257
+ export const hkdfExtractSha384Secret = (salt: u8[], ikm: Secret<u8[]>): Secret<u8[]> => {
258
+ const prk: u8[] = exposeWith(ikm, salt, hkdfExtractSha384Exposed)
259
+ return secret(prk)
260
+ }
261
+
262
+ /**
263
+ * `hkdfExpandSha256` on a PRK held as a `Secret`: the output keying material,
264
+ * held as one, or `null` where `hkdfExpandSha256` answers `null`.
265
+ */
266
+ export const hkdfExpandSha256Secret = (prk: Secret<u8[]>, info: u8[], length: i32): Secret<u8[]> | null => {
267
+ const request: HkdfExpandRequest = { info: info, length: length }
268
+ const okm: u8[] | null = exposeWith(prk, request, hkdfExpandSha256Exposed)
269
+ if (okm === null) {
270
+ return null
271
+ }
272
+ return secret(okm)
273
+ }
274
+
275
+ /**
276
+ * `hkdfExpandSha384` on a PRK held as a `Secret`: the output keying material,
277
+ * held as one, or `null` where `hkdfExpandSha384` answers `null`.
278
+ */
279
+ export const hkdfExpandSha384Secret = (prk: Secret<u8[]>, info: u8[], length: i32): Secret<u8[]> | null => {
280
+ const request: HkdfExpandRequest = { info: info, length: length }
281
+ const okm: u8[] | null = exposeWith(prk, request, hkdfExpandSha384Exposed)
282
+ if (okm === null) {
283
+ return null
284
+ }
285
+ return secret(okm)
286
+ }
287
+
288
+ /**
289
+ * HKDF with HMAC-SHA-256, both steps (RFC 5869 §2): `length` bytes from `salt`,
290
+ * the `Secret` input keying material and `info`, held as a `Secret`, or `null`
291
+ * where `hkdfExpandSha256` answers `null`. The PRK is wiped before it returns.
292
+ */
293
+ export const hkdfSha256Secret = (
294
+ salt: u8[],
295
+ ikm: Secret<u8[]>,
296
+ info: u8[],
297
+ length: i32
298
+ ): Secret<u8[]> | null => {
299
+ const prk: Secret<u8[]> = hkdfExtractSha256Secret(salt, ikm)
300
+ const okm: Secret<u8[]> | null = hkdfExpandSha256Secret(prk, info, length)
301
+ wipe(prk)
302
+ return okm
303
+ }
304
+
305
+ /** `hkdfSha256Secret` with HMAC-SHA-384: the PRK is 48 bytes, and wiped before it returns. */
306
+ export const hkdfSha384Secret = (
307
+ salt: u8[],
308
+ ikm: Secret<u8[]>,
309
+ info: u8[],
310
+ length: i32
311
+ ): Secret<u8[]> | null => {
312
+ const prk: Secret<u8[]> = hkdfExtractSha384Secret(salt, ikm)
313
+ const okm: Secret<u8[]> | null = hkdfExpandSha384Secret(prk, info, length)
314
+ wipe(prk)
315
+ return okm
316
+ }
317
+
189
318
  /**
190
319
  * The `HkdfLabel` struct of RFC 8446 §7.1, the `info` HKDF-Expand-Label hands
191
320
  * to `expand`, or a panic naming `caller` when `length`, `label` or `context`
@@ -212,6 +341,13 @@ const hkdfLabel = (caller: string, label: string, context: u8[], length: i32): u
212
341
  out[1] = toU8(length & 0xff)
213
342
  out[2] = toU8(fullLength)
214
343
  for (let k: i32 = 0; k < fullLength; k += 1) {
344
+ // `out` is longer than `3 + fullLength`, but the proof cannot read that
345
+ // off the sum it was allocated with; this test is what proves the store.
346
+ // It sits in the body rather than the loop condition, which stays the
347
+ // plain `k < fullLength` the attribute pass counts as a bounded loop.
348
+ if (3 + k >= toI32(out.length)) {
349
+ panic("hkdfLabel: the label overran its buffer")
350
+ }
215
351
  out[3 + k] = toU8(toI32(full.charCodeAt(k)))
216
352
  }
217
353
  out[3 + fullLength] = toU8(contextLength)
@@ -221,12 +357,12 @@ const hkdfLabel = (caller: string, label: string, context: u8[], length: i32): u
221
357
 
222
358
  /**
223
359
  * HKDF-Expand-Label with HMAC-SHA-256 (RFC 8446 §7.1): `length` bytes from
224
- * `secret`, `"tls13 " + label` and `context`. Panics on an argument out of the
360
+ * `prk` (RFC 8446's Secret), `"tls13 " + label` and `context`. Panics on an argument out of the
225
361
  * range the module docs give, a secret shorter than 32 bytes among them.
226
362
  */
227
- export const hkdfExpandLabelSha256 = (secret: u8[], label: string, context: u8[], length: i32): u8[] => {
363
+ export const hkdfExpandLabelSha256 = (prk: u8[], label: string, context: u8[], length: i32): u8[] => {
228
364
  const okm: u8[] | null = hkdfExpandSha256(
229
- secret,
365
+ prk,
230
366
  hkdfLabel("hkdfExpandLabelSha256", label, context, length),
231
367
  length
232
368
  )
@@ -238,12 +374,12 @@ export const hkdfExpandLabelSha256 = (secret: u8[], label: string, context: u8[]
238
374
 
239
375
  /**
240
376
  * HKDF-Expand-Label with HMAC-SHA-384 (RFC 8446 §7.1): `length` bytes from
241
- * `secret`, `"tls13 " + label` and `context`. Panics on an argument out of the
377
+ * `prk` (RFC 8446's Secret), `"tls13 " + label` and `context`. Panics on an argument out of the
242
378
  * range the module docs give, a secret shorter than 48 bytes among them.
243
379
  */
244
- export const hkdfExpandLabelSha384 = (secret: u8[], label: string, context: u8[], length: i32): u8[] => {
380
+ export const hkdfExpandLabelSha384 = (prk: u8[], label: string, context: u8[], length: i32): u8[] => {
245
381
  const okm: u8[] | null = hkdfExpandSha384(
246
- secret,
382
+ prk,
247
383
  hkdfLabel("hkdfExpandLabelSha384", label, context, length),
248
384
  length
249
385
  )
@@ -253,6 +389,47 @@ export const hkdfExpandLabelSha384 = (secret: u8[], label: string, context: u8[]
253
389
  return okm
254
390
  }
255
391
 
392
+ /**
393
+ * `hkdfExpandLabelSha256` on a secret held as a `Secret`: the `length` bytes,
394
+ * held as one. The label is built outside `expose`, since the label and the
395
+ * context are not secret, and the PRK is read only inside it. Panics as
396
+ * `hkdfExpandLabelSha256` does.
397
+ */
398
+ export const hkdfExpandLabelSha256Secret = (
399
+ prk: Secret<u8[]>,
400
+ label: string,
401
+ context: u8[],
402
+ length: i32
403
+ ): Secret<u8[]> => {
404
+ const okm: Secret<u8[]> | null = hkdfExpandSha256Secret(
405
+ prk,
406
+ hkdfLabel("hkdfExpandLabelSha256Secret", label, context, length),
407
+ length
408
+ )
409
+ if (okm === null) {
410
+ panic("hkdfExpandLabelSha256Secret: a secret shorter than 32 bytes")
411
+ }
412
+ return okm
413
+ }
414
+
415
+ /** `hkdfExpandLabelSha256Secret` with HMAC-SHA-384: panics as `hkdfExpandLabelSha384` does. */
416
+ export const hkdfExpandLabelSha384Secret = (
417
+ prk: Secret<u8[]>,
418
+ label: string,
419
+ context: u8[],
420
+ length: i32
421
+ ): Secret<u8[]> => {
422
+ const okm: Secret<u8[]> | null = hkdfExpandSha384Secret(
423
+ prk,
424
+ hkdfLabel("hkdfExpandLabelSha384Secret", label, context, length),
425
+ length
426
+ )
427
+ if (okm === null) {
428
+ panic("hkdfExpandLabelSha384Secret: a secret shorter than 48 bytes")
429
+ }
430
+ return okm
431
+ }
432
+
256
433
  // ---- Caller-owned scratch ----------------------------------------------------
257
434
 
258
435
  /** The longest `HkdfLabel`: two length bytes, `label<7..255>` and `context<0..255>`, each with its length byte. */
@@ -435,7 +612,7 @@ export const hkdfExpandInto = (
435
612
 
436
613
  /**
437
614
  * HKDF-Expand-Label (RFC 8446 §7.1) over the hash `hashLength` names: `length`
438
- * bytes from `secret`, `"tls13 " + label` and the context
615
+ * bytes from `prk` (RFC 8446's Secret), `"tls13 " + label` and the context
439
616
  * `context[contextOff .. contextOff + contextLen)`, written at `out[at]`. It
440
617
  * panics where `hkdfExpandLabelSha256` does — a length outside 0 to 255, a
441
618
  * label empty or past 249 bytes, a context past 255, a secret shorter than
@@ -444,7 +621,7 @@ export const hkdfExpandInto = (
444
621
  export const hkdfExpandLabelInto = (
445
622
  s: HkdfScratch,
446
623
  hashLength: i32,
447
- secret: u8[],
624
+ prk: u8[],
448
625
  label: string,
449
626
  context: u8[],
450
627
  contextOff: i32,
@@ -468,7 +645,7 @@ export const hkdfExpandLabelInto = (
468
645
  ) {
469
646
  panic("hkdfExpandLabelInto: the context window is outside its array or longer than 255 bytes")
470
647
  }
471
- if (toI32(secret.length) < hashLength) {
648
+ if (toI32(prk.length) < hashLength) {
472
649
  panic("hkdfExpandLabelInto: a secret shorter than HashLen")
473
650
  }
474
651
  hkdfCheckOutput("hkdfExpandLabelInto", out, at, length)
@@ -491,5 +668,120 @@ export const hkdfExpandLabelInto = (
491
668
  for (let k: i32 = 0; k < contextLen && contextAt + 1 + k < toI32(info.length); k += 1) {
492
669
  info[contextAt + 1 + k] = context[contextOff + k]
493
670
  }
494
- hkdfExpandWindow(s, hashLength, secret, info, contextAt + 1 + contextLen, out, at, length)
671
+ hkdfExpandWindow(s, hashLength, prk, info, contextAt + 1 + contextLen, out, at, length)
672
+ }
673
+
674
+ // ---- From and into a held `Secret` -----------------------------------------
675
+ //
676
+ // The `*Held` forms derive from a `Secret<u8[]>` the caller already holds, and
677
+ // into one, overwriting its bytes in place, so a key that lives in one place
678
+ // for a connection's life is never handed out as a fresh `Secret`. The output
679
+ // is derived plain inside `exposeWith` of the PRK, by the function the
680
+ // `*Secret` forms run, copied where it goes, and wiped. None of this opens a
681
+ // `using arena()` block: every hasher's constructor stores the arrays it
682
+ // allocates into its fields, which the block refuses (NL2424), and a scratch
683
+ // made before the block could be neither written nor wiped inside `exposeWith`
684
+ // (NL2450, and `secureZero` is C). The function's own scope reclaims the
685
+ // temporaries instead.
686
+
687
+ /** The length of the bytes a `Secret` holds, which is public, for `expose` to read. */
688
+ const hkdfHeldLength = (bytes: u8[]): i32 => toI32(bytes.length)
689
+
690
+ /**
691
+ * HKDF-Expand-Label of the held `prk` over the hash `hashLength` names: the
692
+ * `length` bytes, plain, for the caller to copy and wipe. Panics naming
693
+ * `caller` where `hkdfExpandLabelSha256` does.
694
+ */
695
+ const hkdfHeldDerive = (
696
+ caller: string,
697
+ hashLength: i32,
698
+ prk: Secret<u8[]>,
699
+ label: string,
700
+ context: u8[],
701
+ length: i32
702
+ ): u8[] => {
703
+ hkdfCheckHash(caller, hashLength)
704
+ const request: HkdfExpandRequest = { info: hkdfLabel(caller, label, context, length), length: length }
705
+ const okm: u8[] | null =
706
+ hashLength === SHA256_SIZE
707
+ ? exposeWith(prk, request, hkdfExpandSha256Exposed)
708
+ : exposeWith(prk, request, hkdfExpandSha384Exposed)
709
+ if (okm === null) {
710
+ panic(
711
+ `${caller}: a secret shorter than ${hashLength} bytes, or more than ${255 * hashLength} bytes asked`
712
+ )
713
+ }
714
+ return okm
715
+ }
716
+
717
+ /**
718
+ * Overwrites the bytes a `Secret` holds with `okm`, for `exposeWith` to run on
719
+ * the destination: it writes the value it is shown, never its argument.
720
+ */
721
+ const hkdfHeldCopy = (held: u8[], okm: u8[]): void => {
722
+ for (let k: i32 = 0; k < toI32(held.length) && k < toI32(okm.length); k++) {
723
+ held[k] = okm[k]
724
+ }
725
+ }
726
+
727
+ /** Derives into `dst`, as many bytes as it holds, for the two exported forms. */
728
+ const hkdfHeld = (
729
+ caller: string,
730
+ hashLength: i32,
731
+ dst: Secret<u8[]>,
732
+ prk: Secret<u8[]>,
733
+ label: string,
734
+ context: u8[]
735
+ ): void => {
736
+ const okm: u8[] = hkdfHeldDerive(caller, hashLength, prk, label, context, expose(dst, hkdfHeldLength))
737
+ exposeWith(dst, okm, hkdfHeldCopy)
738
+ wipe(okm)
739
+ }
740
+
741
+ /**
742
+ * `hkdfExpandLabelSha256Secret` into `dst`: as many bytes as `dst` holds,
743
+ * derived from `prk`, the label and `context` and written over `dst`'s in
744
+ * place. `dst` may be `prk` itself, since the output is whole before it is
745
+ * copied: that is a ratchet. Panics as `hkdfExpandLabelSha256` does.
746
+ */
747
+ export const hkdfExpandLabelSha256Held = (
748
+ dst: Secret<u8[]>,
749
+ prk: Secret<u8[]>,
750
+ label: string,
751
+ context: u8[]
752
+ ): void => {
753
+ hkdfHeld("hkdfExpandLabelSha256Held", SHA256_SIZE, dst, prk, label, context)
754
+ }
755
+
756
+ /** `hkdfExpandLabelSha256Held` with HMAC-SHA-384: panics as `hkdfExpandLabelSha384` does. */
757
+ export const hkdfExpandLabelSha384Held = (
758
+ dst: Secret<u8[]>,
759
+ prk: Secret<u8[]>,
760
+ label: string,
761
+ context: u8[]
762
+ ): void => {
763
+ hkdfHeld("hkdfExpandLabelSha384Held", SHA384_SIZE, dst, prk, label, context)
764
+ }
765
+
766
+ /**
767
+ * HKDF-Expand-Label of the held `prk` over the hash `hashLength` names into a
768
+ * plain array: `length` bytes written at `out[at]`. This is for output that is
769
+ * kept plain by design, a record layer's AEAD key and IV, say, which live in
770
+ * the arrays the cipher reads. Panics as `hkdfExpandLabelInto` does.
771
+ */
772
+ export const hkdfExpandLabelHeldInto = (
773
+ hashLength: i32,
774
+ prk: Secret<u8[]>,
775
+ label: string,
776
+ context: u8[],
777
+ out: u8[],
778
+ at: i32,
779
+ length: i32
780
+ ): void => {
781
+ hkdfCheckOutput("hkdfExpandLabelHeldInto", out, at, length)
782
+ const okm: u8[] = hkdfHeldDerive("hkdfExpandLabelHeldInto", hashLength, prk, label, context, length)
783
+ for (let k: i32 = 0; k < length && k < toI32(okm.length); k++) {
784
+ out[at + k] = okm[k]
785
+ }
786
+ wipe(okm)
495
787
  }