@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.
- package/INSTALL.md +52 -10
- package/bin/nish +0 -0
- package/docs/AI.md +2777 -0
- package/package.json +2 -1
- package/runtime/browser.d.ts +6 -0
- package/runtime/browser.mjs +77 -0
- package/runtime/core/arrays.d.ts +36 -0
- package/runtime/core/arrays.mjs +153 -0
- package/runtime/core/checks.mjs +20 -0
- package/runtime/core/host.d.ts +80 -0
- package/runtime/core/host.mjs +325 -0
- package/runtime/core/numbers.d.ts +66 -0
- package/runtime/core/numbers.mjs +343 -0
- package/runtime/core/result.d.ts +45 -0
- package/runtime/core/result.mjs +140 -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 +46 -2
- package/runtime/nish.h +75 -2
- package/runtime/nish.mjs +23 -3
- package/runtime/node.mjs +386 -0
- package/runtime/runtime-bytes.c +401 -0
- package/runtime/runtime-cross.c +1 -1
- package/runtime/runtime-host.c +1 -1
- package/runtime/runtime-net.c +12 -1
- package/runtime/runtime-os.c +1 -1
- package/runtime/runtime-parallel.c +1 -1
- package/runtime/runtime-simd.c +231 -7
- package/runtime/runtime-term.c +504 -0
- package/runtime/runtime-wasm.c +0 -11
- package/runtime/runtime.c +190 -6
- package/runtime/shim.mjs +6 -985
- 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 +311 -59
- package/std/LICENSE-fast-float +23 -0
- package/std/LICENSE-fdlibm +8 -0
- package/std/README.md +32 -12
- package/std/base64.ts +116 -0
- package/std/bytes.ts +256 -0
- package/std/cli.ts +344 -0
- package/std/collections.ts +6 -5
- package/std/crypto/LICENSE-crypt_blowfish +26 -0
- package/std/crypto/base64url.ts +125 -0
- package/std/crypto/bcrypt.ts +1557 -0
- package/std/crypto/hmac.ts +138 -2
- package/std/crypto/x509.ts +11 -122
- package/std/json-tokenizer.ts +2998 -57
- package/std/json-value.ts +22 -3
- package/std/json.ts +87 -8
- package/std/math.ts +608 -0
- package/std/net/http1-server.ts +282 -40
- package/std/net/http1.ts +40 -21
- package/std/net/http3-server.ts +50 -12
- package/std/net/postgres.ts +2294 -0
- package/std/net/prefork.ts +23 -3
- package/std/net/quic-listener.ts +20 -4
- package/std/random.ts +66 -4
- 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/yaml-lines.ts +141 -0
- 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
|
+
}
|