@amritk/nish-x86_64-linux 0.18.0 → 0.21.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 (104) hide show
  1. package/INSTALL.md +58 -28
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +2759 -0
  4. package/package.json +3 -2
  5. package/runtime/browser.d.ts +14 -0
  6. package/runtime/browser.mjs +88 -0
  7. package/runtime/core/arrays.d.ts +18 -0
  8. package/runtime/core/arrays.mjs +125 -0
  9. package/runtime/core/checks.mjs +20 -0
  10. package/runtime/core/host.d.ts +70 -0
  11. package/runtime/core/host.mjs +311 -0
  12. package/runtime/core/numbers.d.ts +53 -0
  13. package/runtime/core/numbers.mjs +316 -0
  14. package/runtime/core/result.d.ts +41 -0
  15. package/runtime/core/result.mjs +127 -0
  16. package/runtime/core/strings.d.ts +14 -0
  17. package/runtime/core/strings.mjs +169 -0
  18. package/runtime/core.d.ts +9 -0
  19. package/runtime/core.mjs +55 -0
  20. package/runtime/host.d.ts +6 -0
  21. package/runtime/nish.d.ts +99 -5
  22. package/runtime/nish.h +289 -19
  23. package/runtime/nish.mjs +39 -3
  24. package/runtime/node.mjs +383 -0
  25. package/runtime/runtime-aes.c +490 -0
  26. package/runtime/runtime-bytes.c +401 -0
  27. package/runtime/runtime-cross.c +366 -0
  28. package/runtime/runtime-host.c +8 -7
  29. package/runtime/runtime-net.c +93 -34
  30. package/runtime/runtime-os.c +214 -16
  31. package/runtime/runtime-parallel.c +590 -107
  32. package/runtime/runtime-simd.c +235 -11
  33. package/runtime/runtime-term.c +504 -0
  34. package/runtime/runtime-wasm.c +5 -14
  35. package/runtime/runtime.c +257 -17
  36. package/runtime/shim.mjs +6 -915
  37. package/runtime/sqlite/LICENSE.md +85 -0
  38. package/runtime/sqlite/README.md +45 -0
  39. package/runtime/sqlite/nish-sqlite.c +359 -0
  40. package/runtime/sqlite/sqlite3.c +271131 -0
  41. package/runtime/sqlite/sqlite3.h +14447 -0
  42. package/scripts/build.sh +437 -28
  43. package/std/LICENSE-fast-float +23 -0
  44. package/std/LICENSE-fdlibm +8 -0
  45. package/std/README.md +108 -58
  46. package/std/base64.ts +116 -0
  47. package/std/bytes.ts +419 -0
  48. package/std/cli.ts +328 -0
  49. package/std/collections.ts +6 -5
  50. package/std/crypto/LICENSE-crypt_blowfish +26 -0
  51. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  52. package/std/crypto/aes.ts +238 -76
  53. package/std/crypto/base64url.ts +125 -0
  54. package/std/crypto/bcrypt.ts +760 -0
  55. package/std/crypto/chacha20poly1305.ts +43 -9
  56. package/std/crypto/ed25519.ts +840 -0
  57. package/std/crypto/hkdf.ts +304 -12
  58. package/std/crypto/hmac.ts +382 -54
  59. package/std/crypto/p256.ts +932 -61
  60. package/std/crypto/x25519.ts +55 -37
  61. package/std/crypto/x509-random.ts +71 -0
  62. package/std/crypto/x509.ts +20 -131
  63. package/std/json-tokenizer.ts +4237 -0
  64. package/std/json-typed.ts +1014 -0
  65. package/std/json-value.ts +1182 -0
  66. package/std/json.ts +1202 -34
  67. package/std/math.ts +608 -0
  68. package/std/net/buffer-pool.ts +151 -0
  69. package/std/net/hpack.ts +416 -112
  70. package/std/net/http-fields.ts +93 -0
  71. package/std/net/http1-server.ts +501 -62
  72. package/std/net/http1.ts +31 -6
  73. package/std/net/http2-tls.ts +112 -24
  74. package/std/net/http2.ts +333 -75
  75. package/std/net/http3-server.ts +261 -43
  76. package/std/net/http3.ts +187 -28
  77. package/std/net/postgres.ts +1875 -0
  78. package/std/net/prefork.ts +412 -0
  79. package/std/net/quic-listener.ts +108 -11
  80. package/std/net/quic-packet.ts +243 -82
  81. package/std/net/quic-recovery.ts +361 -30
  82. package/std/net/quic-stream.ts +424 -24
  83. package/std/net/quic.ts +519 -167
  84. package/std/net/router.ts +676 -0
  85. package/std/net/tls/codec.ts +426 -27
  86. package/std/net/tls/record-server.ts +478 -132
  87. package/std/net/tls/record.ts +133 -42
  88. package/std/net/tls/schedule.ts +464 -55
  89. package/std/net/tls/ticket.ts +610 -0
  90. package/std/net/tls-tcp.ts +196 -53
  91. package/std/net/tls.ts +1018 -260
  92. package/std/net/url.ts +571 -0
  93. package/std/random.ts +94 -0
  94. package/std/regex.ts +4274 -0
  95. package/std/sqlite.ts +391 -0
  96. package/std/term-io.ts +978 -0
  97. package/std/term-pixels.ts +874 -0
  98. package/std/term-probe.ts +459 -0
  99. package/std/term-rate.ts +993 -0
  100. package/std/term.ts +2453 -0
  101. package/std/text.ts +90 -7
  102. package/std/threads.ts +438 -14
  103. package/std/yaml-lines.ts +141 -0
  104. package/std/yaml.ts +4675 -0
