@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.
- package/INSTALL.md +58 -28
- package/bin/nish +0 -0
- package/docs/AI.md +2759 -0
- package/package.json +3 -2
- package/runtime/browser.d.ts +14 -0
- package/runtime/browser.mjs +88 -0
- package/runtime/core/arrays.d.ts +18 -0
- package/runtime/core/arrays.mjs +125 -0
- package/runtime/core/checks.mjs +20 -0
- package/runtime/core/host.d.ts +70 -0
- package/runtime/core/host.mjs +311 -0
- package/runtime/core/numbers.d.ts +53 -0
- package/runtime/core/numbers.mjs +316 -0
- package/runtime/core/result.d.ts +41 -0
- package/runtime/core/result.mjs +127 -0
- package/runtime/core/strings.d.ts +14 -0
- package/runtime/core/strings.mjs +169 -0
- package/runtime/core.d.ts +9 -0
- package/runtime/core.mjs +55 -0
- package/runtime/host.d.ts +6 -0
- package/runtime/nish.d.ts +99 -5
- package/runtime/nish.h +289 -19
- package/runtime/nish.mjs +39 -3
- package/runtime/node.mjs +383 -0
- package/runtime/runtime-aes.c +490 -0
- package/runtime/runtime-bytes.c +401 -0
- package/runtime/runtime-cross.c +366 -0
- package/runtime/runtime-host.c +8 -7
- package/runtime/runtime-net.c +93 -34
- package/runtime/runtime-os.c +214 -16
- package/runtime/runtime-parallel.c +590 -107
- package/runtime/runtime-simd.c +235 -11
- package/runtime/runtime-term.c +504 -0
- package/runtime/runtime-wasm.c +5 -14
- package/runtime/runtime.c +257 -17
- package/runtime/shim.mjs +6 -915
- package/runtime/sqlite/LICENSE.md +85 -0
- package/runtime/sqlite/README.md +45 -0
- package/runtime/sqlite/nish-sqlite.c +359 -0
- package/runtime/sqlite/sqlite3.c +271131 -0
- package/runtime/sqlite/sqlite3.h +14447 -0
- package/scripts/build.sh +437 -28
- package/std/LICENSE-fast-float +23 -0
- package/std/LICENSE-fdlibm +8 -0
- package/std/README.md +108 -58
- package/std/base64.ts +116 -0
- package/std/bytes.ts +419 -0
- package/std/cli.ts +328 -0
- package/std/collections.ts +6 -5
- package/std/crypto/LICENSE-crypt_blowfish +26 -0
- package/std/crypto/LICENSE-libsecp256k1 +19 -0
- package/std/crypto/aes.ts +238 -76
- package/std/crypto/base64url.ts +125 -0
- package/std/crypto/bcrypt.ts +760 -0
- package/std/crypto/chacha20poly1305.ts +43 -9
- package/std/crypto/ed25519.ts +840 -0
- package/std/crypto/hkdf.ts +304 -12
- package/std/crypto/hmac.ts +382 -54
- package/std/crypto/p256.ts +932 -61
- package/std/crypto/x25519.ts +55 -37
- package/std/crypto/x509-random.ts +71 -0
- package/std/crypto/x509.ts +20 -131
- package/std/json-tokenizer.ts +4237 -0
- package/std/json-typed.ts +1014 -0
- package/std/json-value.ts +1182 -0
- package/std/json.ts +1202 -34
- package/std/math.ts +608 -0
- package/std/net/buffer-pool.ts +151 -0
- package/std/net/hpack.ts +416 -112
- package/std/net/http-fields.ts +93 -0
- package/std/net/http1-server.ts +501 -62
- package/std/net/http1.ts +31 -6
- package/std/net/http2-tls.ts +112 -24
- package/std/net/http2.ts +333 -75
- package/std/net/http3-server.ts +261 -43
- package/std/net/http3.ts +187 -28
- package/std/net/postgres.ts +1875 -0
- package/std/net/prefork.ts +412 -0
- package/std/net/quic-listener.ts +108 -11
- package/std/net/quic-packet.ts +243 -82
- package/std/net/quic-recovery.ts +361 -30
- package/std/net/quic-stream.ts +424 -24
- package/std/net/quic.ts +519 -167
- package/std/net/router.ts +676 -0
- package/std/net/tls/codec.ts +426 -27
- package/std/net/tls/record-server.ts +478 -132
- package/std/net/tls/record.ts +133 -42
- package/std/net/tls/schedule.ts +464 -55
- package/std/net/tls/ticket.ts +610 -0
- package/std/net/tls-tcp.ts +196 -53
- package/std/net/tls.ts +1018 -260
- package/std/net/url.ts +571 -0
- package/std/random.ts +94 -0
- package/std/regex.ts +4274 -0
- package/std/sqlite.ts +391 -0
- package/std/term-io.ts +978 -0
- package/std/term-pixels.ts +874 -0
- package/std/term-probe.ts +459 -0
- package/std/term-rate.ts +993 -0
- package/std/term.ts +2453 -0
- package/std/text.ts +90 -7
- package/std/threads.ts +438 -14
- package/std/yaml-lines.ts +141 -0
- package/std/yaml.ts +4675 -0
package/std/crypto/hmac.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `nish/crypto/hmac` — HMAC as RFC 2104 defines it, over SHA-256
|
|
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
|
-
*
|
|
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
|
|
36
|
-
import { SHA384_SIZE, SHA512_BLOCK, Sha384,
|
|
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
|
-
/**
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
+
}
|