@amritk/nish-x86_64-linux 0.20.0 → 0.22.1

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 (74) hide show
  1. package/INSTALL.md +52 -10
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +2777 -0
  4. package/package.json +2 -1
  5. package/runtime/browser.d.ts +6 -0
  6. package/runtime/browser.mjs +77 -0
  7. package/runtime/core/arrays.d.ts +36 -0
  8. package/runtime/core/arrays.mjs +153 -0
  9. package/runtime/core/checks.mjs +20 -0
  10. package/runtime/core/host.d.ts +80 -0
  11. package/runtime/core/host.mjs +325 -0
  12. package/runtime/core/numbers.d.ts +66 -0
  13. package/runtime/core/numbers.mjs +343 -0
  14. package/runtime/core/result.d.ts +45 -0
  15. package/runtime/core/result.mjs +140 -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 +46 -2
  22. package/runtime/nish.h +75 -2
  23. package/runtime/nish.mjs +23 -3
  24. package/runtime/node.mjs +386 -0
  25. package/runtime/runtime-bytes.c +401 -0
  26. package/runtime/runtime-cross.c +1 -1
  27. package/runtime/runtime-host.c +1 -1
  28. package/runtime/runtime-net.c +12 -1
  29. package/runtime/runtime-os.c +1 -1
  30. package/runtime/runtime-parallel.c +1 -1
  31. package/runtime/runtime-simd.c +231 -7
  32. package/runtime/runtime-term.c +504 -0
  33. package/runtime/runtime-wasm.c +0 -11
  34. package/runtime/runtime.c +190 -6
  35. package/runtime/shim.mjs +6 -985
  36. package/runtime/sqlite/LICENSE.md +85 -0
  37. package/runtime/sqlite/README.md +45 -0
  38. package/runtime/sqlite/nish-sqlite.c +359 -0
  39. package/runtime/sqlite/sqlite3.c +271131 -0
  40. package/runtime/sqlite/sqlite3.h +14447 -0
  41. package/scripts/build.sh +311 -59
  42. package/std/LICENSE-fast-float +23 -0
  43. package/std/LICENSE-fdlibm +8 -0
  44. package/std/README.md +32 -12
  45. package/std/base64.ts +116 -0
  46. package/std/bytes.ts +256 -0
  47. package/std/cli.ts +344 -0
  48. package/std/collections.ts +6 -5
  49. package/std/crypto/LICENSE-crypt_blowfish +26 -0
  50. package/std/crypto/base64url.ts +125 -0
  51. package/std/crypto/bcrypt.ts +1557 -0
  52. package/std/crypto/hmac.ts +138 -2
  53. package/std/crypto/x509.ts +11 -122
  54. package/std/json-tokenizer.ts +2998 -57
  55. package/std/json-value.ts +22 -3
  56. package/std/json.ts +87 -8
  57. package/std/math.ts +608 -0
  58. package/std/net/http1-server.ts +282 -40
  59. package/std/net/http1.ts +40 -21
  60. package/std/net/http3-server.ts +50 -12
  61. package/std/net/postgres.ts +2294 -0
  62. package/std/net/prefork.ts +23 -3
  63. package/std/net/quic-listener.ts +20 -4
  64. package/std/random.ts +66 -4
  65. package/std/regex.ts +4274 -0
  66. package/std/sqlite.ts +391 -0
  67. package/std/term-io.ts +978 -0
  68. package/std/term-pixels.ts +874 -0
  69. package/std/term-probe.ts +459 -0
  70. package/std/term-rate.ts +993 -0
  71. package/std/term.ts +2453 -0
  72. package/std/text.ts +90 -7
  73. package/std/yaml-lines.ts +141 -0
  74. package/std/yaml.ts +4675 -0