@@ -1,5 +1,6 @@
1
1
  /**
2
- * `nish/crypto/hmac` — HMAC as RFC 2104 defines it, over SHA-256 and SHA-384.
2
+ * `nish/crypto/hmac` — HMAC as RFC 2104 defines it, over SHA-256, SHA-384 and
3
+ * SHA-512.
3
4
  *
4
5
  * H(K XOR opad, H(K XOR ipad, text))
5
6
  *
@@ -24,17 +25,38 @@
24
25
  * which HMAC does not keep secret; a received tag is compared with
25
26
  * `timingSafeEqual`, never `===`.
26
27
  *
28
+ * **What the key reaches is wiped** (CLAUDE.md, "Security"). The key block
29
+ * is wiped once both hashers have absorbed it, and so is the hash
30
+ * of a key longer than a block and the hasher that made it. `digest` wipes
31
+ * the inner digest and both keyed hashers (chaining value, pending block and
32
+ * message schedule) before it answers, so a finished `HmacSha256` holds
33
+ * nothing of the key. The wipes are `wipe`'s volatile stores, which `-O2`
34
+ * keeps (`tests/wipes/crypto.js`, "crypto_hmac crypto_hkdf: the wipes survive -O2").
35
+ * What no wipe here reaches is the caller's own key array, and an
36
+ * `HmacSha256` that is keyed and then dropped without a `digest` keeps its
37
+ * keyed hashers (docs/security/crypto-k1.md, K1-7).
38
+ *
39
+ * A caller that holds its key as a `Secret<u8[]>` (`nish:secret`) MACs with
40
+ * `hmacSha256Secret`, `hmacSha384Secret` or `hmacSha512Secret`, which read the
41
+ * key only inside `exposeWith`; the tag they answer is public and plain.
42
+ *
27
43
  * `HmacSha256Scratch` and `HmacSha384Scratch` are the same computation in
28
44
  * hashers the caller allocates once: `begin` keys them again in place and
29
45
  * `finishInto` writes the tag into the caller's array, so a loop of them
30
46
  * stores nothing of its own and an arena scope around it releases all it
31
47
  * allocated (see "Caller-owned scratch" below).
32
48
  *
33
- * Written from RFC 2104, not ported from another implementation.
49
+ * `pbkdf2HmacSha256` is PBKDF2 with HMAC-SHA-256 (RFC 8018 §5.2), the key
50
+ * derivation SCRAM-SHA-256 runs over a password: the password goes in as a
51
+ * `Secret<u8[]>` and the derived key comes out as one, and every U_j and T_i
52
+ * is wiped before it answers (see "PBKDF2" below).
53
+ *
54
+ * Written from RFC 2104 and RFC 8018, not ported from another implementation.
34
55
  */
