@amritk/nish-x86_64-linux 0.21.0 → 0.23.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.
@@ -14,11 +14,23 @@
14
14
  * // "$2b$12$" + 22 salt characters + 31 digest characters
15
15
  * const ok: Result<boolean, string> = bcryptVerify(pw, storedHash)
16
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
+ *
17
25
  * **The password is a `Secret<u8[]>`** (`nish:secret`). The key schedule and
18
26
  * the 2^cost rounds run inside `exposeWith`; the Blowfish state (the P-array
19
27
  * and the four S-boxes, 1042 words), the expanded key and the key-stream copy
20
28
  * are wiped before it returns. `bcryptVerify` rebuilds the whole 60-character
21
- * hash and compares it with `timingSafeEqual`.
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.
22
34
  *
23
35
  * **The key is the reference's.** The bytes up to the first NUL, then one NUL,
24
36
  * repeated until 72 bytes are taken: a password longer than 72 bytes hashes as
@@ -45,9 +57,12 @@
45
57
  * rounds, hours of one core. Verify only hashes you stored.
46
58
  *
47
59
  * **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).
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).
51
66
  *
52
67
  * The algorithm is Provos and Mazières's ("A Future-Adaptable Password
53
68
  * Scheme", USENIX 1999). The code is adapted from Solar Designer's
@@ -101,7 +116,7 @@
101
116
  */
102
117
 
103
118
  import { timingSafeEqual } from "nish/crypto/ct"
104
- import { Secret, exposeWith, wipe } from "nish:secret"
119
+ import { Secret, exposeWith, secret, wipe } from "nish:secret"
105
120
 
106
121
  /** The length of a bcrypt hash string: `$2b$`, two cost digits, `$`, 22 salt and 31 digest characters. */
107
122
  export const BCRYPT_HASH_LENGTH: i32 = 60
@@ -139,6 +154,17 @@ interface BcryptSetting {
139
154
  safety: u32
140
155
  }
141
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
+
142
168
  /** A parsed hash string: its setting and the 29 characters the rebuilt hash starts with. */
143
169
  interface BcryptParsed {
144
170
  setting: BcryptSetting
@@ -188,9 +214,18 @@ const bcryptSextetOf = (c: i32): i32 => {
188
214
  */
189
215
  const bcryptEncode = (src: u8[], n: i32): u8[] => {
190
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 => {
191
226
  let i: i32 = 0
192
- let o: i32 = 0
193
- while (i + 2 < n && i + 2 < toI32(src.length) && o + 3 < toI32(out.length)) {
227
+ let o: i32 = at
228
+ while (i + 2 < n && i + 2 < toI32(src.length) && o >= 0 && o + 3 < toI32(out.length)) {
194
229
  const c1: i32 = toI32(src[i])
195
230
  const c2: i32 = toI32(src[i + 1])
196
231
  const c3: i32 = toI32(src[i + 2])
@@ -212,7 +247,6 @@ const bcryptEncode = (src: u8[], n: i32): u8[] => {
212
247
  out[o + 1] = bcryptCharOf((c1 & 0x03) << 4)
213
248
  }
214
249
  }
215
- return out
216
250
  }
217
251
 
218
252
  /** `src` copied into `dst` from `at`, as far as `dst` reaches. */
@@ -391,21 +425,19 @@ const bcryptBody = (st: u32[]): void => {
391
425
  }
392
426
 
393
427
  /**
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.
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.
403
438
  */
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) {
439
+ const bcryptKeySchedule = (expanded: u32[], password: u8[], safety: u32): void => {
440
+ if (expanded.length < 19) {
409
441
  return
410
442
  }
411
443
  // The key stream: at most 72 bytes of the password and zeros after, so the
@@ -434,20 +466,39 @@ const bcryptSetKey = (st: u32[], expanded: u32[], password: u8[], safety: u32):
434
466
  }
435
467
  diff |= correct ^ buggy
436
468
  expanded[i] = correct
437
- st[i] ^= correct
438
469
  }
439
470
  diff |= diff >>> 16
440
471
  diff &= 0xffff
441
472
  diff += 0xffff
442
473
  sign <<= 9
443
474
  sign &= ~diff & safety
444
- st[0] ^= sign
475
+ expanded[18] = sign
445
476
  wipe(key)
446
477
  }
447
478
 
448
- /** The state Blowfish starts from: P then S0–S3, the digits of π (`BF_init_state`). */
449
- const bcryptInitState = (): u32[] => {
450
- const st: u32[] = [
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[] = [
451
502
  // P
452
503
  0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
453
504
  0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
@@ -585,7 +636,12 @@ const bcryptInitState = (): u32[] => {
585
636
  0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
586
637
  0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
587
638
  ]
588
- return st
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
+ }
589
645
  }
590
646
 
591
647
  /** The block `lr` into `st` at `at` and `at + 1`. */
@@ -612,37 +668,39 @@ const bcryptXorKey = (st: u32[], expanded: u32[]): void => {
612
668
  }
613
669
  }
614
670
 
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) {
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)) {
618
674
  return
619
675
  }
620
676
  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
677
+ st[at + i] ^= s0
678
+ st[at + i + 1] ^= s1
679
+ st[at + i + 2] ^= s2
680
+ st[at + i + 3] ^= s3
625
681
  }
626
- st[16] ^= s0
627
- st[17] ^= s1
682
+ st[at + 16] ^= s0
683
+ st[at + 17] ^= s1
628
684
  }
629
685
 
630
686
  /**
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.
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.
634
691
  */