@@ -0,0 +1,1557 @@
1
+ /**
2
+ * `nish/crypto/bcrypt` — bcrypt password hashing, `$2b$` out and `$2a$`,
3
+ * `$2b$`, `$2y$` in. It makes the hash libxcrypt's `crypt_r` and
4
+ * crypt_blowfish make, and verifies as they do but for `$2x$` and a digest
5
+ * character outside the alphabet. Where OpenBSD's `bcrypt.c` and Go's
6
+ * `x/crypto` answer otherwise is the interoperability table in
7
+ * docs/security/crypto-bcrypt.md.
8
+ *
9
+ * import { bcryptHash, bcryptVerify } from "nish/crypto/bcrypt"
10
+ * import { Secret, secret } from "nish:secret"
11
+ *
12
+ * const pw: Secret<u8[]> = secret(passwordBytes)
13
+ * const hash: Result<string, string> = bcryptHash(pw, randomBytes(16), 12)
14
+ * // "$2b$12$" + 22 salt characters + 31 digest characters
15
+ * const ok: Result<boolean, string> = bcryptVerify(pw, storedHash)
16
+ *
17
+ * **Four at once.** `bcryptHash4` and `bcryptVerify4` take up to four
18
+ * passwords (lane 0 always, a null lane skipped) and run their Blowfish
19
+ * encryptions interleaved in one loop, so one core does the work of four
20
+ * hashes in well under the time of four; each result is the single path's,
21
+ * byte for byte. `bcryptVerify4` groups lanes by the cost their hash names,
22
+ * and a malformed hash in any lane is an `Err` for the whole call, naming the
23
+ * lane, before anything is hashed.
24
+ *
25
+ * **The password is a `Secret<u8[]>`** (`nish:secret`). The key schedule and
26
+ * the 2^cost rounds run inside `exposeWith`; the Blowfish state (the P-array
27
+ * and the four S-boxes, 1042 words), the expanded key and the key-stream copy
28
+ * are wiped before it returns. `bcryptVerify` rebuilds the whole 60-character
29
+ * hash and compares it with `timingSafeEqual` inside the same `exposeWith`,
30
+ * then wipes the rebuilt hash and the given hash's bytes; the batch wipes its
31
+ * digests and, in `bcryptVerify4`, each lane's rebuilt and given hash. The
32
+ * hash strings `bcryptHash` and `bcryptHash4` return are public and are not
33
+ * wiped, nor are the bytes they are made from.
34
+ *
35
+ * **The key is the reference's.** The bytes up to the first NUL, then one NUL,
36
+ * repeated until 72 bytes are taken: a password longer than 72 bytes hashes as
37
+ * its first 72, and bytes after an embedded NUL are not read, as under
38
+ * `crypt_r`; docs/security/crypto-bcrypt.md says why a longer password is cut
39
+ * rather than refused. Bytes above 127 are unsigned, as `$2b$` and
40
+ * `$2y$` define them. A `$2a$` hash gets crypt_blowfish's countermeasure for
41
+ * the old sign-extension bug: when an 8-bit byte was sign-extended into a key
42
+ * word and yet the buggy and the correct key came out equal, one bit of the
43
+ * first P word is flipped, as crypt_blowfish flips it, so the `$2a$` digest of
44
+ * such a password (`\xff\xff\xa3`) is not its `$2y$` one. It takes an 8-bit
45
+ * byte after the first byte of a key word with only 0xff bytes before it, every
46
+ * such byte in all 18 words being so placed, which UTF-8 never holds. `$2x$` (the bug itself) is refused.
47
+ *
48
+ * **Errors are `Err`s.** A salt that is not 16 bytes, a cost outside 4–31, or
49
+ * a hash string that is not one (the wrong length, an unknown prefix, a cost
50
+ * that is not two digits in range, a character outside the alphabet) is an
51
+ * `Err` naming it. A well-formed hash that is not this password's is
52
+ * `Ok(false)`, and so is one whose last salt character has bits set the
53
+ * reference drops, since the hash the reference makes from it differs.
54
+ *
55
+ * **The cost comes from the hash.** `bcryptVerify` runs 2^cost rounds for the
56
+ * cost the hash names, so a `$2b$31$` hash someone hands you takes 2^31
57
+ * rounds, hours of one core. Verify only hashes you stored.
58
+ *
59
+ * **Pure.** No I/O and no C, so it builds for wasm32. A `std/threads` task
60
+ * may call `bcryptHash`, `bcryptVerify` and the batch: everything they wipe
61
+ * is an array they made, never the caller's `hashes`, and the batch's state is
62
+ * a `Secret` of a fresh array
63
+ * (docs/security/crypto-bcrypt.md). A cost-12 hash takes a few hundred
64
+ * milliseconds of one core; a server runs it off its event loop, on a task
65
+ * (docs/guide/using-nish/password-hashing.md).
66
+ *
67
+ * The algorithm is Provos and Mazières's ("A Future-Adaptable Password
68
+ * Scheme", USENIX 1999). The code is adapted from Solar Designer's
69
+ * crypt_blowfish 1.3, which carries the notice below, and the 1042 initial
70
+ * words, the hexadecimal digits of π, are its `BF_init_state`. Closest
71
+ * upstream for each part: `bcryptInitState` is `BF_init_state`;
72
+ * `bcryptEncrypt` and the rounds in `bcryptBody` are `BF_ENCRYPT`/`BF_ROUND`;
73
+ * `bcryptBody` is `BF_body`; `bcryptSetKey` is `BF_set_key`; `bcryptDecodeSalt`
74
+ * and `bcryptEncode` are `BF_decode` and `BF_encode`; `bcryptExposed` is the
75
+ * part of `BF_crypt` after the setting is parsed, and `bcryptParse` that
76
+ * parse. The state is one flat `u32[]` with P first (words 0–17) and the
77
+ * S-boxes after (18–1041), so the body walks it in one pass, and every S-box
78
+ * index is an `i32` masked to 0–255, which the checker proves in bounds
79
+ * (`BCRYPT_STATE_WORDS`). The magic-string encryption, the parsing and the
80
+ * alphabet are separate functions so a several-lane kernel can share them.
81
+ *
82
+ * Names carry the `bcrypt` prefix because a `std/` module's private functions
83
+ * share the importing program's flat symbol namespace
84
+ * (`docs/wp26-stdlib.md` §3e).
85
+ *
86
+ * Adapted from crypt_blowfish (https://www.openwall.com/crypt/), crypt_blowfish.c.
87
+ * Written by Solar Designer, 1998-2014, and placed in the public domain, with a
88
+ * permissive licence as the fallback; see std/crypto/LICENSE-crypt_blowfish.
89
+ *
90
+ * crypt_blowfish's notice, kept as it asks:
91
+ *
92
+ * Written by Solar Designer <solar at openwall.com> in 1998-2014.
93
+ * No copyright is claimed, and the software is hereby placed in the public
94
+ * domain. In case this attempt to disclaim copyright and place the software
95
+ * in the public domain is deemed null and void, then the software is
96
+ * Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
97
+ * general public under the following terms:
98
+ *
99
+ * Redistribution and use in source and binary forms, with or without
100
+ * modification, are permitted.
101
+ *
102
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
103
+ *
104
+ * It is my intent that you should be able to use this on your system,
105
+ * as part of a software package, or anywhere else to improve security,
106
+ * ensure compatibility, or for any other purpose. I would appreciate
107
+ * it if you give credit where it is due and keep your modifications in
108
+ * the public domain as well, but I don't require that in order to let
109
+ * you place this code and any modifications you make under a license
110
+ * of your choice.
111
+ *
112
+ * This implementation is fully compatible with OpenBSD's bcrypt.c for prefix
113
+ * "$2b$", originally by Niels Provos <provos at citi.umich.edu>, and it uses
114
+ * some of his ideas. The password hashing algorithm was designed by David
115
+ * Mazieres <dm at lcs.mit.edu>.
116
+ */
117
+
118
+ import { timingSafeEqual } from "nish/crypto/ct"
119
+ import { Secret, exposeWith, secret, wipe } from "nish:secret"
120
+
121
+ /** The length of a bcrypt hash string: `$2b$`, two cost digits, `$`, 22 salt and 31 digest characters. */
122
+ export const BCRYPT_HASH_LENGTH: i32 = 60
123
+
124
+ /** The length in bytes of a bcrypt salt. */
125
+ export const BCRYPT_SALT_SIZE: i32 = 16
126
+
127
+ /** The smallest cost the reference accepts: 2^4 rounds. */
128
+ export const BCRYPT_MIN_COST: i32 = 4
129
+
130
+ /** The largest cost: 2^31 rounds. */
131
+ export const BCRYPT_MAX_COST: i32 = 31
132
+
133
+ /** The number of password bytes the key schedule reads; the rest are not. */
134
+ export const BCRYPT_MAX_PASSWORD: i32 = 72
135
+
136
+ /**
137
+ * The state's words: P (18) then S0–S3 (4 × 256). The guards before the
138
+ * rounds and the key copy write 1042, 72 (`BCRYPT_MAX_PASSWORD`) and 16 as
139
+ * literals: the range prover reads a literal bound, not a named constant, and
140
+ * keeps every S-box bounds check without it.
141
+ */
142
+ const BCRYPT_STATE_WORDS: i32 = 1042
143
+
144
+ /** The length of the setting, `$2b$NN$` and the 22 salt characters. */
145
+ const BCRYPT_SETTING_LENGTH: i32 = 29
146
+
147
+ /** What the hash needs besides the password, as one argument, since `exposeWith` hands on only one. */
148
+ interface BcryptSetting {
149
+ /** The salt as four big-endian words. */
150
+ salt: u32[]
151
+ /** log2 of the round count, 4 to 31. */
152
+ cost: i32
153
+ /** 0x10000 for `$2a$`, whose countermeasure it enables; 0 for `$2b$` and `$2y$`. */
154
+ safety: u32
155
+ }
156
+
157
+ /**
158
+ * The one argument of the callbacks `exposeWith` runs on the password: the
159
+ * setting, the 29 characters the hash starts with, and for verifying the hash
160
+ * to compare with ("" when hashing). All of it is only read.
161
+ */
162
+ interface BcryptJob {
163
+ setting: BcryptSetting
164
+ prefix: u8[]
165
+ hash: string
166
+ }
167
+
168
+ /** A parsed hash string: its setting and the 29 characters the rebuilt hash starts with. */
169
+ interface BcryptParsed {
170
+ setting: BcryptSetting
171
+ prefix: u8[]
172
+ }
173
+
174
+ /**
175
+ * All ones when `lo <= c <= hi`, else zero, without a branch: the encoding
176
+ * reads the digest, which is the stored hash when the password is right. The
177
+ * sums are in `u32`, as in `base64urlRangeMask` (std/crypto/base64url.ts), so
178
+ * no overflow check branches on them.
179
+ */
180
+ const bcryptRangeMask = (c: i32, lo: i32, hi: i32): i32 =>
181
+ toI32((toU32(lo) - 1 - toU32(c)) & (toU32(c) - toU32(hi) - 1)) >> 31
182
+
183
+ /**
184
+ * The bcrypt alphabet's character for the sextet `v`, `./A-Za-z0-9`: `.` + `v`,
185
+ * plus the step to each range `v` has reached (2 lands on `A`, 28 on `a`, 54
186
+ * on `0`).
187
+ */
188
+ const bcryptCharOf = (v: i32): u8 =>
189
+ toU8(
190
+ 46 +
191
+ toU32(v) +
192
+ toU32(bcryptRangeMask(v, 2, 63) & 17) +
193
+ toU32(bcryptRangeMask(v, 28, 63) & 6) -
194
+ toU32(bcryptRangeMask(v, 54, 63) & 75)
195
+ )
196
+
197
+ /** The sextet of a bcrypt alphabet character, or -1 for any other byte, without a branch. */
198
+ const bcryptSextetOf = (c: i32): i32 => {
199
+ const punct: i32 = bcryptRangeMask(c, 46, 47)
200
+ const upper: i32 = bcryptRangeMask(c, 65, 90)
201
+ const lower: i32 = bcryptRangeMask(c, 97, 122)
202
+ const digit: i32 = bcryptRangeMask(c, 48, 57)
203
+ const value: i32 =
204
+ (punct & toI32(toU32(c) - 46)) |
205
+ (upper & toI32(toU32(c) - 63)) |
206
+ (lower & toI32(toU32(c) - 69)) |
207
+ (digit & toI32(toU32(c) + 6))
208
+ return value | ~(punct | upper | lower | digit)
209
+ }
210
+
211
+ /**
212
+ * `BF_encode`: the first `n` bytes of `src` as bcrypt base64, three bytes to
213
+ * four characters, and a one- or two-byte tail to two or three.
214
+ */
215
+ const bcryptEncode = (src: u8[], n: i32): u8[] => {
216
+ const out: u8[] = new Array<u8>((n * 4 + 2) / 3)
217
+ bcryptEncodeInto(out, 0, src, n)
218
+ return out
219
+ }
220
+
221
+ /**
222
+ * `bcryptEncode` into `out` from `at`, as far as `out` reaches, so a digest's
223
+ * characters land in an array the caller made and wipes.
224
+ */
225
+ const bcryptEncodeInto = (out: u8[], at: i32, src: u8[], n: i32): void => {
226
+ let i: i32 = 0
227
+ let o: i32 = at
228
+ while (i + 2 < n && i + 2 < toI32(src.length) && o >= 0 && o + 3 < toI32(out.length)) {
229
+ const c1: i32 = toI32(src[i])
230
+ const c2: i32 = toI32(src[i + 1])
231
+ const c3: i32 = toI32(src[i + 2])
232
+ out[o] = bcryptCharOf(c1 >> 2)
233
+ out[o + 1] = bcryptCharOf(((c1 & 0x03) << 4) | (c2 >> 4))
234
+ out[o + 2] = bcryptCharOf(((c2 & 0x0f) << 2) | (c3 >> 6))
235
+ out[o + 3] = bcryptCharOf(c3 & 0x3f)
236
+ i += 3
237
+ o += 4
238
+ }
239
+ if (i >= 0 && i < n && i < toI32(src.length) && o >= 0 && o + 1 < toI32(out.length)) {
240
+ const c1: i32 = toI32(src[i])
241
+ out[o] = bcryptCharOf(c1 >> 2)
242
+ if (i + 1 < n && i + 1 < toI32(src.length) && o + 2 < toI32(out.length)) {
243
+ const c2: i32 = toI32(src[i + 1])
244
+ out[o + 1] = bcryptCharOf(((c1 & 0x03) << 4) | (c2 >> 4))
245
+ out[o + 2] = bcryptCharOf((c2 & 0x0f) << 2)
246
+ } else {
247
+ out[o + 1] = bcryptCharOf((c1 & 0x03) << 4)
248
+ }
249
+ }
250
+ }
251
+
252
+ /** `src` copied into `dst` from `at`, as far as `dst` reaches. */
253
+ const bcryptPut = (dst: u8[], at: i32, src: u8[]): void => {
254
+ for (let k: i32 = 0; k < toI32(src.length); k++) {
255
+ const o: i32 = at + k
256
+ if (o >= 0 && o < toI32(dst.length)) {
257
+ dst[o] = src[k]
258
+ }
259
+ }
260
+ }
261
+
262
+ /**
263
+ * `BF_decode` of the 22 salt characters of `text` from `at`: the 16 bytes as
264
+ * four big-endian words, or `null` if a character is outside the alphabet.
265
+ * The 22nd character gives its top two bits only.
266
+ */
267
+ const bcryptDecodeSalt = (text: string, at: i32): u32[] | null => {
268
+ const sx: i32[] = new Array<i32>(22)
269
+ for (let k: i32 = 0; k < toI32(sx.length); k++) {
270
+ const p: i32 = at + k
271
+ if (p < 0 || p >= toI32(text.length)) {
272
+ return null
273
+ }
274
+ const v: i32 = bcryptSextetOf(toI32(text.charCodeAt(p)))
275
+ if (v < 0) {
276
+ return null
277
+ }
278
+ sx[k] = v
279
+ }
280
+ const bytes: u8[] = new Array<u8>(BCRYPT_SALT_SIZE)
281
+ let s: i32 = 0
282
+ let d: i32 = 0
283
+ while (s + 3 < toI32(sx.length) && d + 2 < toI32(bytes.length)) {
284
+ const c2: i32 = sx[s + 1]
285
+ const c3: i32 = sx[s + 2]
286
+ bytes[d] = toU8((sx[s] << 2) | ((c2 & 0x30) >> 4))
287
+ bytes[d + 1] = toU8(((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2))
288
+ bytes[d + 2] = toU8(((c3 & 0x03) << 6) | sx[s + 3])
289
+ s += 4
290
+ d += 3
291
+ }
292
+ bytes[15] = toU8((sx[20] << 2) | ((sx[21] & 0x30) >> 4))
293
+ return bcryptSaltWords(bytes)
294
+ }
295
+
296
+ /** Sixteen salt bytes as four big-endian words (`BF_swap`). */
297
+ const bcryptSaltWords = (bytes: u8[]): u32[] => {
298
+ const words: u32[] = new Array<u32>(4)
299
+ let w: i32 = 0
300
+ for (let b: i32 = 0; b + 3 < toI32(bytes.length) && w < toI32(words.length); b += 4) {
301
+ words[w] =
302
+ (toU32(bytes[b]) << 24) | (toU32(bytes[b + 1]) << 16) | (toU32(bytes[b + 2]) << 8) | toU32(bytes[b + 3])
303
+ w++
304
+ }
305
+ return words
306
+ }
307
+
308
+ /** `$2b$NN$` and the 22 salt characters for a cost and 16 salt bytes. */
309
+ const bcryptSettingOf = (salt: u8[], cost: i32): u8[] => {
310
+ const out: u8[] = new Array<u8>(BCRYPT_SETTING_LENGTH)
311
+ out[0] = 36
312
+ out[1] = 50
313
+ out[2] = 98
314
+ out[3] = 36
315
+ out[4] = toU8(cost / 10 + 48)
316
+ out[5] = toU8((cost % 10) + 48)
317
+ out[6] = 36
318
+ bcryptPut(out, 7, bcryptEncode(salt, BCRYPT_SALT_SIZE))
319
+ return out
320
+ }
321
+
322
+ /**
323
+ * `BF_crypt`'s parse of a hash or setting: `$2a$`, `$2b$` or `$2y$`, two cost
324
+ * digits from 04 to 31, `$` and 22 salt characters. The prefix it answers has
325
+ * the last salt character canonical, its low four bits cleared, as the
326
+ * reference writes it.
327
+ */
328
+ const bcryptParse = (text: string): Result<BcryptParsed, string> => {
329
+ if (toI32(text.length) < BCRYPT_SETTING_LENGTH) {
330
+ return Err("bcrypt: hash too short")
331
+ }
332
+ const minor: i32 = toI32(text.charCodeAt(2))
333
+ if (
334
+ toI32(text.charCodeAt(0)) !== 36 ||
335
+ toI32(text.charCodeAt(1)) !== 50 ||
336
+ toI32(text.charCodeAt(3)) !== 36 ||
337
+ (minor !== 97 && minor !== 98 && minor !== 121)
338
+ ) {
339
+ return Err("bcrypt: not a $2a$, $2b$ or $2y$ hash")
340
+ }
341
+ const d1: i32 = toI32(text.charCodeAt(4)) - 48
342
+ const d2: i32 = toI32(text.charCodeAt(5)) - 48
343
+ if (d1 < 0 || d1 > 9 || d2 < 0 || d2 > 9 || toI32(text.charCodeAt(6)) !== 36) {
344
+ return Err("bcrypt: the cost is not two digits")
345
+ }
346
+ const cost: i32 = d1 * 10 + d2
347
+ if (cost < BCRYPT_MIN_COST || cost > BCRYPT_MAX_COST) {
348
+ return Err("bcrypt: the cost is outside 04 to 31")
349
+ }
350
+ const salt: u32[] | null = bcryptDecodeSalt(text, 7)
351
+ if (salt === null) {
352
+ return Err("bcrypt: the salt is not bcrypt base64")
353
+ }
354
+ const prefix: u8[] = new Array<u8>(BCRYPT_SETTING_LENGTH)
355
+ for (let i: i32 = 0; i + 1 < toI32(prefix.length) && i < toI32(text.length); i++) {
356
+ prefix[i] = toU8(text.charCodeAt(i))
357
+ }
358
+ prefix[28] = bcryptCharOf(bcryptSextetOf(toI32(text.charCodeAt(28))) & 0x30)
359
+ const safety: u32 = minor === 97 ? 0x10000 : 0
360
+ const setting: BcryptSetting = { salt: salt, cost: cost, safety: safety }
361
+ const parsed: BcryptParsed = { setting: setting, prefix: prefix }
362
+ return Ok(parsed)
363
+ }
364
+
365
+ /**
366
+ * One Blowfish block, `lr[0]` and `lr[1]` in place (`BF_ENCRYPT`), under the
367
+ * state `st`. The rounds are `bcryptBody`'s, which repeats them inline.
368
+ */
369
+ const bcryptEncrypt = (st: u32[], lr: u32[]): void => {
370
+ if (st.length < 1042) {
371
+ return
372
+ }
373
+ if (lr.length < 2) {
374
+ return
375
+ }
376
+ let l: u32 = lr[0] ^ st[0]
377
+ let r: u32 = lr[1]
378
+ for (let n: i32 = 1; n < 17; n += 2) {
379
+ const la: i32 = toI32(l >>> 24) & 0xff
380
+ const lb: i32 = toI32(l >>> 16) & 0xff
381
+ const lc: i32 = toI32(l >>> 8) & 0xff
382
+ const ld: i32 = toI32(l) & 0xff
383
+ r ^= st[n] ^ (((st[18 + la] + st[274 + lb]) ^ st[530 + lc]) + st[786 + ld])
384
+ const ra: i32 = toI32(r >>> 24) & 0xff
385
+ const rb: i32 = toI32(r >>> 16) & 0xff
386
+ const rc: i32 = toI32(r >>> 8) & 0xff
387
+ const rd: i32 = toI32(r) & 0xff
388
+ l ^= st[n + 1] ^ (((st[18 + ra] + st[274 + rb]) ^ st[530 + rc]) + st[786 + rd])
389
+ }
390
+ lr[0] = r ^ st[17]
391
+ lr[1] = l
392
+ }
393
+
394
+ /**
395
+ * `BF_body`: encrypt the zero block, then each result in turn, writing the
396
+ * pairs over the whole state, P first and then the S-boxes. The hot loop:
397
+ * 2^(cost+1) calls of 521 blocks each.
398
+ */
399
+ const bcryptBody = (st: u32[]): void => {
400
+ if (st.length < 1042) {
401
+ return
402
+ }
403
+ let l: u32 = 0
404
+ let r: u32 = 0
405
+ for (let i: i32 = 0; i + 1 < toI32(st.length); i += 2) {
406
+ l ^= st[0]
407
+ for (let n: i32 = 1; n < 17; n += 2) {
408
+ const la: i32 = toI32(l >>> 24) & 0xff
409
+ const lb: i32 = toI32(l >>> 16) & 0xff
410
+ const lc: i32 = toI32(l >>> 8) & 0xff
411
+ const ld: i32 = toI32(l) & 0xff
412
+ r ^= st[n] ^ (((st[18 + la] + st[274 + lb]) ^ st[530 + lc]) + st[786 + ld])
413
+ const ra: i32 = toI32(r >>> 24) & 0xff
414
+ const rb: i32 = toI32(r >>> 16) & 0xff
415
+ const rc: i32 = toI32(r >>> 8) & 0xff
416
+ const rd: i32 = toI32(r) & 0xff
417
+ l ^= st[n + 1] ^ (((st[18 + ra] + st[274 + rb]) ^ st[530 + rc]) + st[786 + rd])
418
+ }
419
+ const t: u32 = r
420
+ r = l
421
+ l = t ^ st[17]
422
+ st[i] = l
423
+ st[i + 1] = r
424
+ }
425
+ }
426
+
427
+ /**
428
+ * `BF_set_key`'s key: the 18 expanded-key words into `expanded[0..17]`, and
429
+ * the word P[0] is XORed with into `expanded[18]`, for `password` read as its
430
+ * bytes to the first NUL and one NUL, repeated. `safety` is 0x10000 for `$2a$`
431
+ * and 0 otherwise: for `$2a$` the key is also built with each byte
432
+ * sign-extended, as the old bug built it, and if an 8-bit byte was
433
+ * sign-extended into a word (`sign`) yet the two keys came out equal (`diff`
434
+ * is 0), `expanded[18]` is bit 16, which flips it in P[0] as crypt_blowfish
435
+ * flips it. All of it is computed without a branch on a password byte: only
436
+ * the copy's length and the cycle position, which follow the password's length
437
+ * and NULs, decide a load.
438
+ */
439
+ const bcryptKeySchedule = (expanded: u32[], password: u8[], safety: u32): void => {
440
+ if (expanded.length < 19) {
441
+ return
442
+ }
443
+ // The key stream: at most 72 bytes of the password and zeros after, so the
444
+ // cycle below reads a NUL at 72 at the latest without testing the length.
445
+ // It is 128 long so that the position, masked to 7 bits, needs no check.
446
+ const key: u8[] = new Array<u8>(128)
447
+ for (let i: i32 = 0; i < toI32(password.length) && i < 72; i++) {
448
+ key[i] = password[i]
449
+ }
450
+ let p: i32 = 0
451
+ let sign: u32 = 0
452
+ let diff: u32 = 0
453
+ for (let i: i32 = 0; i < 18; i++) {
454
+ let correct: u32 = 0
455
+ let buggy: u32 = 0
456
+ for (let j: i32 = 0; j < 4; j++) {
457
+ const q: i32 = p & 0x7f
458
+ const c: i32 = toI32(key[q])
459
+ correct = (correct << 8) | toU32(c)
460
+ buggy = (buggy << 8) | ((toU32(c) ^ 0x80) - 0x80)
461
+ if (j !== 0) {
462
+ sign |= buggy & 0x80
463
+ }
464
+ // The next position, or 0 after a NUL: 1 if c is nonzero, else 0.
465
+ p = (p + 1) * ((c | -c) >>> 31)
466
+ }
467
+ diff |= correct ^ buggy
468
+ expanded[i] = correct
469
+ }
470
+ diff |= diff >>> 16
471
+ diff &= 0xffff
472
+ diff += 0xffff
473
+ sign <<= 9
474
+ sign &= ~diff & safety
475
+ expanded[18] = sign
476
+ wipe(key)
477
+ }
478
+
479
+ /** `BF_set_key`: `bcryptKeySchedule` into `expanded`, and P XORed with it. */
480
+ const bcryptSetKey = (st: u32[], expanded: u32[], password: u8[], safety: u32): void => {
481
+ bcryptKeySchedule(expanded, password, safety)
482
+ if (st.length < 1042) {
483
+ return
484
+ }
485
+ if (expanded.length < 19) {
486
+ return
487
+ }
488
+ for (let i: i32 = 0; i < 18; i++) {
489
+ st[i] ^= expanded[i]
490
+ }
491
+ st[0] ^= expanded[18]
492
+ }
493
+
494
+ /**
495
+ * The 1042 words of `st` from `at` set to the state Blowfish starts from: P
496
+ * then S0–S3, the digits of π (`BF_init_state`). The caller allocates `st`,
497
+ * so that the wipe of it is a wipe of memory the hashing function made, which
498
+ * a `std/threads` task may do.
499
+ */
500
+ const bcryptInitState = (st: u32[], at: i32): void => {
501
+ const init: u32[] = [
502
+ // P
503
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
504
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
505
+ 0x9216d5d9, 0x8979fb1b,
506
+ // S0
507
+ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
508
+ 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
509
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
510
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
511
+ 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
512
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
513
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
514
+ 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
515
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
516
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
517
+ 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
518
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
519
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
520
+ 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
521
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
522
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
523
+ 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
524
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
525
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
526
+ 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
527
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
528
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
529
+ 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
530
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
531
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
532
+ 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
533
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
534
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
535
+ 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
536
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
537
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
538
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
539
+ // S1
540
+ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
541
+ 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
542
+ 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
543
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
544
+ 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
545
+ 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
546
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
547
+ 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
548
+ 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
549
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
550
+ 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
551
+ 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
552
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
553
+ 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
554
+ 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
555
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
556
+ 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
557
+ 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
558
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
559
+ 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
560
+ 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
561
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
562
+ 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
563
+ 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
564
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
565
+ 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
566
+ 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
567
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
568
+ 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
569
+ 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
570
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
571
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
572
+ // S2
573
+ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
574
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
575
+ 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
576
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
577
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
578
+ 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
579
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
580
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
581
+ 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
582
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
583
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
584
+ 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
585
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
586
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
587
+ 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
588
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
589
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
590
+ 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
591
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
592
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
593
+ 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
594
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
595
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
596
+ 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
597
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
598
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
599
+ 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
600
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
601
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
602
+ 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
603
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
604
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
605
+ // S3
606
+ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
607
+ 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
608
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
609
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
610
+ 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
611
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
612
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
613
+ 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
614
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
615
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
616
+ 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
617
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
618
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
619
+ 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
620
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
621
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
622
+ 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
623
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
624
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
625
+ 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
626
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
627
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
628
+ 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
629
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
630
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
631
+ 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
632
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
633
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
634
+ 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
635
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
636
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
637
+ 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
638
+ ]
639
+ if (at < 0) {
640
+ return
641
+ }
642
+ for (let i: i32 = 0; i < toI32(init.length) && at + i < toI32(st.length); i++) {
643
+ st[at + i] = init[i]
644
+ }
645
+ }
646
+
647
+ /** The block `lr` into `st` at `at` and `at + 1`. */
648
+ const bcryptStore = (st: u32[], at: i32, lr: u32[]): void => {
649
+ if (lr.length < 2) {
650
+ return
651
+ }
652
+ if (at >= 0 && at + 1 < toI32(st.length)) {
653
+ st[at] = lr[0]
654
+ st[at + 1] = lr[1]
655
+ }
656
+ }
657
+
658
+ /** P XORed with the expanded key: the first half of a cost round. */
659
+ const bcryptXorKey = (st: u32[], expanded: u32[]): void => {
660
+ if (st.length < 18) {
661
+ return
662
+ }
663
+ if (expanded.length < 18) {
664
+ return
665
+ }
666
+ for (let i: i32 = 0; i < 18; i++) {
667
+ st[i] ^= expanded[i]
668
+ }
669
+ }
670
+
671
+ /** The 18 P words at `at` XORed with the salt words, repeated: the second half of a cost round. */
672
+ const bcryptXorSalt = (st: u32[], at: i32, s0: u32, s1: u32, s2: u32, s3: u32): void => {
673
+ if (at < 0 || at + 17 >= toI32(st.length)) {
674
+ return
675
+ }
676
+ for (let i: i32 = 0; i + 3 < 16; i += 4) {
677
+ st[at + i] ^= s0
678
+ st[at + i + 1] ^= s1
679
+ st[at + i + 2] ^= s2
680
+ st[at + i + 3] ^= s3
681
+ }
682
+ st[at + 16] ^= s0
683
+ st[at + 17] ^= s1
684
+ }
685
+
686
+ /**
687
+ * The hash itself, on the plain password: the 31 digest characters (`BF_crypt`
688
+ * from the key schedule on) into `out` at 29, which the caller made and wipes.
689
+ * The state, the expanded key, the block and the raw digest are wiped before
690
+ * it returns.
691
+ */
692
+ const bcryptExposed = (password: u8[], setting: BcryptSetting, out: u8[]): void => {
693
+ const salt: u32[] = setting.salt
694
+ if (salt.length < 4) {
695
+ return
696
+ }
697
+ const s0: u32 = salt[0]
698
+ const s1: u32 = salt[1]
699
+ const s2: u32 = salt[2]
700
+ const s3: u32 = salt[3]
701
+ const st: u32[] = new Array<u32>(BCRYPT_STATE_WORDS)
702
+ bcryptInitState(st, 0)
703
+ const expanded: u32[] = new Array<u32>(19)
704
+ const lr: u32[] = new Array<u32>(2)
705
+ bcryptSetKey(st, expanded, password, setting.safety)
706
+
707
+ // The salted expansion: each block is XORed with a salt half before it is
708
+ // encrypted, the first half and the second in turn (`BF_crypt`'s two loops).
709
+ for (let i: i32 = 0; i < BCRYPT_STATE_WORDS; i += 2) {
710
+ const odd: boolean = (i & 2) !== 0
711
+ lr[0] ^= odd ? s2 : s0
712
+ lr[1] ^= odd ? s3 : s1
713
+ bcryptEncrypt(st, lr)
714
+ bcryptStore(st, i, lr)
715
+ }
716
+
717
+ // 2^cost rounds of the key, then the salt, re-keying the whole state.
718
+ let rounds: u32 = toU32(1) << toU32(setting.cost)
719
+ while (rounds !== 0) {
720
+ bcryptXorKey(st, expanded)
721
+ bcryptBody(st)
722
+ bcryptXorSalt(st, 0, s0, s1, s2, s3)
723
+ bcryptBody(st)
724
+ rounds -= 1
725
+ }
726
+
727
+ // "OrpheanBeholderScryDoubt", each pair encrypted 64 times, big-endian.
728
+ const digest: u8[] = new Array<u8>(24)
729
+ bcryptMagic(st, lr, digest, 0, 0x4f727068, 0x65616e42)
730
+ bcryptMagic(st, lr, digest, 8, 0x65686f6c, 0x64657253)
731
+ bcryptMagic(st, lr, digest, 16, 0x63727944, 0x6f756274)
732
+ // 23 of the 24 bytes, as the reference encodes them.
733
+ bcryptEncodeInto(out, BCRYPT_SETTING_LENGTH, digest, 23)
734
+ wipe(st)
735
+ wipe(expanded)
736
+ wipe(lr)
737
+ wipe(digest)
738
+ }
739
+
740
+ /** One pair of the magic string, encrypted 64 times under `st`, into `dst` at `at` as eight big-endian bytes. */
741
+ const bcryptMagic = (st: u32[], lr: u32[], dst: u8[], at: i32, l: u32, r: u32): void => {
742
+ lr[0] = l
743
+ lr[1] = r
744
+ for (let k: i32 = 0; k < 64; k++) {
745
+ bcryptEncrypt(st, lr)
746
+ }
747
+ bcryptPutBlock(dst, at, lr[0], lr[1])
748
+ }
749
+
750
+ /** The block `l`, `r` into `dst` at `at` as eight big-endian bytes; nothing when it does not fit. */
751
+ const bcryptPutBlock = (dst: u8[], at: i32, l: u32, r: u32): void => {
752
+ if (at < 0 || at + 7 >= toI32(dst.length)) {
753
+ return
754
+ }
755
+ dst[at] = toU8(l >>> 24)
756
+ dst[at + 1] = toU8((l >>> 16) & 0xff)
757
+ dst[at + 2] = toU8((l >>> 8) & 0xff)
758
+ dst[at + 3] = toU8(l & 0xff)
759
+ dst[at + 4] = toU8(r >>> 24)
760
+ dst[at + 5] = toU8((r >>> 16) & 0xff)
761
+ dst[at + 6] = toU8((r >>> 8) & 0xff)
762
+ dst[at + 7] = toU8(r & 0xff)
763
+ }
764
+
765
+ /**
766
+ * The 60-byte hash of the plain password under `job`'s setting into `out`,
767
+ * which the caller made and wipes: the 29-byte setting, then the digest.
768
+ */
769
+ const bcryptHashInto = (password: u8[], job: BcryptJob, out: u8[]): void => {
770
+ bcryptPut(out, 0, job.prefix)
771
+ bcryptExposed(password, job.setting, out)
772
+ }
773
+
774
+ /**
775
+ * The 60 bytes of the hash, for `exposeWith` on the password. They are the
776
+ * hash `bcryptHash` returns, so they are not secret and are not wiped.
777
+ */
778
+ const bcryptHashExposed = (password: u8[], job: BcryptJob): u8[] => {
779
+ const out: u8[] = new Array<u8>(BCRYPT_HASH_LENGTH)
780
+ bcryptHashInto(password, job, out)
781
+ return out
782
+ }
783
+
784
+ /**
785
+ * Whether the hash rebuilt from the plain password is `job.hash`, for
786
+ * `exposeWith` on the password.
787
+ */
788
+ const bcryptVerifyExposed = (password: u8[], job: BcryptJob): boolean => {
789
+ const expected: u8[] = new Array<u8>(BCRYPT_HASH_LENGTH)
790
+ bcryptHashInto(password, job, expected)
791
+ return bcryptSameWiped(expected, job.hash)
792
+ }
793
+
794
+ /**
795
+ * Whether the 60 bytes of `rebuilt` are `hash`'s: a constant-time compare.
796
+ * `rebuilt` and the copy of `hash` made here are both wiped, so the answer is
797
+ * all that leaves.
798
+ */
799
+ const bcryptSameWiped = (rebuilt: u8[], hash: string): boolean => {
800
+ const given: u8[] = new Array<u8>(BCRYPT_HASH_LENGTH)
801
+ bcryptBytesOf(given, hash)
802
+ const same: boolean = timingSafeEqual(rebuilt, given)
803
+ wipe(rebuilt)
804
+ wipe(given)
805
+ return same
806
+ }
807
+
808
+ /**
809
+ * The `$2b$` hash of `password` with a 16-byte `salt` and 2^`cost` rounds:
810
+ * 60 characters, `$2b$`, the two-digit cost, `$`, the salt and the digest in
811
+ * bcrypt's base64. The salt must come from a CSPRNG and be fresh for each
812
+ * hash. An `Err` for a salt of another length or a cost outside 4–31.
813
+ */
814
+ export const bcryptHash = (password: Secret<u8[]>, salt: u8[], cost: i32): Result<string, string> => {
815
+ if (salt.length !== 16) {
816
+ return Err("bcrypt: the salt is not 16 bytes")
817
+ }
818
+ if (cost < BCRYPT_MIN_COST || cost > BCRYPT_MAX_COST) {
819
+ return Err("bcrypt: the cost is outside 4 to 31")
820
+ }
821
+ const setting: BcryptSetting = { salt: bcryptSaltWords(salt), cost: cost, safety: toU32(0) }
822
+ const job: BcryptJob = { setting: setting, prefix: bcryptSettingOf(salt, cost), hash: "" }
823
+ const out: u8[] = exposeWith(password, job, bcryptHashExposed)
824
+ return Ok(String.fromBytes(out, 0, BCRYPT_HASH_LENGTH))
825
+ }
826
+
827
+ /**
828
+ * Whether `hash` is `password`'s: `Ok(true)` or `Ok(false)`, decided by a
829
+ * constant-time compare of all 60 characters with the hash rebuilt from the
830
+ * password and `hash`'s own prefix, salt and cost. `$2a$`, `$2b$` and `$2y$`
831
+ * are read; an `Err` names what makes `hash` not a bcrypt hash.
832
+ */
833
+ export const bcryptVerify = (password: Secret<u8[]>, hash: string): Result<boolean, string> => {
834
+ const parsed: Result<BcryptParsed, string> = bcryptParseHash(hash)
835
+ if (parsed.isErr()) {
836
+ return Err(parsed.error)
837
+ }
838
+ return Ok(bcryptVerifyOne(password, parsed.value, hash))
839
+ }
840
+
841
+ /** Whether `hash`, parsed as `parsed`, is `password`'s. */
842
+ const bcryptVerifyOne = (password: Secret<u8[]>, parsed: BcryptParsed, hash: string): boolean => {
843
+ const job: BcryptJob = { setting: parsed.setting, prefix: parsed.prefix, hash: hash }
844
+ return exposeWith(password, job, bcryptVerifyExposed)
845
+ }
846
+
847
+ /**
848
+ * `hash` parsed for verifying: 60 characters, its setting well-formed and its
849
+ * digest in bcrypt's base64, or an `Err` naming what is not.
850
+ */
851
+ const bcryptParseHash = (hash: string): Result<BcryptParsed, string> => {
852
+ if (hash.length !== 60) {
853
+ return Err("bcrypt: a hash is 60 characters")
854
+ }
855
+ const parsed: Result<BcryptParsed, string> = bcryptParse(hash)
856
+ if (parsed.isErr()) {
857
+ return Err(parsed.error)
858
+ }
859
+ let alphabet: i32 = 0
860
+ for (let i: i32 = 0; i < toI32(hash.length); i++) {
861
+ if (i >= BCRYPT_SETTING_LENGTH) {
862
+ alphabet |= bcryptSextetOf(toI32(hash.charCodeAt(i)))
863
+ }
864
+ }
865
+ if (alphabet < 0) {
866
+ return Err("bcrypt: the digest is not bcrypt base64")
867
+ }
868
+ return parsed
869
+ }
870
+
871
+ /** The 60 characters of a hash `bcryptParseHash` accepted, as bytes into `given`, which the caller made and wipes. */
872
+ const bcryptBytesOf = (given: u8[], hash: string): void => {
873
+ for (let i: i32 = 0; i < toI32(given.length) && i < toI32(hash.length); i++) {
874
+ given[i] = toU8(hash.charCodeAt(i))
875
+ }
876
+ }
877
+
878
+ // ---------------------------------------------------------------------------
879
+ // The four-lane batch. Four independent hashes share one `Secret<u32[]>`:
880
+ // lane k's Blowfish state at 1042k (0–4167), its expanded key at 4168 + 18k
881
+ // and its `$2a$` safety word at 4240 + k. One exposeWith per password XORs that
882
+ // lane's key in, and one more runs the expansion, the 2^cost rounds and the
883
+ // magic string on all four with their encryption chains interleaved, so the
884
+ // S-box loads of one lane overlap the others' and the core is not left
885
+ // waiting on one chain's latency.
886
+ // ---------------------------------------------------------------------------
887
+
888
+ /** Where the four expanded keys of 18 start in the batch state, after the four Blowfish states. */
889
+ const BCRYPT_BATCH_KEYS: i32 = 4168
890
+ /** Where the four safety words start, after the keys. */
891
+ const BCRYPT_BATCH_SAFETY: i32 = 4240
892
+ /** Words in the batch state: four Blowfish states, four expanded keys of 18 and four safety words. */
893
+ const BCRYPT_BATCH_WORDS: i32 = 4244
894
+
895
+ /**
896
+ * What the batch kernel reads besides the state: each lane's four salt words
897
+ * (lane k at 4k), each lane's `$2a$` safety word, the shared cost, the lanes
898
+ * that run (bit k for lane k), each lane's 29-character setting and, for
899
+ * verifying, each lane's hash to compare with.
900
+ */
901
+ interface BcryptBatch {
902
+ salts: u32[]
903
+ safety: u32[]
904
+ cost: i32
905
+ mask: i32
906
+ prefixes: u8[][]
907
+ hashes: string[]
908
+ }
909
+
910
+ /** The four states set to π and each lane's safety word stored, for `exposeWith` on the fresh batch state. */
911
+ const bcryptInit4 = (st: u32[], batch: BcryptBatch): i32 => {
912
+ if (st.length < 4244) {
913
+ return 0
914
+ }
915
+ const safety: u32[] = batch.safety
916
+ for (let k: i32 = 0; k < 4; k++) {
917
+ bcryptInitState(st, k * BCRYPT_STATE_WORDS)
918
+ st[BCRYPT_BATCH_SAFETY + k] = k < toI32(safety.length) ? safety[k] : 0
919
+ }
920
+ return 0
921
+ }
922
+
923
+ /**
924
+ * Word `arg & 31` of `bcryptKeySchedule` for `password`, with the `$2a$`
925
+ * safety when bit 5 of `arg` is set. The schedule is built in a fresh array
926
+ * and wiped here, so the one word is all that leaves the `exposeWith`.
927
+ */
928
+ const bcryptKeyWord = (password: u8[], arg: i32): u32 => {
929
+ const expanded: u32[] = new Array<u32>(19)
930
+ const safety: u32 = (arg & 32) !== 0 ? 0x10000 : 0
931
+ bcryptKeySchedule(expanded, password, safety)
932
+ const k: i32 = arg & 31
933
+ const word: u32 = k < 19 ? expanded[k] : 0
934
+ wipe(expanded)
935
+ return word
936
+ }
937
+
938
+ /**
939
+ * Lane `lane`'s key into the batch state: P XORed with it as `bcryptSetKey`
940
+ * XORs it, and the 18 words kept for the cost rounds. Each word is one
941
+ * `exposeWith` on the password, since the state is what this function is
942
+ * exposing and a `Secret` cannot be handed into it otherwise.
943
+ */
944
+ const bcryptLoadLane = (st: u32[], password: Secret<u8[]>, lane: i32): i32 => {
945
+ if (st.length < 4244) {
946
+ return 0
947
+ }
948
+ if (lane < 0 || lane > 3) {
949
+ return 0
950
+ }
951
+ const at: i32 = lane * BCRYPT_STATE_WORDS
952
+ const kept: i32 = lane * 18 + BCRYPT_BATCH_KEYS
953
+ const flag: i32 = st[BCRYPT_BATCH_SAFETY + lane] !== 0 ? 32 : 0
954
+ for (let k: i32 = 0; k < 18; k++) {
955
+ const word: u32 = exposeWith(password, k | flag, bcryptKeyWord)
956
+ st[at + k] ^= word
957
+ st[kept + k] = word
958
+ }
959
+ st[at] ^= exposeWith(password, 18 | flag, bcryptKeyWord)
960
+ return 0
961
+ }
962
+
963
+ /** `bcryptLoadLane` for lane 0, in the shape `exposeWith` calls. */
964
+ const bcryptLoadLane0 = (st: u32[], password: Secret<u8[]>): i32 => bcryptLoadLane(st, password, 0)
965
+
966
+ /** `bcryptLoadLane` for lane 1. */
967
+ const bcryptLoadLane1 = (st: u32[], password: Secret<u8[]>): i32 => bcryptLoadLane(st, password, 1)
968
+
969
+ /** `bcryptLoadLane` for lane 2. */
970
+ const bcryptLoadLane2 = (st: u32[], password: Secret<u8[]>): i32 => bcryptLoadLane(st, password, 2)
971
+
972
+ /** `bcryptLoadLane` for lane 3. */
973
+ const bcryptLoadLane3 = (st: u32[], password: Secret<u8[]>): i32 => bcryptLoadLane(st, password, 3)
974
+
975
+ /**
976
+ * `bcryptBody` on all four lanes at once: each lane's state re-encrypted in
977
+ * place, the four encryption chains interleaved round by round so that one
978
+ * lane's S-box loads overlap the others' instead of waiting on their own. The
979
+ * hot loop of the batch: 2^(cost+1) calls of 521 blocks per lane.
980
+ */
981
+ const bcryptBody4 = (st: u32[]): void => {
982
+ if (st.length < 4168) {
983
+ return
984
+ }
985
+ let l0: u32 = 0
986
+ let r0: u32 = 0
987
+ let l1: u32 = 0
988
+ let r1: u32 = 0
989
+ let l2: u32 = 0
990
+ let r2: u32 = 0
991
+ let l3: u32 = 0
992
+ let r3: u32 = 0
993
+ for (let i: i32 = 0; i + 1 < 1042; i += 2) {
994
+ l0 ^= st[0]
995
+ l1 ^= st[1042]
996
+ l2 ^= st[2084]
997
+ l3 ^= st[3126]
998
+ for (let n: i32 = 1; n < 17; n += 2) {
999
+ const l0a: i32 = toI32(l0 >>> 24) & 0xff
1000
+ const l0b: i32 = toI32(l0 >>> 16) & 0xff
1001
+ const l0c: i32 = toI32(l0 >>> 8) & 0xff
1002
+ const l0d: i32 = toI32(l0) & 0xff
1003
+ const l1a: i32 = toI32(l1 >>> 24) & 0xff
1004
+ const l1b: i32 = toI32(l1 >>> 16) & 0xff
1005
+ const l1c: i32 = toI32(l1 >>> 8) & 0xff
1006
+ const l1d: i32 = toI32(l1) & 0xff
1007
+ const l2a: i32 = toI32(l2 >>> 24) & 0xff
1008
+ const l2b: i32 = toI32(l2 >>> 16) & 0xff
1009
+ const l2c: i32 = toI32(l2 >>> 8) & 0xff
1010
+ const l2d: i32 = toI32(l2) & 0xff
1011
+ const l3a: i32 = toI32(l3 >>> 24) & 0xff
1012
+ const l3b: i32 = toI32(l3 >>> 16) & 0xff
1013
+ const l3c: i32 = toI32(l3 >>> 8) & 0xff
1014
+ const l3d: i32 = toI32(l3) & 0xff
1015
+ r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1016
+ r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1017
+ r2 ^= st[2084 + n] ^ (((st[2102 + l2a] + st[2358 + l2b]) ^ st[2614 + l2c]) + st[2870 + l2d])
1018
+ r3 ^= st[3126 + n] ^ (((st[3144 + l3a] + st[3400 + l3b]) ^ st[3656 + l3c]) + st[3912 + l3d])
1019
+ const r0a: i32 = toI32(r0 >>> 24) & 0xff
1020
+ const r0b: i32 = toI32(r0 >>> 16) & 0xff
1021
+ const r0c: i32 = toI32(r0 >>> 8) & 0xff
1022
+ const r0d: i32 = toI32(r0) & 0xff
1023
+ const r1a: i32 = toI32(r1 >>> 24) & 0xff
1024
+ const r1b: i32 = toI32(r1 >>> 16) & 0xff
1025
+ const r1c: i32 = toI32(r1 >>> 8) & 0xff
1026
+ const r1d: i32 = toI32(r1) & 0xff
1027
+ const r2a: i32 = toI32(r2 >>> 24) & 0xff
1028
+ const r2b: i32 = toI32(r2 >>> 16) & 0xff
1029
+ const r2c: i32 = toI32(r2 >>> 8) & 0xff
1030
+ const r2d: i32 = toI32(r2) & 0xff
1031
+ const r3a: i32 = toI32(r3 >>> 24) & 0xff
1032
+ const r3b: i32 = toI32(r3 >>> 16) & 0xff
1033
+ const r3c: i32 = toI32(r3 >>> 8) & 0xff
1034
+ const r3d: i32 = toI32(r3) & 0xff
1035
+ l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1036
+ l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1037
+ l2 ^= st[2084 + n + 1] ^ (((st[2102 + r2a] + st[2358 + r2b]) ^ st[2614 + r2c]) + st[2870 + r2d])
1038
+ l3 ^= st[3126 + n + 1] ^ (((st[3144 + r3a] + st[3400 + r3b]) ^ st[3656 + r3c]) + st[3912 + r3d])
1039
+ }
1040
+ const t0: u32 = r0
1041
+ r0 = l0
1042
+ l0 = t0 ^ st[17]
1043
+ st[i] = l0
1044
+ st[i + 1] = r0
1045
+ const t1: u32 = r1
1046
+ r1 = l1
1047
+ l1 = t1 ^ st[1059]
1048
+ st[1042 + i] = l1
1049
+ st[1042 + i + 1] = r1
1050
+ const t2: u32 = r2
1051
+ r2 = l2
1052
+ l2 = t2 ^ st[2101]
1053
+ st[2084 + i] = l2
1054
+ st[2084 + i + 1] = r2
1055
+ const t3: u32 = r3
1056
+ r3 = l3
1057
+ l3 = t3 ^ st[3143]
1058
+ st[3126 + i] = l3
1059
+ st[3126 + i + 1] = r3
1060
+ }
1061
+ }
1062
+
1063
+ /** `bcryptEncrypt` on all four lanes: the block of lane `k` is `lr[2k]` and `lr[2k + 1]`. */
1064
+ const bcryptEncrypt4 = (st: u32[], lr: u32[]): void => {
1065
+ if (st.length < 4168) {
1066
+ return
1067
+ }
1068
+ if (lr.length < 8) {
1069
+ return
1070
+ }
1071
+ let l0: u32 = lr[0] ^ st[0]
1072
+ let r0: u32 = lr[1]
1073
+ let l1: u32 = lr[2] ^ st[1042]
1074
+ let r1: u32 = lr[3]
1075
+ let l2: u32 = lr[4] ^ st[2084]
1076
+ let r2: u32 = lr[5]
1077
+ let l3: u32 = lr[6] ^ st[3126]
1078
+ let r3: u32 = lr[7]
1079
+ for (let n: i32 = 1; n < 17; n += 2) {
1080
+ const l0a: i32 = toI32(l0 >>> 24) & 0xff
1081
+ const l0b: i32 = toI32(l0 >>> 16) & 0xff
1082
+ const l0c: i32 = toI32(l0 >>> 8) & 0xff
1083
+ const l0d: i32 = toI32(l0) & 0xff
1084
+ const l1a: i32 = toI32(l1 >>> 24) & 0xff
1085
+ const l1b: i32 = toI32(l1 >>> 16) & 0xff
1086
+ const l1c: i32 = toI32(l1 >>> 8) & 0xff
1087
+ const l1d: i32 = toI32(l1) & 0xff
1088
+ const l2a: i32 = toI32(l2 >>> 24) & 0xff
1089
+ const l2b: i32 = toI32(l2 >>> 16) & 0xff
1090
+ const l2c: i32 = toI32(l2 >>> 8) & 0xff
1091
+ const l2d: i32 = toI32(l2) & 0xff
1092
+ const l3a: i32 = toI32(l3 >>> 24) & 0xff
1093
+ const l3b: i32 = toI32(l3 >>> 16) & 0xff
1094
+ const l3c: i32 = toI32(l3 >>> 8) & 0xff
1095
+ const l3d: i32 = toI32(l3) & 0xff
1096
+ r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1097
+ r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1098
+ r2 ^= st[2084 + n] ^ (((st[2102 + l2a] + st[2358 + l2b]) ^ st[2614 + l2c]) + st[2870 + l2d])
1099
+ r3 ^= st[3126 + n] ^ (((st[3144 + l3a] + st[3400 + l3b]) ^ st[3656 + l3c]) + st[3912 + l3d])
1100
+ const r0a: i32 = toI32(r0 >>> 24) & 0xff
1101
+ const r0b: i32 = toI32(r0 >>> 16) & 0xff
1102
+ const r0c: i32 = toI32(r0 >>> 8) & 0xff
1103
+ const r0d: i32 = toI32(r0) & 0xff
1104
+ const r1a: i32 = toI32(r1 >>> 24) & 0xff
1105
+ const r1b: i32 = toI32(r1 >>> 16) & 0xff
1106
+ const r1c: i32 = toI32(r1 >>> 8) & 0xff
1107
+ const r1d: i32 = toI32(r1) & 0xff
1108
+ const r2a: i32 = toI32(r2 >>> 24) & 0xff
1109
+ const r2b: i32 = toI32(r2 >>> 16) & 0xff
1110
+ const r2c: i32 = toI32(r2 >>> 8) & 0xff
1111
+ const r2d: i32 = toI32(r2) & 0xff
1112
+ const r3a: i32 = toI32(r3 >>> 24) & 0xff
1113
+ const r3b: i32 = toI32(r3 >>> 16) & 0xff
1114
+ const r3c: i32 = toI32(r3 >>> 8) & 0xff
1115
+ const r3d: i32 = toI32(r3) & 0xff
1116
+ l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1117
+ l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1118
+ l2 ^= st[2084 + n + 1] ^ (((st[2102 + r2a] + st[2358 + r2b]) ^ st[2614 + r2c]) + st[2870 + r2d])
1119
+ l3 ^= st[3126 + n + 1] ^ (((st[3144 + r3a] + st[3400 + r3b]) ^ st[3656 + r3c]) + st[3912 + r3d])
1120
+ }
1121
+ lr[0] = r0 ^ st[17]
1122
+ lr[1] = l0
1123
+ lr[2] = r1 ^ st[1059]
1124
+ lr[3] = l1
1125
+ lr[4] = r2 ^ st[2101]
1126
+ lr[5] = l2
1127
+ lr[6] = r3 ^ st[3143]
1128
+ lr[7] = l3
1129
+ }
1130
+
1131
+ /** Each lane's P XORed with its kept expanded key, as `bcryptXorKey`. */
1132
+ const bcryptXorKey4 = (st: u32[]): void => {
1133
+ if (st.length < 4244) {
1134
+ return
1135
+ }
1136
+ for (let i: i32 = 0; i < 18; i++) {
1137
+ st[i] ^= st[4168 + i]
1138
+ st[1042 + i] ^= st[4186 + i]
1139
+ st[2084 + i] ^= st[4204 + i]
1140
+ st[3126 + i] ^= st[4222 + i]
1141
+ }
1142
+ }
1143
+
1144
+ /** Each lane's P XORed with its salt words, as `bcryptXorSalt`. */
1145
+ const bcryptXorSalt4 = (st: u32[], salts: u32[]): void => {
1146
+ if (salts.length < 16) {
1147
+ return
1148
+ }
1149
+ for (let k: i32 = 0; k < 4; k++) {
1150
+ bcryptXorSalt(
1151
+ st,
1152
+ k * BCRYPT_STATE_WORDS,
1153
+ salts[k * 4],
1154
+ salts[k * 4 + 1],
1155
+ salts[k * 4 + 2],
1156
+ salts[k * 4 + 3]
1157
+ )
1158
+ }
1159
+ }
1160
+
1161
+ /**
1162
+ * The batch's hash, on the batch state with its keys loaded: the salted
1163
+ * expansion, 2^cost rounds and the magic string on all four lanes, and the 31
1164
+ * digest characters of lane k into `digests` at 31k, which the caller made and
1165
+ * wipes. The block and digest copies are wiped here.
1166
+ */
1167
+ const bcryptEks4 = (st: u32[], salts: u32[], cost: i32, digests: u8[]): void => {
1168
+ if (st.length < 4244 || salts.length < 16) {
1169
+ return
1170
+ }
1171
+ const lr: u32[] = new Array<u32>(8)
1172
+ for (let i: i32 = 0; i < 1042; i += 2) {
1173
+ const half: i32 = (i & 2) !== 0 ? 2 : 0
1174
+ for (let k: i32 = 0; k < 4; k++) {
1175
+ lr[2 * k] ^= salts[4 * k + half]
1176
+ lr[2 * k + 1] ^= salts[4 * k + half + 1]
1177
+ }
1178
+ bcryptEncrypt4(st, lr)
1179
+ for (let k: i32 = 0; k < 4; k++) {
1180
+ const at: i32 = k * 1042 + i
1181
+ if (at + 1 < toI32(st.length)) {
1182
+ st[at] = lr[2 * k]
1183
+ st[at + 1] = lr[2 * k + 1]
1184
+ }
1185
+ }
1186
+ }
1187
+
1188
+ let rounds: u32 = toU32(1) << toU32(cost)
1189
+ while (rounds !== 0) {
1190
+ bcryptXorKey4(st)
1191
+ bcryptBody4(st)
1192
+ bcryptXorSalt4(st, salts)
1193
+ bcryptBody4(st)
1194
+ rounds -= 1
1195
+ }
1196
+
1197
+ const digest: u8[] = new Array<u8>(96)
1198
+ bcryptMagic4(st, lr, digest, 0, 0x4f727068, 0x65616e42)
1199
+ bcryptMagic4(st, lr, digest, 8, 0x65686f6c, 0x64657253)
1200
+ bcryptMagic4(st, lr, digest, 16, 0x63727944, 0x6f756274)
1201
+ const one: u8[] = new Array<u8>(24)
1202
+ for (let k: i32 = 0; k < 4; k++) {
1203
+ for (let j: i32 = 0; j < 24; j++) {
1204
+ one[j] = digest[24 * k + j]
1205
+ }
1206
+ bcryptEncodeInto(digests, 31 * k, one, 23)
1207
+ }
1208
+ wipe(lr)
1209
+ wipe(digest)
1210
+ wipe(one)
1211
+ }
1212
+
1213
+ /**
1214
+ * The four lanes' 60-byte hashes, lane k at 60k, for `exposeWith` on the
1215
+ * loaded batch state; a lane outside `batch.mask` is left zero. They are the
1216
+ * hashes `bcryptHash4` returns, so they are not secret and are not wiped; the
1217
+ * digest characters they are joined from are wiped here.
1218
+ */
1219
+ const bcryptHash4Exposed = (st: u32[], batch: BcryptBatch): u8[] => {
1220
+ const out: u8[] = new Array<u8>(4 * BCRYPT_HASH_LENGTH)
1221
+ const digests: u8[] = new Array<u8>(124)
1222
+ bcryptEks4(st, batch.salts, batch.cost, digests)
1223
+ for (let k: i32 = 0; k < 4 && k < toI32(batch.prefixes.length); k++) {
1224
+ if ((batch.mask & (1 << k)) !== 0) {
1225
+ bcryptJoin(out, BCRYPT_HASH_LENGTH * k, batch.prefixes[k], digests, k)
1226
+ }
1227
+ }
1228
+ wipe(digests)
1229
+ return out
1230
+ }
1231
+
1232
+ /**
1233
+ * Bit k set for each running lane whose rebuilt hash is `batch.hashes[k]`, for
1234
+ * `exposeWith` on the loaded batch state. The digest characters are built and
1235
+ * wiped here, so the bits are all that leaves.
1236
+ */
1237
+ const bcryptVerify4Exposed = (st: u32[], batch: BcryptBatch): i32 => {
1238
+ const digests: u8[] = new Array<u8>(124)
1239
+ bcryptEks4(st, batch.salts, batch.cost, digests)
1240
+ let same: i32 = 0
1241
+ for (let k: i32 = 0; k < 4 && k < toI32(batch.prefixes.length) && k < toI32(batch.hashes.length); k++) {
1242
+ if ((batch.mask & (1 << k)) !== 0 && bcryptJoinedOk(batch.prefixes[k], digests, k, batch.hashes[k])) {
1243
+ same |= 1 << k
1244
+ }
1245
+ }
1246
+ wipe(digests)
1247
+ return same
1248
+ }
1249
+
1250
+ /** `bcryptMagic` on all four lanes: lane k's eight bytes into `dst` at `24k + at`. */
1251
+ const bcryptMagic4 = (st: u32[], lr: u32[], dst: u8[], at: i32, l: u32, r: u32): void => {
1252
+ if (lr.length < 8) {
1253
+ return
1254
+ }
1255
+ for (let k: i32 = 0; k < 4; k++) {
1256
+ lr[2 * k] = l
1257
+ lr[2 * k + 1] = r
1258
+ }
1259
+ for (let n: i32 = 0; n < 64; n++) {
1260
+ bcryptEncrypt4(st, lr)
1261
+ }
1262
+ for (let k: i32 = 0; k < 4; k++) {
1263
+ bcryptPutBlock(dst, 24 * k + at, lr[2 * k], lr[2 * k + 1])
1264
+ }
1265
+ }
1266
+
1267
+ /**
1268
+ * The batch `state` set up for `batch.mask`: the four states set to π, the
1269
+ * safety words stored and each running lane's key loaded. A lane outside the
1270
+ * mask runs on no key and its characters are not read; lane 0 is always
1271
+ * given, and a null lane must be outside the mask.
1272
+ */
1273
+ const bcryptLoad4 = (
1274
+ state: Secret<u32[]>,
1275
+ p0: Secret<u8[]>,
1276
+ p1: Secret<u8[]> | null,
1277
+ p2: Secret<u8[]> | null,
1278
+ p3: Secret<u8[]> | null,
1279
+ batch: BcryptBatch
1280
+ ): void => {
1281
+ const mask: i32 = batch.mask
1282
+ exposeWith(state, batch, bcryptInit4)
1283
+ if ((mask & 1) !== 0) {
1284
+ exposeWith(state, p0, bcryptLoadLane0)
1285
+ }
1286
+ if (p1 !== null && (mask & 2) !== 0) {
1287
+ exposeWith(state, p1, bcryptLoadLane1)
1288
+ }
1289
+ if (p2 !== null && (mask & 4) !== 0) {
1290
+ exposeWith(state, p2, bcryptLoadLane2)
1291
+ }
1292
+ if (p3 !== null && (mask & 8) !== 0) {
1293
+ exposeWith(state, p3, bcryptLoadLane3)
1294
+ }
1295
+ }
1296
+
1297
+ /** Lane k's 60-byte hash out of the batch's `all`, as a string. */
1298
+ const bcryptLaneString = (all: u8[], k: i32): string =>
1299
+ String.fromBytes(all, BCRYPT_HASH_LENGTH * k, BCRYPT_HASH_LENGTH * (k + 1))
1300
+
1301
+ /** Bit k set for each lane given: lane 0 always, lanes 1–3 when not null. */
1302
+ const bcryptMaskOf = (p1: Secret<u8[]> | null, p2: Secret<u8[]> | null, p3: Secret<u8[]> | null): i32 => {
1303
+ let mask: i32 = 1
1304
+ if (p1 !== null) {
1305
+ mask |= 2
1306
+ }
1307
+ if (p2 !== null) {
1308
+ mask |= 4
1309
+ }
1310
+ if (p3 !== null) {
1311
+ mask |= 8
1312
+ }
1313
+ return mask
1314
+ }
1315
+
1316
+ /**
1317
+ * The `$2b$` hashes of up to four passwords at once, each as `bcryptHash`
1318
+ * makes it: lane k with the 16 bytes of `salts` at 16k and 2^`cost` rounds.
1319
+ * Lane 0 is always hashed; a null lane gives "" and its salt bytes are not
1320
+ * read. Two to four lanes run as one interleaved computation, which on one
1321
+ * core takes about the time of 1.2 single hashes (bench/crypto/bcrypt); one
1322
+ * lane runs as `bcryptHash`. An `Err` for `salts` of another length than 64
1323
+ * or a cost outside 4–31.
1324
+ */
1325
+ export const bcryptHash4 = (
1326
+ p0: Secret<u8[]>,
1327
+ p1: Secret<u8[]> | null,
1328
+ p2: Secret<u8[]> | null,
1329
+ p3: Secret<u8[]> | null,
1330
+ salts: u8[],
1331
+ cost: i32
1332
+ ): Result<string[], string> => {
1333
+ if (salts.length !== 64) {
1334
+ return Err("bcrypt: the salts are not 64 bytes")
1335
+ }
1336
+ if (cost < BCRYPT_MIN_COST || cost > BCRYPT_MAX_COST) {
1337
+ return Err("bcrypt: the cost is outside 4 to 31")
1338
+ }
1339
+ const mask: i32 = bcryptMaskOf(p1, p2, p3)
1340
+ const hashes: string[] = ["", "", "", ""]
1341
+ if (mask === 1) {
1342
+ const one: Result<string, string> = bcryptHash(p0, bcryptSaltOf(salts, 0), cost)
1343
+ if (one.isErr()) {
1344
+ return Err(one.error)
1345
+ }
1346
+ hashes[0] = one.value
1347
+ return Ok(hashes)
1348
+ }
1349
+ const words: u32[] = new Array<u32>(16)
1350
+ const prefixes: u8[][] = []
1351
+ for (let k: i32 = 0; k < 4; k++) {
1352
+ if ((mask & (1 << k)) === 0) {
1353
+ prefixes.push([])
1354
+ continue
1355
+ }
1356
+ prefixes.push(bcryptBatchSalt(words, salts, k, cost))
1357
+ }
1358
+ const none: string[] = []
1359
+ const batch: BcryptBatch = {
1360
+ salts: words,
1361
+ safety: new Array<u32>(4),
1362
+ cost: cost,
1363
+ mask: mask,
1364
+ prefixes: prefixes,
1365
+ hashes: none,
1366
+ }
1367
+ const state: Secret<u32[]> = secret(new Array<u32>(BCRYPT_BATCH_WORDS))
1368
+ bcryptLoad4(state, p0, p1, p2, p3, batch)
1369
+ const all: u8[] = exposeWith(state, batch, bcryptHash4Exposed)
1370
+ wipe(state)
1371
+ const out: string[] = []
1372
+ for (let k: i32 = 0; k < 4; k++) {
1373
+ out.push((mask & (1 << k)) !== 0 ? bcryptLaneString(all, k) : "")
1374
+ }
1375
+ return Ok(out)
1376
+ }
1377
+
1378
+ /** Lane `k`'s salt from the 64 bytes of `salts` into `words` at 4k as four words, and its 29-byte setting. */
1379
+ const bcryptBatchSalt = (words: u32[], salts: u8[], k: i32, cost: i32): u8[] => {
1380
+ const salt: u8[] = bcryptSaltOf(salts, k)
1381
+ const four: u32[] = bcryptSaltWords(salt)
1382
+ for (let j: i32 = 0; j < 4 && j < toI32(four.length) && 4 * k + j < toI32(words.length); j++) {
1383
+ words[4 * k + j] = four[j]
1384
+ }
1385
+ return bcryptSettingOf(salt, cost)
1386
+ }
1387
+
1388
+ /** The 16 salt bytes of lane `k` from the 64 of a batch. */
1389
+ const bcryptSaltOf = (salts: u8[], k: i32): u8[] => {
1390
+ const salt: u8[] = new Array<u8>(16)
1391
+ for (let j: i32 = 0; j < 16 && 16 * k + j < toI32(salts.length); j++) {
1392
+ salt[j] = salts[16 * k + j]
1393
+ }
1394
+ return salt
1395
+ }
1396
+
1397
+ /** The 60 bytes of lane `k`'s hash into `out` at `at`: its 29-byte setting, then its 31 digest characters from the batch's 124. */
1398
+ const bcryptJoin = (out: u8[], at: i32, prefix: u8[], digests: u8[], k: i32): void => {
1399
+ bcryptPut(out, at, prefix)
1400
+ const o: i32 = at + BCRYPT_SETTING_LENGTH
1401
+ for (
1402
+ let j: i32 = 0;
1403
+ j < 31 && 31 * k + j < toI32(digests.length) && o + j >= 0 && o + j < toI32(out.length);
1404
+ j++
1405
+ ) {
1406
+ out[o + j] = digests[31 * k + j]
1407
+ }
1408
+ }
1409
+
1410
+ /**
1411
+ * Whether each of `hashes` (four strings) is its lane's password's, as
1412
+ * `bcryptVerify` decides it: lane 0 always, a null lane `false` and its
1413
+ * string not read. Lanes are grouped by the cost their hash names and each
1414
+ * group of two or more runs as one interleaved computation; a group of one
1415
+ * runs as `bcryptVerify`. A malformed hash in any given lane makes the whole
1416
+ * call an `Err` naming the lane ("bcrypt: lane 2: …") before any hashing, so
1417
+ * one bad row in a batch cannot be read as a wrong password.
1418
+ */
1419
+ export const bcryptVerify4 = (
1420
+ p0: Secret<u8[]>,
1421
+ p1: Secret<u8[]> | null,
1422
+ p2: Secret<u8[]> | null,
1423
+ p3: Secret<u8[]> | null,
1424
+ hashes: string[]
1425
+ ): Result<boolean[], string> => {
1426
+ if (hashes.length !== 4) {
1427
+ return Err("bcrypt: a batch is four hashes")
1428
+ }
1429
+ const mask: i32 = bcryptMaskOf(p1, p2, p3)
1430
+ const lanes: BcryptParsed[] = []
1431
+ for (let k: i32 = 0; k < 4 && k < toI32(hashes.length); k++) {
1432
+ if ((mask & (1 << k)) !== 0) {
1433
+ const parsed: Result<BcryptParsed, string> = bcryptParseHash(hashes[k])
1434
+ if (parsed.isErr()) {
1435
+ const why: string = parsed.error.startsWith("bcrypt: ") ? parsed.error.substring(8) : parsed.error
1436
+ return Err(`bcrypt: lane ${k}: ${why}`)
1437
+ }
1438
+ lanes.push(parsed.value)
1439
+ } else {
1440
+ lanes.push({
1441
+ setting: { salt: new Array<u32>(4), cost: toI32(0), safety: toU32(0) },
1442
+ prefix: new Array<u8>(0),
1443
+ })
1444
+ }
1445
+ }
1446
+ const ok: boolean[] = [false, false, false, false]
1447
+ let done: i32 = ~mask & 15
1448
+ for (
1449
+ let k: i32 = 0;
1450
+ k < 4 && k < toI32(lanes.length) && k < toI32(hashes.length) && k < toI32(ok.length);
1451
+ k++
1452
+ ) {
1453
+ if ((done & (1 << k)) === 0) {
1454
+ const cost: i32 = lanes[k].setting.cost
1455
+ let group: i32 = 0
1456
+ for (let j: i32 = k; j < 4 && j < toI32(lanes.length); j++) {
1457
+ if ((done & (1 << j)) === 0 && lanes[j].setting.cost === cost) {
1458
+ group |= 1 << j
1459
+ }
1460
+ }
1461
+ done |= group
1462
+ if (group === 1 << k) {
1463
+ const one: boolean = bcryptLaneOk(p0, p1, p2, p3, k, lanes[k], hashes[k])
1464
+ if (k < toI32(ok.length)) {
1465
+ ok[k] = one
1466
+ }
1467
+ } else {
1468
+ bcryptVerifyGroup(p0, p1, p2, p3, group, lanes, hashes, ok)
1469
+ }
1470
+ }
1471
+ }
1472
+ return Ok(ok)
1473
+ }
1474
+
1475
+ /** Whether lane `k`'s password, hashed alone with `lane`'s setting, gives `hash`. */
1476
+ const bcryptLaneOk = (
1477
+ p0: Secret<u8[]>,
1478
+ p1: Secret<u8[]> | null,
1479
+ p2: Secret<u8[]> | null,
1480
+ p3: Secret<u8[]> | null,
1481
+ k: i32,
1482
+ lane: BcryptParsed,
1483
+ hash: string
1484
+ ): boolean => {
1485
+ if (k === 0) {
1486
+ return bcryptVerifyOne(p0, lane, hash)
1487
+ }
1488
+ if (k === 1 && p1 !== null) {
1489
+ return bcryptVerifyOne(p1, lane, hash)
1490
+ }
1491
+ if (k === 2 && p2 !== null) {
1492
+ return bcryptVerifyOne(p2, lane, hash)
1493
+ }
1494
+ if (k === 3 && p3 !== null) {
1495
+ return bcryptVerifyOne(p3, lane, hash)
1496
+ }
1497
+ return false
1498
+ }
1499
+
1500
+ /** Whether lane `k`'s hash joined from `prefix` and the batch's digests is `hash`. */
1501
+ const bcryptJoinedOk = (prefix: u8[], digests: u8[], k: i32, hash: string): boolean => {
1502
+ const joined: u8[] = new Array<u8>(BCRYPT_HASH_LENGTH)
1503
+ bcryptJoin(joined, 0, prefix, digests, k)
1504
+ return bcryptSameWiped(joined, hash)
1505
+ }
1506
+
1507
+ /** One interleaved run for the lanes in `group`, all of one cost, each lane's answer into `ok`. */
1508
+ const bcryptVerifyGroup = (
1509
+ p0: Secret<u8[]>,
1510
+ p1: Secret<u8[]> | null,
1511
+ p2: Secret<u8[]> | null,
1512
+ p3: Secret<u8[]> | null,
1513
+ group: i32,
1514
+ lanes: BcryptParsed[],
1515
+ hashes: string[],
1516
+ ok: boolean[]
1517
+ ): void => {
1518
+ const words: u32[] = new Array<u32>(16)
1519
+ const safety: u32[] = new Array<u32>(4)
1520
+ const prefixes: u8[][] = []
1521
+ const given: string[] = []
1522
+ let cost: i32 = 0
1523
+ // A copy, so no callee is handed the caller's `hashes` (the thread rule).
1524
+ for (let k: i32 = 0; k < toI32(hashes.length); k++) {
1525
+ given.push(hashes[k])
1526
+ }
1527
+ for (let k: i32 = 0; k < toI32(lanes.length); k++) {
1528
+ prefixes.push(lanes[k].prefix)
1529
+ }
1530
+ for (let k: i32 = 0; k < 4 && k < toI32(lanes.length) && k < toI32(safety.length); k++) {
1531
+ if ((group & (1 << k)) !== 0) {
1532
+ const setting: BcryptSetting = lanes[k].setting
1533
+ cost = setting.cost
1534
+ safety[k] = setting.safety
1535
+ for (let j: i32 = 0; j < 4 && j < toI32(setting.salt.length); j++) {
1536
+ words[4 * k + j] = setting.salt[j]
1537
+ }
1538
+ }
1539
+ }
1540
+ const batch: BcryptBatch = {
1541
+ salts: words,
1542
+ safety: safety,
1543
+ cost: cost,
1544
+ mask: group,
1545
+ prefixes: prefixes,
1546
+ hashes: given,
1547
+ }
1548
+ const state: Secret<u32[]> = secret(new Array<u32>(BCRYPT_BATCH_WORDS))
1549
+ bcryptLoad4(state, p0, p1, p2, p3, batch)
1550
+ const same: i32 = exposeWith(state, batch, bcryptVerify4Exposed)
1551
+ wipe(state)
1552
+ for (let k: i32 = 0; k < 4 && k < toI32(ok.length); k++) {
1553
+ if ((group & (1 << k)) !== 0) {
1554
+ ok[k] = (same & (1 << k)) !== 0
1555
+ }
1556
+ }
1557
+ }