35
- import { SHA256_BLOCK, SHA256_SIZE, Sha256, sha256 } from "nish/crypto/sha256"
36
- import { SHA384_SIZE, SHA512_BLOCK, Sha384, sha384 } from "nish/crypto/sha512"
56
+ import { SHA256_BLOCK, SHA256_SIZE, Sha256 } from "nish/crypto/sha256"
57
+ import { SHA384_SIZE, SHA512_BLOCK, SHA512_SIZE, Sha384, Sha512 } from "nish/crypto/sha512"
37
58
  import { timingSafeEqual } from "nish/crypto/ct"
59
+ import { Secret, exposeWith, secret, wipe } from "nish:secret"
38
60
 
39
61
  /** RFC 2104 §2's `ipad`, the byte XORed into the key for the inner hash. */
40
62
  const HMAC_IPAD: i32 = 0x36
@@ -76,6 +98,141 @@ const hmacPadBlock = (key: u8[], blockSize: i32, pad: i32): u8[] => {
76
98
  return out
77
99
  }
78
100
 
101
+ /**
102
+ * Zeroes everything `h` holds of the key and the message it was fed: its hash
103
+ * value, its pending block and its message schedule, with `wipe`'s volatile
104
+ * stores, which no optimiser removes however dead the hasher is about to be.
105
+ * The hasher is left finished, so that feeding it before it is started again
106
+ * panics, as feeding a spent one always has.
107
+ */
108
+ const hmacWipeSha256 = (h: Sha256): void => {
109
+ wipe(h.state)
110
+ wipe(h.block)
111
+ wipe(h.schedule)
112
+ h.fill = 0
113
+ h.total = 0
114
+ h.finished = true
115
+ }
116
+
117
+ /** `hmacWipeSha256` for SHA-384. */
118
+ const hmacWipeSha384 = (h: Sha384): void => {
119
+ wipe(h.engine.state)
120
+ wipe(h.engine.block)
121
+ wipe(h.engine.schedule)
122
+ h.engine.filled = 0
123
+ h.engine.count = toU64(0)
124
+ h.engine.finished = true
125
+ }
126
+
127
+ /** `hmacWipeSha256` for SHA-512. */
128
+ const hmacWipeSha512 = (h: Sha512): void => {
129
+ wipe(h.engine.state)
130
+ wipe(h.engine.block)
131
+ wipe(h.engine.schedule)
132
+ h.engine.filled = 0
133
+ h.engine.count = toU64(0)
134
+ h.engine.finished = true
135
+ }
136
+
137
+ /**
138
+ * Panics on a key longer than 2^31 - 1 bytes, before any `toI32` of its
139
+ * length: under `--number-mode f64` that would saturate, and the key hashed
140
+ * would be a prefix, so two keys sharing it would MAC alike (K1-1).
141
+ */
142
+ const hmacCheckKey = (key: u8[]): void => {
143
+ if (key.length > 2147483647) {
144
+ panic("hmac: a key longer than 2^31 - 1 bytes")
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Keys `inner` with `k XOR ipad` and `outer` with `k XOR opad`, `k` no longer
150
+ * than SHA-256's block, in one block that is rewritten between the two and
151
+ * wiped once both have absorbed it.
152
+ */
153
+ const hmacPadsSha256 = (inner: Sha256, outer: Sha256, k: u8[]): void => {
154
+ const pad: u8[] = hmacPadBlock(k, SHA256_BLOCK, HMAC_IPAD)
155
+ inner.update(pad, HMAC_FROM, SHA256_BLOCK)
156
+ hmacPadInto(pad, k, HMAC_FROM, toI32(k.length), HMAC_OPAD)
157
+ outer.update(pad, HMAC_FROM, SHA256_BLOCK)
158
+ wipe(pad)
159
+ }
160
+
161
+ /**
162
+ * Keys `inner` and `outer` with `key` as RFC 2104 §2 says: a key longer than
163
+ * the block is replaced by its hash, in a hasher of its own, and that hash
164
+ * and that hasher are wiped once the key blocks are absorbed.
165
+ */
166
+ const hmacKeySha256 = (inner: Sha256, outer: Sha256, key: u8[]): void => {
167
+ hmacCheckKey(key)
168
+ if (toI32(key.length) <= SHA256_BLOCK) {
169
+ hmacPadsSha256(inner, outer, key)
170
+ return
171
+ }
172
+ const hasher = new Sha256()
173
+ hasher.update(key, HMAC_FROM, toI32(key.length))
174
+ const hashed: u8[] = hasher.digest()
175
+ hmacWipeSha256(hasher)
176
+ hmacPadsSha256(inner, outer, hashed)
177
+ wipe(hashed)
178
+ }
179
+
180
+ /** `hmacPadsSha256` for SHA-384's 128-byte block. */
181
+ const hmacPadsSha384 = (inner: Sha384, outer: Sha384, k: u8[]): void => {
182
+ const pad: u8[] = hmacPadBlock(k, SHA512_BLOCK, HMAC_IPAD)
183
+ inner.update(pad, HMAC_FROM, SHA512_BLOCK)
184
+ hmacPadInto(pad, k, HMAC_FROM, toI32(k.length), HMAC_OPAD)
185
+ outer.update(pad, HMAC_FROM, SHA512_BLOCK)
186
+ wipe(pad)
187
+ }
188
+
189
+ /**
190
+ * Keys `inner` and `outer` with `key` as RFC 2104 §2 says: a key longer than
191
+ * the block is replaced by its hash, in a hasher of its own, and that hash
192
+ * and that hasher are wiped once the key blocks are absorbed.
193
+ */
194
+ const hmacKeySha384 = (inner: Sha384, outer: Sha384, key: u8[]): void => {
195
+ hmacCheckKey(key)
196
+ if (toI32(key.length) <= SHA512_BLOCK) {
197
+ hmacPadsSha384(inner, outer, key)
198
+ return
199
+ }
200
+ const hasher = new Sha384()
201
+ hasher.update(key, HMAC_FROM, toI32(key.length))
202
+ const hashed: u8[] = hasher.digest()
203
+ hmacWipeSha384(hasher)
204
+ hmacPadsSha384(inner, outer, hashed)
205
+ wipe(hashed)
206
+ }
207
+
208
+ /** `hmacPadsSha256` for SHA-512's 128-byte block. */
209
+ const hmacPadsSha512 = (inner: Sha512, outer: Sha512, k: u8[]): void => {
210
+ const pad: u8[] = hmacPadBlock(k, SHA512_BLOCK, HMAC_IPAD)
211
+ inner.update(pad, HMAC_FROM, SHA512_BLOCK)
212
+ hmacPadInto(pad, k, HMAC_FROM, toI32(k.length), HMAC_OPAD)
213
+ outer.update(pad, HMAC_FROM, SHA512_BLOCK)
214
+ wipe(pad)
215
+ }
216
+
217
+ /**
218
+ * Keys `inner` and `outer` with `key` as RFC 2104 §2 says: a key longer than
219
+ * the block is replaced by its hash, in a hasher of its own, and that hash
220
+ * and that hasher are wiped once the key blocks are absorbed.
221
+ */
222
+ const hmacKeySha512 = (inner: Sha512, outer: Sha512, key: u8[]): void => {
223
+ hmacCheckKey(key)
224
+ if (toI32(key.length) <= SHA512_BLOCK) {
225
+ hmacPadsSha512(inner, outer, key)
226
+ return
227
+ }
228
+ const hasher = new Sha512()
229
+ hasher.update(key, HMAC_FROM, toI32(key.length))
230
+ const hashed: u8[] = hasher.digest()
231
+ hmacWipeSha512(hasher)
232
+ hmacPadsSha512(inner, outer, hashed)
233
+ wipe(hashed)
234
+ }
235
+
79
236
  /**
80
237
  * HMAC-SHA-256 in progress (RFC 2104 with RFC 4231's SHA-256): feed the
81
238
  * message with `update`, as many windows as it takes, and take the 32-byte
@@ -87,13 +244,14 @@ export class HmacSha256 {
87
244
  /** H(K XOR opad, …), fed the key block in the constructor and the inner digest last. */
88
245
  outer: Sha256
89
246
 
90
- /** Keys the computation. A key longer than 64 bytes is replaced by its SHA-256 (RFC 2104 §2). */
247
+ /**
248
+ * Keys the computation. A key longer than 64 bytes is replaced by its
249
+ * SHA-256 (RFC 2104 §2), which is wiped once used (`hmacKeySha256`).
250
+ */
91
251
  constructor(key: u8[]) {
92
- const k: u8[] = toI32(key.length) > SHA256_BLOCK ? sha256(key) : key
93
252
  this.inner = new Sha256()
94
- this.inner.update(hmacPadBlock(k, SHA256_BLOCK, HMAC_IPAD), HMAC_FROM, SHA256_BLOCK)
95
253
  this.outer = new Sha256()
96
- this.outer.update(hmacPadBlock(k, SHA256_BLOCK, HMAC_OPAD), HMAC_FROM, SHA256_BLOCK)
254
+ hmacKeySha256(this.inner, this.outer, key)
97
255
  }
98
256
 
99
257
  /** Absorbs `data[off .. off + len)`; a window outside `data` panics in `Sha256.update`. */
@@ -101,11 +259,15 @@ export class HmacSha256 {
101
259
  this.inner.update(data, off, len)
102
260
  }
103
261
 
104
- /** The 32-byte tag, in a fresh array. Ends the computation. */
262
+ /** The 32-byte tag, in a fresh array. Ends the computation, and wipes both hashers. */
105
263
  digest(): u8[] {
106
264
  const innerDigest: u8[] = this.inner.digest()
107
265
  this.outer.update(innerDigest, HMAC_FROM, SHA256_SIZE)
108
- return this.outer.digest()
266
+ wipe(innerDigest)
267
+ const tag: u8[] = this.outer.digest()
268
+ hmacWipeSha256(this.inner)
269
+ hmacWipeSha256(this.outer)
270
+ return tag
109
271
  }
110
272
  }
111
273
 
@@ -121,11 +283,9 @@ export class HmacSha384 {
121
283
 
122
284
  /** Keys the computation. A key longer than 128 bytes is replaced by its SHA-384 (RFC 2104 §2). */
123
285
  constructor(key: u8[]) {
124
- const k: u8[] = toI32(key.length) > SHA512_BLOCK ? sha384(key) : key
125
286
  this.inner = new Sha384()
126
- this.inner.update(hmacPadBlock(k, SHA512_BLOCK, HMAC_IPAD), HMAC_FROM, SHA512_BLOCK)
127
287
  this.outer = new Sha384()
128
- this.outer.update(hmacPadBlock(k, SHA512_BLOCK, HMAC_OPAD), HMAC_FROM, SHA512_BLOCK)
288
+ hmacKeySha384(this.inner, this.outer, key)
129
289
  }
130
290
 
131
291
  /** Absorbs `data[off .. off + len)`; a window outside `data` panics in `Sha384.update`. */
@@ -133,11 +293,49 @@ export class HmacSha384 {
133
293
  this.inner.update(data, off, len)
134
294
  }
135
295
 
136
- /** The 48-byte tag, in a fresh array. Ends the computation. */
296
+ /** The 48-byte tag, in a fresh array. Ends the computation, and wipes both hashers. */
137
297
  digest(): u8[] {
138
298
  const innerDigest: u8[] = this.inner.digest()
139
299
  this.outer.update(innerDigest, HMAC_FROM, SHA384_SIZE)
140
- return this.outer.digest()
300
+ wipe(innerDigest)
301
+ const tag: u8[] = this.outer.digest()
302
+ hmacWipeSha384(this.inner)
303
+ hmacWipeSha384(this.outer)
304
+ return tag
305
+ }
306
+ }
307
+
308
+ /**
309
+ * HMAC-SHA-512 in progress (RFC 2104 with RFC 4231's SHA-512), with
310
+ * `HmacSha256`'s methods. The block is SHA-512's 128 bytes and the tag 64.
311
+ */
312
+ export class HmacSha512 {
313
+ /** H(K XOR ipad, …), fed the key block in the constructor and the message after. */
314
+ inner: Sha512
315
+ /** H(K XOR opad, …), fed the key block in the constructor and the inner digest last. */
316
+ outer: Sha512
317
+
318
+ /** Keys the computation. A key longer than 128 bytes is replaced by its SHA-512 (RFC 2104 §2). */
319
+ constructor(key: u8[]) {
320
+ this.inner = new Sha512()
321
+ this.outer = new Sha512()
322
+ hmacKeySha512(this.inner, this.outer, key)
323
+ }
324
+
325
+ /** Absorbs `data[off .. off + len)`; a window outside `data` panics in `Sha512.update`. */
326
+ update(data: u8[], off: i32, len: i32): void {
327
+ this.inner.update(data, off, len)
328
+ }
329
+
330
+ /** The 64-byte tag, in a fresh array. Ends the computation, and wipes both hashers. */
331
+ digest(): u8[] {
332
+ const innerDigest: u8[] = this.inner.digest()
333
+ this.outer.update(innerDigest, HMAC_FROM, SHA512_SIZE)
334
+ wipe(innerDigest)
335
+ const tag: u8[] = this.outer.digest()
336
+ hmacWipeSha512(this.inner)
337
+ hmacWipeSha512(this.outer)
338
+ return tag
141
339
  }
142
340
  }
143
341
 
@@ -147,7 +345,7 @@ export class HmacSha384 {
147
345
  * `data` longer than 2^31 - 1 bytes panics, as `sha256` does and for its
148
346
  * reason: under `--number-mode f64` the tag would be of a prefix, and a tag
149
347
  * that verifies every message sharing that prefix is a forgery. A key that
150
- * long reaches `sha256` in the constructor and panics there.
348
+ * long panics in the constructor for the same reason.
151
349
  */
152
350
  export const hmacSha256 = (key: u8[], data: u8[]): u8[] => {
153
351
  if (data.length > 2147483647) {
@@ -171,6 +369,31 @@ export const hmacSha384 = (key: u8[], data: u8[]): u8[] => {
171
369
  return mac.digest()
172
370
  }
173
371
 
372
+ /**
373
+ * The HMAC-SHA-512 of all of `data` under `key`, as a fresh 64-byte array.
374
+ * `data` longer than 2^31 - 1 bytes panics, as in `hmacSha256`.
375
+ */
376
+ export const hmacSha512 = (key: u8[], data: u8[]): u8[] => {
377
+ if (data.length > 2147483647) {
378
+ panic("hmacSha512: a message longer than 2^31 - 1 bytes")
379
+ }
380
+ const mac = new HmacSha512(key)
381
+ mac.update(data, HMAC_FROM, toI32(data.length))
382
+ return mac.digest()
383
+ }
384
+
385
+ /**
386
+ * `hmacSha256` under a key held as a `Secret`, read only inside `exposeWith`.
387
+ * The tag is public, so it is answered plain; the caller still wipes the key.
388
+ */
389
+ export const hmacSha256Secret = (key: Secret<u8[]>, data: u8[]): u8[] => exposeWith(key, data, hmacSha256)
390
+
391
+ /** `hmacSha384` under a key held as a `Secret`, as `hmacSha256Secret` is. */
392
+ export const hmacSha384Secret = (key: Secret<u8[]>, data: u8[]): u8[] => exposeWith(key, data, hmacSha384)
393
+
394
+ /** `hmacSha512` under a key held as a `Secret`, as `hmacSha256Secret` is. */
395
+ export const hmacSha512Secret = (key: Secret<u8[]>, data: u8[]): u8[] => exposeWith(key, data, hmacSha512)
396
+
174
397
  /**
175
398
  * Whether `tag` is the HMAC-SHA-256 of `data` under `key`, compared by
176
399
  * `timingSafeEqual`. A tag that is not 32 bytes answers `false` without a
@@ -184,6 +407,10 @@ export const hmacSha256Verify = (key: u8[], data: u8[], tag: u8[]): boolean =>
184
407
  export const hmacSha384Verify = (key: u8[], data: u8[], tag: u8[]): boolean =>
185
408
  timingSafeEqual(hmacSha384(key, data), tag)
186
409
 
410
+ /** Whether `tag` is the HMAC-SHA-512 of `data` under `key`, compared as `hmacSha256Verify` does. */
411
+ export const hmacSha512Verify = (key: u8[], data: u8[], tag: u8[]): boolean =>
412
+ timingSafeEqual(hmacSha512(key, data), tag)
413
+
187
414
  // ---- Caller-owned scratch --------------------------------------------------
188
415
  //
189
416
  // `HmacSha256` makes its two hashers in its constructor, and the one-shot
@@ -241,44 +468,6 @@ export const hmacCopySha384 = (from: Sha384, to: Sha384): void => {
241
468
  to.engine.finished = from.engine.finished
242
469
  }
243
470
 
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
471
  /** Whether `[off, off + len)` is a window inside an array of `length` elements. */
283
472
  const hmacWindowFits = (length: i32, off: i32, len: i32): boolean =>
284
473
  off >= 0 && len >= 0 && off <= length - len
@@ -346,6 +535,10 @@ export class HmacSha256Scratch {
346
535
  * stored to the arena analysis.
347
536
  */
348
537
  keyBlocks(key: u8[], off: i32, len: i32): void {
538
+ // Starting a hasher again copies only the live part of `fresh`, so the
539
+ // last computation's block and schedule are wiped first (K1-7).
540
+ hmacWipeSha256(this.inner)
541
+ hmacWipeSha256(this.outer)
349
542
  hmacPadInto(this.pad, key, off, len, HMAC_IPAD)
350
543
  hmacCopySha256(this.fresh, this.inner)
351
544
  this.inner.update(this.pad, HMAC_FROM, SHA256_BLOCK)
@@ -434,6 +627,10 @@ export class HmacSha384Scratch {
434
627
  * stored to the arena analysis.
435
628
  */
436
629
  keyBlocks(key: u8[], off: i32, len: i32): void {
630
+ // Starting a hasher again copies only the live part of `fresh`, so the
631
+ // last computation's block and schedule are wiped first (K1-7).
632
+ hmacWipeSha384(this.inner)
633
+ hmacWipeSha384(this.outer)
437
634
  hmacPadInto(this.pad, key, off, len, HMAC_IPAD)
438
635
  hmacCopySha384(this.fresh, this.inner)
439
636
  this.inner.update(this.pad, HMAC_FROM, SHA512_BLOCK)
@@ -469,3 +666,134 @@ export class HmacSha384Scratch {
469
666
  hmacWipeSha384(this.outer)
470
667
  }
471
668
  }
669
+
670
+ // ---- PBKDF2 ----------------------------------------------------------------
671
+ //
672
+ // PBKDF2 with HMAC-SHA-256 as its PRF (RFC 8018 §5.2), the key derivation
673
+ // SCRAM-SHA-256 (RFC 7677, RFC 5802 §3 "Hi") runs over a password. The
674
+ // password keys an inner and an outer hasher once; every U_j after that is
675
+ // two compressions in a working hasher copied from them, so a count of 4096
676
+ // costs 4096 HMACs and never keys one again.
677
+
678
+ /** What `pbkdf2HmacSha256` passes through `exposeWith`, which carries one argument. */
679
+ interface Pbkdf2Request {
680
+ /** The salt, public: SCRAM's server sends it. */
681
+ salt: u8[]
682
+ /** The iteration count `c`, at least 1. */
683
+ iterations: i32
684
+ /** The derived key's length `dkLen` in bytes, at least 1. */
685
+ length: i32
686
+ }
687
+
688
+ /** The length of INT(i), the big-endian block index after the salt; typed, as `HMAC_FROM` is. */
689
+ const PBKDF2_INDEX: i32 = 4
690
+
691
+ /**
692
+ * Ends the HMAC `work` holds, keyed as `keyedOuter` is on the outer side, and
693
+ * writes its 32-byte tag into `out`. Both digests `work` answers are wiped
694
+ * here, so they die with this call and its arena scope.
695
+ */
696
+ const pbkdf2Finish = (keyedOuter: Sha256, work: Sha256, out: u8[]): void => {
697
+ const innerDigest: u8[] = work.digest()
698
+ hmacCopySha256(keyedOuter, work)
699
+ work.update(innerDigest, HMAC_FROM, SHA256_SIZE)
700
+ wipe(innerDigest)
701
+ const tag: u8[] = work.digest()
702
+ for (let i: i32 = 0; i < SHA256_SIZE && i < toI32(tag.length) && i < toI32(out.length); i += 1) {
703
+ out[i] = tag[i]
704
+ }
705
+ wipe(tag)
706
+ }
707
+
708
+ /** `pbkdf2HmacSha256` on the exposed password. */
709
+ const pbkdf2HmacSha256Exposed = (password: u8[], request: Pbkdf2Request): u8[] | null => {
710
+ const salt: u8[] = request.salt
711
+ const iterations: i32 = request.iterations
712
+ const length: i32 = request.length
713
+ // Before any `toI32`, which would saturate under `--number-mode f64`.
714
+ if (password.length > 2147483647 || salt.length > 2147483647) {
715
+ return null
716
+ }
717
+ if (iterations < 1 || length < 1) {
718
+ return null
719
+ }
720
+ const keyedInner = new Sha256()
721
+ const keyedOuter = new Sha256()
722
+ hmacKeySha256(keyedInner, keyedOuter, password)
723
+ const work = new Sha256()
724
+ const u: u8[] = new Array<u8>(SHA256_SIZE)
725
+ const t: u8[] = new Array<u8>(SHA256_SIZE)
726
+ const index: u8[] = new Array<u8>(PBKDF2_INDEX)
727
+ const dk: u8[] = new Array<u8>(length)
728
+ // dkLen is an i32, so it never reaches RFC 8018's (2^32 - 1) * hLen bound,
729
+ // and the block index below never passes 2^26.
730
+ let block: i32 = 1
731
+ let done: i32 = 0
732
+ while (done < length) {
733
+ // U_1 = PRF(P, S || INT(i)), INT(i) four bytes, most significant first.
734
+ index[0] = toU8((block >> 24) & 0xff)
735
+ index[1] = toU8((block >> 16) & 0xff)
736
+ index[2] = toU8((block >> 8) & 0xff)
737
+ index[3] = toU8(block & 0xff)
738
+ hmacCopySha256(keyedInner, work)
739
+ work.update(salt, HMAC_FROM, toI32(salt.length))
740
+ work.update(index, HMAC_FROM, PBKDF2_INDEX)
741
+ pbkdf2Finish(keyedOuter, work, u)
742
+ for (let j: i32 = 0; j < SHA256_SIZE && j < toI32(t.length) && j < toI32(u.length); j += 1) {
743
+ t[j] = u[j]
744
+ }
745
+ // U_c = PRF(P, U_{c-1}); T_i = U_1 XOR ... XOR U_c.
746
+ for (let c: i32 = 1; c < iterations; c += 1) {
747
+ hmacCopySha256(keyedInner, work)
748
+ work.update(u, HMAC_FROM, SHA256_SIZE)
749
+ pbkdf2Finish(keyedOuter, work, u)
750
+ for (let j: i32 = 0; j < SHA256_SIZE && j < toI32(t.length) && j < toI32(u.length); j += 1) {
751
+ t[j] = t[j] ^ u[j]
752
+ }
753
+ }
754
+ const take: i32 = length - done < SHA256_SIZE ? length - done : SHA256_SIZE
755
+ for (let j: i32 = 0; j < take && j < toI32(t.length) && done + j < toI32(dk.length); j += 1) {
756
+ dk[done + j] = t[j]
757
+ }
758
+ done += take
759
+ block += 1
760
+ }
761
+ wipe(u)
762
+ wipe(t)
763
+ hmacWipeSha256(work)
764
+ hmacWipeSha256(keyedInner)
765
+ hmacWipeSha256(keyedOuter)
766
+ return dk
767
+ }
768
+
769
+ /**
770
+ * PBKDF2-HMAC-SHA256(P, S, c, dkLen) as RFC 8018 §5.2 defines it: the
771
+ * `dkLen`-byte key derived from the password `password` and the salt `salt`
772
+ * over `iterations` rounds, as a `Secret`.
773
+ *
774
+ * const salted: Secret<u8[]> | null = pbkdf2HmacSha256(password, salt, 4096, 32);
775
+ *
776
+ * Bad input answers `null` and never panics: an iteration count or a length
777
+ * below 1, or a password or salt longer than 2^31 - 1 bytes. The password is
778
+ * read only inside `exposeWith`, and every U_j, every T_i, both keyed hashers
779
+ * and the working one are wiped before the key is answered; the digests on
780
+ * the way are wiped in the call that made them. The time taken depends on
781
+ * `iterations`, `dkLen` and the password's length past a block, all of which
782
+ * RFC 8018 treats as public, and on nothing of the password's bytes. The time
783
+ * is linear in `iterations` and nothing here bounds it, so a caller that takes
784
+ * the count from an untrusted peer (a SCRAM server's `i=`) must cap it first:
785
+ * 2^31 - 1 rounds is about 2^32 compressions.
786
+ */
787
+ export const pbkdf2HmacSha256 = (
788
+ password: Secret<u8[]>,
789
+ salt: u8[],
790
+ iterations: i32,
791
+ length: i32
792
+ ): Secret<u8[]> | null => {
793
+ const request: Pbkdf2Request = { salt: salt, iterations: iterations, length: length }
794
+ const dk: u8[] | null = exposeWith(password, request, pbkdf2HmacSha256Exposed)
795
+ if (dk === null) {
796
+ return null
797
+ }
798
+ return secret(dk)
799
+ }