635
- const bcryptExposed = (password: u8[], setting: BcryptSetting): u8[] => {
692
+ const bcryptExposed = (password: u8[], setting: BcryptSetting, out: u8[]): void => {
636
693
  const salt: u32[] = setting.salt
637
694
  if (salt.length < 4) {
638
- return new Array<u8>(31)
695
+ return
639
696
  }
640
697
  const s0: u32 = salt[0]
641
698
  const s1: u32 = salt[1]
642
699
  const s2: u32 = salt[2]
643
700
  const s3: u32 = salt[3]
644
- const st: u32[] = bcryptInitState()
645
- const expanded: u32[] = new Array<u32>(18)
701
+ const st: u32[] = new Array<u32>(BCRYPT_STATE_WORDS)
702
+ bcryptInitState(st, 0)
703
+ const expanded: u32[] = new Array<u32>(19)
646
704
  const lr: u32[] = new Array<u32>(2)
647
705
  bcryptSetKey(st, expanded, password, setting.safety)
648
706
 
@@ -661,7 +719,7 @@ const bcryptExposed = (password: u8[], setting: BcryptSetting): u8[] => {
661
719
  while (rounds !== 0) {
662
720
  bcryptXorKey(st, expanded)
663
721
  bcryptBody(st)
664
- bcryptXorSalt(st, s0, s1, s2, s3)
722
+ bcryptXorSalt(st, 0, s0, s1, s2, s3)
665
723
  bcryptBody(st)
666
724
  rounds -= 1
667
725
  }
@@ -672,12 +730,11 @@ const bcryptExposed = (password: u8[], setting: BcryptSetting): u8[] => {
672
730
  bcryptMagic(st, lr, digest, 8, 0x65686f6c, 0x64657253)
673
731
  bcryptMagic(st, lr, digest, 16, 0x63727944, 0x6f756274)
674
732
  // 23 of the 24 bytes, as the reference encodes them.
675
- const out: u8[] = bcryptEncode(digest, 23)
733
+ bcryptEncodeInto(out, BCRYPT_SETTING_LENGTH, digest, 23)
676
734
  wipe(st)
677
735
  wipe(expanded)
678
736
  wipe(lr)
679
737
  wipe(digest)
680
- return out
681
738
  }
682
739
 
683
740
  /** One pair of the magic string, encrypted 64 times under `st`, into `dst` at `at` as eight big-endian bytes. */
@@ -687,30 +744,67 @@ const bcryptMagic = (st: u32[], lr: u32[], dst: u8[], at: i32, l: u32, r: u32):
687
744
  for (let k: i32 = 0; k < 64; k++) {
688
745
  bcryptEncrypt(st, lr)
689
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 => {
690
752
  if (at < 0 || at + 7 >= toI32(dst.length)) {
691
753
  return
692
754
  }
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)
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[] => {
708
779
  const out: u8[] = new Array<u8>(BCRYPT_HASH_LENGTH)
709
- bcryptPut(out, 0, prefix)
710
- bcryptPut(out, BCRYPT_SETTING_LENGTH, digest)
780
+ bcryptHashInto(password, job, out)
711
781
  return out
712
782
  }
713
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
+
714
808
  /**
715
809
  * The `$2b$` hash of `password` with a 16-byte `salt` and 2^`cost` rounds:
716
810
  * 60 characters, `$2b$`, the two-digit cost, `$`, the salt and the digest in
@@ -725,7 +819,8 @@ export const bcryptHash = (password: Secret<u8[]>, salt: u8[], cost: i32): Resul
725
819
  return Err("bcrypt: the cost is outside 4 to 31")
726
820
  }
727
821
  const setting: BcryptSetting = { salt: bcryptSaltWords(salt), cost: cost, safety: toU32(0) }
728
- const out: u8[] = bcryptRun(password, bcryptSettingOf(salt, cost), setting)
822
+ const job: BcryptJob = { setting: setting, prefix: bcryptSettingOf(salt, cost), hash: "" }
823
+ const out: u8[] = exposeWith(password, job, bcryptHashExposed)
729
824
  return Ok(String.fromBytes(out, 0, BCRYPT_HASH_LENGTH))
730
825
  }
731
826
 
@@ -736,6 +831,24 @@ export const bcryptHash = (password: Secret<u8[]>, salt: u8[], cost: i32): Resul
736
831
  * are read; an `Err` names what makes `hash` not a bcrypt hash.
737
832
  */
738
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> => {
739
852
  if (hash.length !== 60) {
740
853
  return Err("bcrypt: a hash is 60 characters")
741
854
  }
@@ -743,18 +856,702 @@ export const bcryptVerify = (password: Secret<u8[]>, hash: string): Result<boole
743
856
  if (parsed.isErr()) {
744
857
  return Err(parsed.error)
745
858
  }
746
- const given: u8[] = new Array<u8>(BCRYPT_HASH_LENGTH)
747
859
  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))
860
+ for (let i: i32 = 0; i < toI32(hash.length); i++) {
750
861
  if (i >= BCRYPT_SETTING_LENGTH) {
751
- alphabet |= bcryptSextetOf(c)
862
+ alphabet |= bcryptSextetOf(toI32(hash.charCodeAt(i)))
752
863
  }
753
- given[i] = toU8(c)
754
864
  }
755
865
  if (alphabet < 0) {
756
866
  return Err("bcrypt: the digest is not bcrypt base64")
757
867
  }
758
- const expected: u8[] = bcryptRun(password, parsed.value.prefix, parsed.value.setting)
759
- return Ok(timingSafeEqual(expected, given))
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
+ }
760
1557
  }