@amritk/nish-x86_64-linux 0.18.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL.md +58 -28
- package/bin/nish +0 -0
- package/docs/AI.md +2759 -0
- package/package.json +3 -2
- package/runtime/browser.d.ts +14 -0
- package/runtime/browser.mjs +88 -0
- package/runtime/core/arrays.d.ts +18 -0
- package/runtime/core/arrays.mjs +125 -0
- package/runtime/core/checks.mjs +20 -0
- package/runtime/core/host.d.ts +70 -0
- package/runtime/core/host.mjs +311 -0
- package/runtime/core/numbers.d.ts +53 -0
- package/runtime/core/numbers.mjs +316 -0
- package/runtime/core/result.d.ts +41 -0
- package/runtime/core/result.mjs +127 -0
- package/runtime/core/strings.d.ts +14 -0
- package/runtime/core/strings.mjs +169 -0
- package/runtime/core.d.ts +9 -0
- package/runtime/core.mjs +55 -0
- package/runtime/host.d.ts +6 -0
- package/runtime/nish.d.ts +99 -5
- package/runtime/nish.h +289 -19
- package/runtime/nish.mjs +39 -3
- package/runtime/node.mjs +383 -0
- package/runtime/runtime-aes.c +490 -0
- package/runtime/runtime-bytes.c +401 -0
- package/runtime/runtime-cross.c +366 -0
- package/runtime/runtime-host.c +8 -7
- package/runtime/runtime-net.c +93 -34
- package/runtime/runtime-os.c +214 -16
- package/runtime/runtime-parallel.c +590 -107
- package/runtime/runtime-simd.c +235 -11
- package/runtime/runtime-term.c +504 -0
- package/runtime/runtime-wasm.c +5 -14
- package/runtime/runtime.c +257 -17
- package/runtime/shim.mjs +6 -915
- package/runtime/sqlite/LICENSE.md +85 -0
- package/runtime/sqlite/README.md +45 -0
- package/runtime/sqlite/nish-sqlite.c +359 -0
- package/runtime/sqlite/sqlite3.c +271131 -0
- package/runtime/sqlite/sqlite3.h +14447 -0
- package/scripts/build.sh +437 -28
- package/std/LICENSE-fast-float +23 -0
- package/std/LICENSE-fdlibm +8 -0
- package/std/README.md +108 -58
- package/std/base64.ts +116 -0
- package/std/bytes.ts +419 -0
- package/std/cli.ts +328 -0
- package/std/collections.ts +6 -5
- package/std/crypto/LICENSE-crypt_blowfish +26 -0
- package/std/crypto/LICENSE-libsecp256k1 +19 -0
- package/std/crypto/aes.ts +238 -76
- package/std/crypto/base64url.ts +125 -0
- package/std/crypto/bcrypt.ts +760 -0
- package/std/crypto/chacha20poly1305.ts +43 -9
- package/std/crypto/ed25519.ts +840 -0
- package/std/crypto/hkdf.ts +304 -12
- package/std/crypto/hmac.ts +382 -54
- package/std/crypto/p256.ts +932 -61
- package/std/crypto/x25519.ts +55 -37
- package/std/crypto/x509-random.ts +71 -0
- package/std/crypto/x509.ts +20 -131
- package/std/json-tokenizer.ts +4237 -0
- package/std/json-typed.ts +1014 -0
- package/std/json-value.ts +1182 -0
- package/std/json.ts +1202 -34
- package/std/math.ts +608 -0
- package/std/net/buffer-pool.ts +151 -0
- package/std/net/hpack.ts +416 -112
- package/std/net/http-fields.ts +93 -0
- package/std/net/http1-server.ts +501 -62
- package/std/net/http1.ts +31 -6
- package/std/net/http2-tls.ts +112 -24
- package/std/net/http2.ts +333 -75
- package/std/net/http3-server.ts +261 -43
- package/std/net/http3.ts +187 -28
- package/std/net/postgres.ts +1875 -0
- package/std/net/prefork.ts +412 -0
- package/std/net/quic-listener.ts +108 -11
- package/std/net/quic-packet.ts +243 -82
- package/std/net/quic-recovery.ts +361 -30
- package/std/net/quic-stream.ts +424 -24
- package/std/net/quic.ts +519 -167
- package/std/net/router.ts +676 -0
- package/std/net/tls/codec.ts +426 -27
- package/std/net/tls/record-server.ts +478 -132
- package/std/net/tls/record.ts +133 -42
- package/std/net/tls/schedule.ts +464 -55
- package/std/net/tls/ticket.ts +610 -0
- package/std/net/tls-tcp.ts +196 -53
- package/std/net/tls.ts +1018 -260
- package/std/net/url.ts +571 -0
- package/std/random.ts +94 -0
- package/std/regex.ts +4274 -0
- package/std/sqlite.ts +391 -0
- package/std/term-io.ts +978 -0
- package/std/term-pixels.ts +874 -0
- package/std/term-probe.ts +459 -0
- package/std/term-rate.ts +993 -0
- package/std/term.ts +2453 -0
- package/std/text.ts +90 -7
- package/std/threads.ts +438 -14
- package/std/yaml-lines.ts +141 -0
- package/std/yaml.ts +4675 -0
|
@@ -0,0 +1,760 @@
|
|
|
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
|
+
* **The password is a `Secret<u8[]>`** (`nish:secret`). The key schedule and
|
|
18
|
+
* the 2^cost rounds run inside `exposeWith`; the Blowfish state (the P-array
|
|
19
|
+
* and the four S-boxes, 1042 words), the expanded key and the key-stream copy
|
|
20
|
+
* are wiped before it returns. `bcryptVerify` rebuilds the whole 60-character
|
|
21
|
+
* hash and compares it with `timingSafeEqual`.
|
|
22
|
+
*
|
|
23
|
+
* **The key is the reference's.** The bytes up to the first NUL, then one NUL,
|
|
24
|
+
* repeated until 72 bytes are taken: a password longer than 72 bytes hashes as
|
|
25
|
+
* its first 72, and bytes after an embedded NUL are not read, as under
|
|
26
|
+
* `crypt_r`; docs/security/crypto-bcrypt.md says why a longer password is cut
|
|
27
|
+
* rather than refused. Bytes above 127 are unsigned, as `$2b$` and
|
|
28
|
+
* `$2y$` define them. A `$2a$` hash gets crypt_blowfish's countermeasure for
|
|
29
|
+
* the old sign-extension bug: when an 8-bit byte was sign-extended into a key
|
|
30
|
+
* word and yet the buggy and the correct key came out equal, one bit of the
|
|
31
|
+
* first P word is flipped, as crypt_blowfish flips it, so the `$2a$` digest of
|
|
32
|
+
* such a password (`\xff\xff\xa3`) is not its `$2y$` one. It takes an 8-bit
|
|
33
|
+
* byte after the first byte of a key word with only 0xff bytes before it, every
|
|
34
|
+
* such byte in all 18 words being so placed, which UTF-8 never holds. `$2x$` (the bug itself) is refused.
|
|
35
|
+
*
|
|
36
|
+
* **Errors are `Err`s.** A salt that is not 16 bytes, a cost outside 4–31, or
|
|
37
|
+
* a hash string that is not one (the wrong length, an unknown prefix, a cost
|
|
38
|
+
* that is not two digits in range, a character outside the alphabet) is an
|
|
39
|
+
* `Err` naming it. A well-formed hash that is not this password's is
|
|
40
|
+
* `Ok(false)`, and so is one whose last salt character has bits set the
|
|
41
|
+
* reference drops, since the hash the reference makes from it differs.
|
|
42
|
+
*
|
|
43
|
+
* **The cost comes from the hash.** `bcryptVerify` runs 2^cost rounds for the
|
|
44
|
+
* cost the hash names, so a `$2b$31$` hash someone hands you takes 2^31
|
|
45
|
+
* rounds, hours of one core. Verify only hashes you stored.
|
|
46
|
+
*
|
|
47
|
+
* **Pure.** No I/O and no C, so it builds for wasm32. A `std/threads` task
|
|
48
|
+
* cannot call it yet, because it wipes (docs/security/crypto-bcrypt.md). A
|
|
49
|
+
* cost-12 hash takes a few hundred milliseconds of one core; a server runs it
|
|
50
|
+
* off its event loop (docs/guide/using-nish/password-hashing.md).
|
|
51
|
+
*
|
|
52
|
+
* The algorithm is Provos and Mazières's ("A Future-Adaptable Password
|
|
53
|
+
* Scheme", USENIX 1999). The code is adapted from Solar Designer's
|
|
54
|
+
* crypt_blowfish 1.3, which carries the notice below, and the 1042 initial
|
|
55
|
+
* words, the hexadecimal digits of π, are its `BF_init_state`. Closest
|
|
56
|
+
* upstream for each part: `bcryptInitState` is `BF_init_state`;
|
|
57
|
+
* `bcryptEncrypt` and the rounds in `bcryptBody` are `BF_ENCRYPT`/`BF_ROUND`;
|
|
58
|
+
* `bcryptBody` is `BF_body`; `bcryptSetKey` is `BF_set_key`; `bcryptDecodeSalt`
|
|
59
|
+
* and `bcryptEncode` are `BF_decode` and `BF_encode`; `bcryptExposed` is the
|
|
60
|
+
* part of `BF_crypt` after the setting is parsed, and `bcryptParse` that
|
|
61
|
+
* parse. The state is one flat `u32[]` with P first (words 0–17) and the
|
|
62
|
+
* S-boxes after (18–1041), so the body walks it in one pass, and every S-box
|
|
63
|
+
* index is an `i32` masked to 0–255, which the checker proves in bounds
|
|
64
|
+
* (`BCRYPT_STATE_WORDS`). The magic-string encryption, the parsing and the
|
|
65
|
+
* alphabet are separate functions so a several-lane kernel can share them.
|
|
66
|
+
*
|
|
67
|
+
* Names carry the `bcrypt` prefix because a `std/` module's private functions
|
|
68
|
+
* share the importing program's flat symbol namespace
|
|
69
|
+
* (`docs/wp26-stdlib.md` §3e).
|
|
70
|
+
*
|
|
71
|
+
* Adapted from crypt_blowfish (https://www.openwall.com/crypt/), crypt_blowfish.c.
|
|
72
|
+
* Written by Solar Designer, 1998-2014, and placed in the public domain, with a
|
|
73
|
+
* permissive licence as the fallback; see std/crypto/LICENSE-crypt_blowfish.
|
|
74
|
+
*
|
|
75
|
+
* crypt_blowfish's notice, kept as it asks:
|
|
76
|
+
*
|
|
77
|
+
* Written by Solar Designer <solar at openwall.com> in 1998-2014.
|
|
78
|
+
* No copyright is claimed, and the software is hereby placed in the public
|
|
79
|
+
* domain. In case this attempt to disclaim copyright and place the software
|
|
80
|
+
* in the public domain is deemed null and void, then the software is
|
|
81
|
+
* Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
|
|
82
|
+
* general public under the following terms:
|
|
83
|
+
*
|
|
84
|
+
* Redistribution and use in source and binary forms, with or without
|
|
85
|
+
* modification, are permitted.
|
|
86
|
+
*
|
|
87
|
+
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
|
88
|
+
*
|
|
89
|
+
* It is my intent that you should be able to use this on your system,
|
|
90
|
+
* as part of a software package, or anywhere else to improve security,
|
|
91
|
+
* ensure compatibility, or for any other purpose. I would appreciate
|
|
92
|
+
* it if you give credit where it is due and keep your modifications in
|
|
93
|
+
* the public domain as well, but I don't require that in order to let
|
|
94
|
+
* you place this code and any modifications you make under a license
|
|
95
|
+
* of your choice.
|
|
96
|
+
*
|
|
97
|
+
* This implementation is fully compatible with OpenBSD's bcrypt.c for prefix
|
|
98
|
+
* "$2b$", originally by Niels Provos <provos at citi.umich.edu>, and it uses
|
|
99
|
+
* some of his ideas. The password hashing algorithm was designed by David
|
|
100
|
+
* Mazieres <dm at lcs.mit.edu>.
|
|
101
|
+
*/
|
|
102
|
+
|
|
103
|
+
import { timingSafeEqual } from "nish/crypto/ct"
|
|
104
|
+
import { Secret, exposeWith, wipe } from "nish:secret"
|
|
105
|
+
|
|
106
|
+
/** The length of a bcrypt hash string: `$2b$`, two cost digits, `$`, 22 salt and 31 digest characters. */
|
|
107
|
+
export const BCRYPT_HASH_LENGTH: i32 = 60
|
|
108
|
+
|
|
109
|
+
/** The length in bytes of a bcrypt salt. */
|
|
110
|
+
export const BCRYPT_SALT_SIZE: i32 = 16
|
|
111
|
+
|
|
112
|
+
/** The smallest cost the reference accepts: 2^4 rounds. */
|
|
113
|
+
export const BCRYPT_MIN_COST: i32 = 4
|
|
114
|
+
|
|
115
|
+
/** The largest cost: 2^31 rounds. */
|
|
116
|
+
export const BCRYPT_MAX_COST: i32 = 31
|
|
117
|
+
|
|
118
|
+
/** The number of password bytes the key schedule reads; the rest are not. */
|
|
119
|
+
export const BCRYPT_MAX_PASSWORD: i32 = 72
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The state's words: P (18) then S0–S3 (4 × 256). The guards before the
|
|
123
|
+
* rounds and the key copy write 1042, 72 (`BCRYPT_MAX_PASSWORD`) and 16 as
|
|
124
|
+
* literals: the range prover reads a literal bound, not a named constant, and
|
|
125
|
+
* keeps every S-box bounds check without it.
|
|
126
|
+
*/
|
|
127
|
+
const BCRYPT_STATE_WORDS: i32 = 1042
|
|
128
|
+
|
|
129
|
+
/** The length of the setting, `$2b$NN$` and the 22 salt characters. */
|
|
130
|
+
const BCRYPT_SETTING_LENGTH: i32 = 29
|
|
131
|
+
|
|
132
|
+
/** What the hash needs besides the password, as one argument, since `exposeWith` hands on only one. */
|
|
133
|
+
interface BcryptSetting {
|
|
134
|
+
/** The salt as four big-endian words. */
|
|
135
|
+
salt: u32[]
|
|
136
|
+
/** log2 of the round count, 4 to 31. */
|
|
137
|
+
cost: i32
|
|
138
|
+
/** 0x10000 for `$2a$`, whose countermeasure it enables; 0 for `$2b$` and `$2y$`. */
|
|
139
|
+
safety: u32
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** A parsed hash string: its setting and the 29 characters the rebuilt hash starts with. */
|
|
143
|
+
interface BcryptParsed {
|
|
144
|
+
setting: BcryptSetting
|
|
145
|
+
prefix: u8[]
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* All ones when `lo <= c <= hi`, else zero, without a branch: the encoding
|
|
150
|
+
* reads the digest, which is the stored hash when the password is right. The
|
|
151
|
+
* sums are in `u32`, as in `base64urlRangeMask` (std/crypto/base64url.ts), so
|
|
152
|
+
* no overflow check branches on them.
|
|
153
|
+
*/
|
|
154
|
+
const bcryptRangeMask = (c: i32, lo: i32, hi: i32): i32 =>
|
|
155
|
+
toI32((toU32(lo) - 1 - toU32(c)) & (toU32(c) - toU32(hi) - 1)) >> 31
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* The bcrypt alphabet's character for the sextet `v`, `./A-Za-z0-9`: `.` + `v`,
|
|
159
|
+
* plus the step to each range `v` has reached (2 lands on `A`, 28 on `a`, 54
|
|
160
|
+
* on `0`).
|
|
161
|
+
*/
|
|
162
|
+
const bcryptCharOf = (v: i32): u8 =>
|
|
163
|
+
toU8(
|
|
164
|
+
46 +
|
|
165
|
+
toU32(v) +
|
|
166
|
+
toU32(bcryptRangeMask(v, 2, 63) & 17) +
|
|
167
|
+
toU32(bcryptRangeMask(v, 28, 63) & 6) -
|
|
168
|
+
toU32(bcryptRangeMask(v, 54, 63) & 75)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
/** The sextet of a bcrypt alphabet character, or -1 for any other byte, without a branch. */
|
|
172
|
+
const bcryptSextetOf = (c: i32): i32 => {
|
|
173
|
+
const punct: i32 = bcryptRangeMask(c, 46, 47)
|
|
174
|
+
const upper: i32 = bcryptRangeMask(c, 65, 90)
|
|
175
|
+
const lower: i32 = bcryptRangeMask(c, 97, 122)
|
|
176
|
+
const digit: i32 = bcryptRangeMask(c, 48, 57)
|
|
177
|
+
const value: i32 =
|
|
178
|
+
(punct & toI32(toU32(c) - 46)) |
|
|
179
|
+
(upper & toI32(toU32(c) - 63)) |
|
|
180
|
+
(lower & toI32(toU32(c) - 69)) |
|
|
181
|
+
(digit & toI32(toU32(c) + 6))
|
|
182
|
+
return value | ~(punct | upper | lower | digit)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* `BF_encode`: the first `n` bytes of `src` as bcrypt base64, three bytes to
|
|
187
|
+
* four characters, and a one- or two-byte tail to two or three.
|
|
188
|
+
*/
|
|
189
|
+
const bcryptEncode = (src: u8[], n: i32): u8[] => {
|
|
190
|
+
const out: u8[] = new Array<u8>((n * 4 + 2) / 3)
|
|
191
|
+
let i: i32 = 0
|
|
192
|
+
let o: i32 = 0
|
|
193
|
+
while (i + 2 < n && i + 2 < toI32(src.length) && o + 3 < toI32(out.length)) {
|
|
194
|
+
const c1: i32 = toI32(src[i])
|
|
195
|
+
const c2: i32 = toI32(src[i + 1])
|
|
196
|
+
const c3: i32 = toI32(src[i + 2])
|
|
197
|
+
out[o] = bcryptCharOf(c1 >> 2)
|
|
198
|
+
out[o + 1] = bcryptCharOf(((c1 & 0x03) << 4) | (c2 >> 4))
|
|
199
|
+
out[o + 2] = bcryptCharOf(((c2 & 0x0f) << 2) | (c3 >> 6))
|
|
200
|
+
out[o + 3] = bcryptCharOf(c3 & 0x3f)
|
|
201
|
+
i += 3
|
|
202
|
+
o += 4
|
|
203
|
+
}
|
|
204
|
+
if (i >= 0 && i < n && i < toI32(src.length) && o >= 0 && o + 1 < toI32(out.length)) {
|
|
205
|
+
const c1: i32 = toI32(src[i])
|
|
206
|
+
out[o] = bcryptCharOf(c1 >> 2)
|
|
207
|
+
if (i + 1 < n && i + 1 < toI32(src.length) && o + 2 < toI32(out.length)) {
|
|
208
|
+
const c2: i32 = toI32(src[i + 1])
|
|
209
|
+
out[o + 1] = bcryptCharOf(((c1 & 0x03) << 4) | (c2 >> 4))
|
|
210
|
+
out[o + 2] = bcryptCharOf((c2 & 0x0f) << 2)
|
|
211
|
+
} else {
|
|
212
|
+
out[o + 1] = bcryptCharOf((c1 & 0x03) << 4)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return out
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** `src` copied into `dst` from `at`, as far as `dst` reaches. */
|
|
219
|
+
const bcryptPut = (dst: u8[], at: i32, src: u8[]): void => {
|
|
220
|
+
for (let k: i32 = 0; k < toI32(src.length); k++) {
|
|
221
|
+
const o: i32 = at + k
|
|
222
|
+
if (o >= 0 && o < toI32(dst.length)) {
|
|
223
|
+
dst[o] = src[k]
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* `BF_decode` of the 22 salt characters of `text` from `at`: the 16 bytes as
|
|
230
|
+
* four big-endian words, or `null` if a character is outside the alphabet.
|
|
231
|
+
* The 22nd character gives its top two bits only.
|
|
232
|
+
*/
|
|
233
|
+
const bcryptDecodeSalt = (text: string, at: i32): u32[] | null => {
|
|
234
|
+
const sx: i32[] = new Array<i32>(22)
|
|
235
|
+
for (let k: i32 = 0; k < toI32(sx.length); k++) {
|
|
236
|
+
const p: i32 = at + k
|
|
237
|
+
if (p < 0 || p >= toI32(text.length)) {
|
|
238
|
+
return null
|
|
239
|
+
}
|
|
240
|
+
const v: i32 = bcryptSextetOf(toI32(text.charCodeAt(p)))
|
|
241
|
+
if (v < 0) {
|
|
242
|
+
return null
|
|
243
|
+
}
|
|
244
|
+
sx[k] = v
|
|
245
|
+
}
|
|
246
|
+
const bytes: u8[] = new Array<u8>(BCRYPT_SALT_SIZE)
|
|
247
|
+
let s: i32 = 0
|
|
248
|
+
let d: i32 = 0
|
|
249
|
+
while (s + 3 < toI32(sx.length) && d + 2 < toI32(bytes.length)) {
|
|
250
|
+
const c2: i32 = sx[s + 1]
|
|
251
|
+
const c3: i32 = sx[s + 2]
|
|
252
|
+
bytes[d] = toU8((sx[s] << 2) | ((c2 & 0x30) >> 4))
|
|
253
|
+
bytes[d + 1] = toU8(((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2))
|
|
254
|
+
bytes[d + 2] = toU8(((c3 & 0x03) << 6) | sx[s + 3])
|
|
255
|
+
s += 4
|
|
256
|
+
d += 3
|
|
257
|
+
}
|
|
258
|
+
bytes[15] = toU8((sx[20] << 2) | ((sx[21] & 0x30) >> 4))
|
|
259
|
+
return bcryptSaltWords(bytes)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Sixteen salt bytes as four big-endian words (`BF_swap`). */
|
|
263
|
+
const bcryptSaltWords = (bytes: u8[]): u32[] => {
|
|
264
|
+
const words: u32[] = new Array<u32>(4)
|
|
265
|
+
let w: i32 = 0
|
|
266
|
+
for (let b: i32 = 0; b + 3 < toI32(bytes.length) && w < toI32(words.length); b += 4) {
|
|
267
|
+
words[w] =
|
|
268
|
+
(toU32(bytes[b]) << 24) | (toU32(bytes[b + 1]) << 16) | (toU32(bytes[b + 2]) << 8) | toU32(bytes[b + 3])
|
|
269
|
+
w++
|
|
270
|
+
}
|
|
271
|
+
return words
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** `$2b$NN$` and the 22 salt characters for a cost and 16 salt bytes. */
|
|
275
|
+
const bcryptSettingOf = (salt: u8[], cost: i32): u8[] => {
|
|
276
|
+
const out: u8[] = new Array<u8>(BCRYPT_SETTING_LENGTH)
|
|
277
|
+
out[0] = 36
|
|
278
|
+
out[1] = 50
|
|
279
|
+
out[2] = 98
|
|
280
|
+
out[3] = 36
|
|
281
|
+
out[4] = toU8(cost / 10 + 48)
|
|
282
|
+
out[5] = toU8((cost % 10) + 48)
|
|
283
|
+
out[6] = 36
|
|
284
|
+
bcryptPut(out, 7, bcryptEncode(salt, BCRYPT_SALT_SIZE))
|
|
285
|
+
return out
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* `BF_crypt`'s parse of a hash or setting: `$2a$`, `$2b$` or `$2y$`, two cost
|
|
290
|
+
* digits from 04 to 31, `$` and 22 salt characters. The prefix it answers has
|
|
291
|
+
* the last salt character canonical, its low four bits cleared, as the
|
|
292
|
+
* reference writes it.
|
|
293
|
+
*/
|
|
294
|
+
const bcryptParse = (text: string): Result<BcryptParsed, string> => {
|
|
295
|
+
if (toI32(text.length) < BCRYPT_SETTING_LENGTH) {
|
|
296
|
+
return Err("bcrypt: hash too short")
|
|
297
|
+
}
|
|
298
|
+
const minor: i32 = toI32(text.charCodeAt(2))
|
|
299
|
+
if (
|
|
300
|
+
toI32(text.charCodeAt(0)) !== 36 ||
|
|
301
|
+
toI32(text.charCodeAt(1)) !== 50 ||
|
|
302
|
+
toI32(text.charCodeAt(3)) !== 36 ||
|
|
303
|
+
(minor !== 97 && minor !== 98 && minor !== 121)
|
|
304
|
+
) {
|
|
305
|
+
return Err("bcrypt: not a $2a$, $2b$ or $2y$ hash")
|
|
306
|
+
}
|
|
307
|
+
const d1: i32 = toI32(text.charCodeAt(4)) - 48
|
|
308
|
+
const d2: i32 = toI32(text.charCodeAt(5)) - 48
|
|
309
|
+
if (d1 < 0 || d1 > 9 || d2 < 0 || d2 > 9 || toI32(text.charCodeAt(6)) !== 36) {
|
|
310
|
+
return Err("bcrypt: the cost is not two digits")
|
|
311
|
+
}
|
|
312
|
+
const cost: i32 = d1 * 10 + d2
|
|
313
|
+
if (cost < BCRYPT_MIN_COST || cost > BCRYPT_MAX_COST) {
|
|
314
|
+
return Err("bcrypt: the cost is outside 04 to 31")
|
|
315
|
+
}
|
|
316
|
+
const salt: u32[] | null = bcryptDecodeSalt(text, 7)
|
|
317
|
+
if (salt === null) {
|
|
318
|
+
return Err("bcrypt: the salt is not bcrypt base64")
|
|
319
|
+
}
|
|
320
|
+
const prefix: u8[] = new Array<u8>(BCRYPT_SETTING_LENGTH)
|
|
321
|
+
for (let i: i32 = 0; i + 1 < toI32(prefix.length) && i < toI32(text.length); i++) {
|
|
322
|
+
prefix[i] = toU8(text.charCodeAt(i))
|
|
323
|
+
}
|
|
324
|
+
prefix[28] = bcryptCharOf(bcryptSextetOf(toI32(text.charCodeAt(28))) & 0x30)
|
|
325
|
+
const safety: u32 = minor === 97 ? 0x10000 : 0
|
|
326
|
+
const setting: BcryptSetting = { salt: salt, cost: cost, safety: safety }
|
|
327
|
+
const parsed: BcryptParsed = { setting: setting, prefix: prefix }
|
|
328
|
+
return Ok(parsed)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* One Blowfish block, `lr[0]` and `lr[1]` in place (`BF_ENCRYPT`), under the
|
|
333
|
+
* state `st`. The rounds are `bcryptBody`'s, which repeats them inline.
|
|
334
|
+
*/
|
|
335
|
+
const bcryptEncrypt = (st: u32[], lr: u32[]): void => {
|
|
336
|
+
if (st.length < 1042) {
|
|
337
|
+
return
|
|
338
|
+
}
|
|
339
|
+
if (lr.length < 2) {
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
let l: u32 = lr[0] ^ st[0]
|
|
343
|
+
let r: u32 = lr[1]
|
|
344
|
+
for (let n: i32 = 1; n < 17; n += 2) {
|
|
345
|
+
const la: i32 = toI32(l >>> 24) & 0xff
|
|
346
|
+
const lb: i32 = toI32(l >>> 16) & 0xff
|
|
347
|
+
const lc: i32 = toI32(l >>> 8) & 0xff
|
|
348
|
+
const ld: i32 = toI32(l) & 0xff
|
|
349
|
+
r ^= st[n] ^ (((st[18 + la] + st[274 + lb]) ^ st[530 + lc]) + st[786 + ld])
|
|
350
|
+
const ra: i32 = toI32(r >>> 24) & 0xff
|
|
351
|
+
const rb: i32 = toI32(r >>> 16) & 0xff
|
|
352
|
+
const rc: i32 = toI32(r >>> 8) & 0xff
|
|
353
|
+
const rd: i32 = toI32(r) & 0xff
|
|
354
|
+
l ^= st[n + 1] ^ (((st[18 + ra] + st[274 + rb]) ^ st[530 + rc]) + st[786 + rd])
|
|
355
|
+
}
|
|
356
|
+
lr[0] = r ^ st[17]
|
|
357
|
+
lr[1] = l
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* `BF_body`: encrypt the zero block, then each result in turn, writing the
|
|
362
|
+
* pairs over the whole state, P first and then the S-boxes. The hot loop:
|
|
363
|
+
* 2^(cost+1) calls of 521 blocks each.
|
|
364
|
+
*/
|
|
365
|
+
const bcryptBody = (st: u32[]): void => {
|
|
366
|
+
if (st.length < 1042) {
|
|
367
|
+
return
|
|
368
|
+
}
|
|
369
|
+
let l: u32 = 0
|
|
370
|
+
let r: u32 = 0
|
|
371
|
+
for (let i: i32 = 0; i + 1 < toI32(st.length); i += 2) {
|
|
372
|
+
l ^= st[0]
|
|
373
|
+
for (let n: i32 = 1; n < 17; n += 2) {
|
|
374
|
+
const la: i32 = toI32(l >>> 24) & 0xff
|
|
375
|
+
const lb: i32 = toI32(l >>> 16) & 0xff
|
|
376
|
+
const lc: i32 = toI32(l >>> 8) & 0xff
|
|
377
|
+
const ld: i32 = toI32(l) & 0xff
|
|
378
|
+
r ^= st[n] ^ (((st[18 + la] + st[274 + lb]) ^ st[530 + lc]) + st[786 + ld])
|
|
379
|
+
const ra: i32 = toI32(r >>> 24) & 0xff
|
|
380
|
+
const rb: i32 = toI32(r >>> 16) & 0xff
|
|
381
|
+
const rc: i32 = toI32(r >>> 8) & 0xff
|
|
382
|
+
const rd: i32 = toI32(r) & 0xff
|
|
383
|
+
l ^= st[n + 1] ^ (((st[18 + ra] + st[274 + rb]) ^ st[530 + rc]) + st[786 + rd])
|
|
384
|
+
}
|
|
385
|
+
const t: u32 = r
|
|
386
|
+
r = l
|
|
387
|
+
l = t ^ st[17]
|
|
388
|
+
st[i] = l
|
|
389
|
+
st[i + 1] = r
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* `BF_set_key`: the 18 expanded-key words into `expanded`, and P XORed with
|
|
395
|
+
* them, for `password` read as its bytes to the first NUL and one NUL,
|
|
396
|
+
* repeated. `safety` is 0x10000 for `$2a$` and 0 otherwise: for `$2a$` the
|
|
397
|
+
* key is also built with each byte sign-extended, as the old bug built it,
|
|
398
|
+
* and if an 8-bit byte was sign-extended into a word (`sign`) yet the two
|
|
399
|
+
* keys came out equal (`diff` is 0), bit 16 of P[0] is flipped, as
|
|
400
|
+
* crypt_blowfish flips it. All of it is computed without a branch
|
|
401
|
+
* on a password byte: only the copy's length and the cycle position, which
|
|
402
|
+
* follow the password's length and NULs, decide a load.
|
|
403
|
+
*/
|
|
404
|
+
const bcryptSetKey = (st: u32[], expanded: u32[], password: u8[], safety: u32): void => {
|
|
405
|
+
if (st.length < 1042) {
|
|
406
|
+
return
|
|
407
|
+
}
|
|
408
|
+
if (expanded.length < 18) {
|
|
409
|
+
return
|
|
410
|
+
}
|
|
411
|
+
// The key stream: at most 72 bytes of the password and zeros after, so the
|
|
412
|
+
// cycle below reads a NUL at 72 at the latest without testing the length.
|
|
413
|
+
// It is 128 long so that the position, masked to 7 bits, needs no check.
|
|
414
|
+
const key: u8[] = new Array<u8>(128)
|
|
415
|
+
for (let i: i32 = 0; i < toI32(password.length) && i < 72; i++) {
|
|
416
|
+
key[i] = password[i]
|
|
417
|
+
}
|
|
418
|
+
let p: i32 = 0
|
|
419
|
+
let sign: u32 = 0
|
|
420
|
+
let diff: u32 = 0
|
|
421
|
+
for (let i: i32 = 0; i < 18; i++) {
|
|
422
|
+
let correct: u32 = 0
|
|
423
|
+
let buggy: u32 = 0
|
|
424
|
+
for (let j: i32 = 0; j < 4; j++) {
|
|
425
|
+
const q: i32 = p & 0x7f
|
|
426
|
+
const c: i32 = toI32(key[q])
|
|
427
|
+
correct = (correct << 8) | toU32(c)
|
|
428
|
+
buggy = (buggy << 8) | ((toU32(c) ^ 0x80) - 0x80)
|
|
429
|
+
if (j !== 0) {
|
|
430
|
+
sign |= buggy & 0x80
|
|
431
|
+
}
|
|
432
|
+
// The next position, or 0 after a NUL: 1 if c is nonzero, else 0.
|
|
433
|
+
p = (p + 1) * ((c | -c) >>> 31)
|
|
434
|
+
}
|
|
435
|
+
diff |= correct ^ buggy
|
|
436
|
+
expanded[i] = correct
|
|
437
|
+
st[i] ^= correct
|
|
438
|
+
}
|
|
439
|
+
diff |= diff >>> 16
|
|
440
|
+
diff &= 0xffff
|
|
441
|
+
diff += 0xffff
|
|
442
|
+
sign <<= 9
|
|
443
|
+
sign &= ~diff & safety
|
|
444
|
+
st[0] ^= sign
|
|
445
|
+
wipe(key)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/** The state Blowfish starts from: P then S0–S3, the digits of π (`BF_init_state`). */
|
|
449
|
+
const bcryptInitState = (): u32[] => {
|
|
450
|
+
const st: u32[] = [
|
|
451
|
+
// P
|
|
452
|
+
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
|
|
453
|
+
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
|
|
454
|
+
0x9216d5d9, 0x8979fb1b,
|
|
455
|
+
// S0
|
|
456
|
+
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
|
|
457
|
+
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
|
|
458
|
+
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
|
|
459
|
+
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
|
|
460
|
+
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
|
|
461
|
+
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
|
|
462
|
+
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
|
|
463
|
+
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
|
|
464
|
+
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
|
|
465
|
+
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
|
|
466
|
+
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
|
|
467
|
+
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
|
|
468
|
+
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
|
|
469
|
+
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
|
|
470
|
+
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
|
|
471
|
+
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
|
|
472
|
+
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
|
|
473
|
+
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
|
|
474
|
+
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
|
|
475
|
+
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
|
|
476
|
+
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
|
|
477
|
+
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
|
|
478
|
+
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
|
|
479
|
+
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
|
|
480
|
+
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
|
|
481
|
+
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
|
|
482
|
+
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
|
|
483
|
+
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
|
|
484
|
+
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
|
|
485
|
+
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
|
|
486
|
+
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
|
|
487
|
+
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
|
|
488
|
+
// S1
|
|
489
|
+
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
|
|
490
|
+
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
|
|
491
|
+
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
|
|
492
|
+
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
|
|
493
|
+
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
|
|
494
|
+
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
|
|
495
|
+
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
|
|
496
|
+
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
|
|
497
|
+
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
|
|
498
|
+
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
|
|
499
|
+
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
|
|
500
|
+
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
|
|
501
|
+
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
|
|
502
|
+
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
|
|
503
|
+
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
|
|
504
|
+
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
|
|
505
|
+
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
|
|
506
|
+
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
|
|
507
|
+
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
|
|
508
|
+
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
|
|
509
|
+
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
|
|
510
|
+
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
|
|
511
|
+
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
|
|
512
|
+
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
|
|
513
|
+
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
|
|
514
|
+
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
|
|
515
|
+
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
|
|
516
|
+
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
|
|
517
|
+
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
|
|
518
|
+
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
|
|
519
|
+
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
|
|
520
|
+
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
|
|
521
|
+
// S2
|
|
522
|
+
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
|
|
523
|
+
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
|
|
524
|
+
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
|
|
525
|
+
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
|
|
526
|
+
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
|
|
527
|
+
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
|
|
528
|
+
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
|
|
529
|
+
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
|
|
530
|
+
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
|
|
531
|
+
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
|
|
532
|
+
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
|
|
533
|
+
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
|
|
534
|
+
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
|
|
535
|
+
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
|
|
536
|
+
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
|
|
537
|
+
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
|
|
538
|
+
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
|
|
539
|
+
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
|
|
540
|
+
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
|
|
541
|
+
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
|
|
542
|
+
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
|
|
543
|
+
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
|
|
544
|
+
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
|
|
545
|
+
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
|
|
546
|
+
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
|
|
547
|
+
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
|
|
548
|
+
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
|
|
549
|
+
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
|
|
550
|
+
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
|
|
551
|
+
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
|
|
552
|
+
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
|
|
553
|
+
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
|
|
554
|
+
// S3
|
|
555
|
+
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
|
|
556
|
+
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
|
|
557
|
+
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
|
|
558
|
+
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
|
|
559
|
+
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
|
|
560
|
+
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
|
|
561
|
+
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
|
|
562
|
+
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
|
|
563
|
+
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
|
|
564
|
+
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
|
|
565
|
+
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
|
|
566
|
+
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
|
|
567
|
+
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
|
|
568
|
+
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
|
|
569
|
+
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
|
|
570
|
+
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
|
|
571
|
+
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
|
|
572
|
+
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
|
|
573
|
+
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
|
|
574
|
+
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
|
|
575
|
+
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
|
|
576
|
+
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
|
|
577
|
+
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
|
|
578
|
+
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
|
|
579
|
+
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
|
|
580
|
+
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
|
|
581
|
+
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
|
|
582
|
+
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
|
|
583
|
+
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
|
|
584
|
+
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
|
|
585
|
+
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
|
|
586
|
+
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
|
|
587
|
+
]
|
|
588
|
+
return st
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** The block `lr` into `st` at `at` and `at + 1`. */
|
|
592
|
+
const bcryptStore = (st: u32[], at: i32, lr: u32[]): void => {
|
|
593
|
+
if (lr.length < 2) {
|
|
594
|
+
return
|
|
595
|
+
}
|
|
596
|
+
if (at >= 0 && at + 1 < toI32(st.length)) {
|
|
597
|
+
st[at] = lr[0]
|
|
598
|
+
st[at + 1] = lr[1]
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/** P XORed with the expanded key: the first half of a cost round. */
|
|
603
|
+
const bcryptXorKey = (st: u32[], expanded: u32[]): void => {
|
|
604
|
+
if (st.length < 18) {
|
|
605
|
+
return
|
|
606
|
+
}
|
|
607
|
+
if (expanded.length < 18) {
|
|
608
|
+
return
|
|
609
|
+
}
|
|
610
|
+
for (let i: i32 = 0; i < 18; i++) {
|
|
611
|
+
st[i] ^= expanded[i]
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/** P XORed with the salt words, repeated: the second half of a cost round. */
|
|
616
|
+
const bcryptXorSalt = (st: u32[], s0: u32, s1: u32, s2: u32, s3: u32): void => {
|
|
617
|
+
if (st.length < 18) {
|
|
618
|
+
return
|
|
619
|
+
}
|
|
620
|
+
for (let i: i32 = 0; i + 3 < 16; i += 4) {
|
|
621
|
+
st[i] ^= s0
|
|
622
|
+
st[i + 1] ^= s1
|
|
623
|
+
st[i + 2] ^= s2
|
|
624
|
+
st[i + 3] ^= s3
|
|
625
|
+
}
|
|
626
|
+
st[16] ^= s0
|
|
627
|
+
st[17] ^= s1
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* The hash itself, for `exposeWith` to run on the plain password: the 31
|
|
632
|
+
* digest characters (`BF_crypt` from the key schedule on). The state, the
|
|
633
|
+
* expanded key and the block are wiped before it returns.
|
|
634
|
+
*/
|
|
635
|
+
const bcryptExposed = (password: u8[], setting: BcryptSetting): u8[] => {
|
|
636
|
+
const salt: u32[] = setting.salt
|
|
637
|
+
if (salt.length < 4) {
|
|
638
|
+
return new Array<u8>(31)
|
|
639
|
+
}
|
|
640
|
+
const s0: u32 = salt[0]
|
|
641
|
+
const s1: u32 = salt[1]
|
|
642
|
+
const s2: u32 = salt[2]
|
|
643
|
+
const s3: u32 = salt[3]
|
|
644
|
+
const st: u32[] = bcryptInitState()
|
|
645
|
+
const expanded: u32[] = new Array<u32>(18)
|
|
646
|
+
const lr: u32[] = new Array<u32>(2)
|
|
647
|
+
bcryptSetKey(st, expanded, password, setting.safety)
|
|
648
|
+
|
|
649
|
+
// The salted expansion: each block is XORed with a salt half before it is
|
|
650
|
+
// encrypted, the first half and the second in turn (`BF_crypt`'s two loops).
|
|
651
|
+
for (let i: i32 = 0; i < BCRYPT_STATE_WORDS; i += 2) {
|
|
652
|
+
const odd: boolean = (i & 2) !== 0
|
|
653
|
+
lr[0] ^= odd ? s2 : s0
|
|
654
|
+
lr[1] ^= odd ? s3 : s1
|
|
655
|
+
bcryptEncrypt(st, lr)
|
|
656
|
+
bcryptStore(st, i, lr)
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// 2^cost rounds of the key, then the salt, re-keying the whole state.
|
|
660
|
+
let rounds: u32 = toU32(1) << toU32(setting.cost)
|
|
661
|
+
while (rounds !== 0) {
|
|
662
|
+
bcryptXorKey(st, expanded)
|
|
663
|
+
bcryptBody(st)
|
|
664
|
+
bcryptXorSalt(st, s0, s1, s2, s3)
|
|
665
|
+
bcryptBody(st)
|
|
666
|
+
rounds -= 1
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// "OrpheanBeholderScryDoubt", each pair encrypted 64 times, big-endian.
|
|
670
|
+
const digest: u8[] = new Array<u8>(24)
|
|
671
|
+
bcryptMagic(st, lr, digest, 0, 0x4f727068, 0x65616e42)
|
|
672
|
+
bcryptMagic(st, lr, digest, 8, 0x65686f6c, 0x64657253)
|
|
673
|
+
bcryptMagic(st, lr, digest, 16, 0x63727944, 0x6f756274)
|
|
674
|
+
// 23 of the 24 bytes, as the reference encodes them.
|
|
675
|
+
const out: u8[] = bcryptEncode(digest, 23)
|
|
676
|
+
wipe(st)
|
|
677
|
+
wipe(expanded)
|
|
678
|
+
wipe(lr)
|
|
679
|
+
wipe(digest)
|
|
680
|
+
return out
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/** One pair of the magic string, encrypted 64 times under `st`, into `dst` at `at` as eight big-endian bytes. */
|
|
684
|
+
const bcryptMagic = (st: u32[], lr: u32[], dst: u8[], at: i32, l: u32, r: u32): void => {
|
|
685
|
+
lr[0] = l
|
|
686
|
+
lr[1] = r
|
|
687
|
+
for (let k: i32 = 0; k < 64; k++) {
|
|
688
|
+
bcryptEncrypt(st, lr)
|
|
689
|
+
}
|
|
690
|
+
if (at < 0 || at + 7 >= toI32(dst.length)) {
|
|
691
|
+
return
|
|
692
|
+
}
|
|
693
|
+
const l2: u32 = lr[0]
|
|
694
|
+
const r2: u32 = lr[1]
|
|
695
|
+
dst[at] = toU8(l2 >>> 24)
|
|
696
|
+
dst[at + 1] = toU8((l2 >>> 16) & 0xff)
|
|
697
|
+
dst[at + 2] = toU8((l2 >>> 8) & 0xff)
|
|
698
|
+
dst[at + 3] = toU8(l2 & 0xff)
|
|
699
|
+
dst[at + 4] = toU8(r2 >>> 24)
|
|
700
|
+
dst[at + 5] = toU8((r2 >>> 16) & 0xff)
|
|
701
|
+
dst[at + 6] = toU8((r2 >>> 8) & 0xff)
|
|
702
|
+
dst[at + 7] = toU8(r2 & 0xff)
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/** The 60 bytes of the hash: the 29-byte setting, then the digest the password gives under it. */
|
|
706
|
+
const bcryptRun = (password: Secret<u8[]>, prefix: u8[], setting: BcryptSetting): u8[] => {
|
|
707
|
+
const digest: u8[] = exposeWith(password, setting, bcryptExposed)
|
|
708
|
+
const out: u8[] = new Array<u8>(BCRYPT_HASH_LENGTH)
|
|
709
|
+
bcryptPut(out, 0, prefix)
|
|
710
|
+
bcryptPut(out, BCRYPT_SETTING_LENGTH, digest)
|
|
711
|
+
return out
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* The `$2b$` hash of `password` with a 16-byte `salt` and 2^`cost` rounds:
|
|
716
|
+
* 60 characters, `$2b$`, the two-digit cost, `$`, the salt and the digest in
|
|
717
|
+
* bcrypt's base64. The salt must come from a CSPRNG and be fresh for each
|
|
718
|
+
* hash. An `Err` for a salt of another length or a cost outside 4–31.
|
|
719
|
+
*/
|
|
720
|
+
export const bcryptHash = (password: Secret<u8[]>, salt: u8[], cost: i32): Result<string, string> => {
|
|
721
|
+
if (salt.length !== 16) {
|
|
722
|
+
return Err("bcrypt: the salt is not 16 bytes")
|
|
723
|
+
}
|
|
724
|
+
if (cost < BCRYPT_MIN_COST || cost > BCRYPT_MAX_COST) {
|
|
725
|
+
return Err("bcrypt: the cost is outside 4 to 31")
|
|
726
|
+
}
|
|
727
|
+
const setting: BcryptSetting = { salt: bcryptSaltWords(salt), cost: cost, safety: toU32(0) }
|
|
728
|
+
const out: u8[] = bcryptRun(password, bcryptSettingOf(salt, cost), setting)
|
|
729
|
+
return Ok(String.fromBytes(out, 0, BCRYPT_HASH_LENGTH))
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Whether `hash` is `password`'s: `Ok(true)` or `Ok(false)`, decided by a
|
|
734
|
+
* constant-time compare of all 60 characters with the hash rebuilt from the
|
|
735
|
+
* password and `hash`'s own prefix, salt and cost. `$2a$`, `$2b$` and `$2y$`
|
|
736
|
+
* are read; an `Err` names what makes `hash` not a bcrypt hash.
|
|
737
|
+
*/
|
|
738
|
+
export const bcryptVerify = (password: Secret<u8[]>, hash: string): Result<boolean, string> => {
|
|
739
|
+
if (hash.length !== 60) {
|
|
740
|
+
return Err("bcrypt: a hash is 60 characters")
|
|
741
|
+
}
|
|
742
|
+
const parsed: Result<BcryptParsed, string> = bcryptParse(hash)
|
|
743
|
+
if (parsed.isErr()) {
|
|
744
|
+
return Err(parsed.error)
|
|
745
|
+
}
|
|
746
|
+
const given: u8[] = new Array<u8>(BCRYPT_HASH_LENGTH)
|
|
747
|
+
let alphabet: i32 = 0
|
|
748
|
+
for (let i: i32 = 0; i < toI32(given.length) && i < toI32(hash.length); i++) {
|
|
749
|
+
const c: i32 = toI32(hash.charCodeAt(i))
|
|
750
|
+
if (i >= BCRYPT_SETTING_LENGTH) {
|
|
751
|
+
alphabet |= bcryptSextetOf(c)
|
|
752
|
+
}
|
|
753
|
+
given[i] = toU8(c)
|
|
754
|
+
}
|
|
755
|
+
if (alphabet < 0) {
|
|
756
|
+
return Err("bcrypt: the digest is not bcrypt base64")
|
|
757
|
+
}
|
|
758
|
+
const expected: u8[] = bcryptRun(password, parsed.value.prefix, parsed.value.setting)
|
|
759
|
+
return Ok(timingSafeEqual(expected, given))
|
|
760
|
+
}
|