@amritk/nish-x86_64-linux 0.27.2 → 0.28.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,13 +14,17 @@
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
17
+ * **Several at once.** `bcryptHashN` and `bcryptVerifyN` take one to six
18
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,
19
+ * encryptions interleaved in one loop, so one core does the work of several
20
+ * hashes in well under the time of as many; each result is the single path's,
21
+ * byte for byte. `bcryptVerifyN` groups lanes by the cost their hash names,
22
22
  * and a malformed hash in any lane is an `Err` for the whole call, naming the
23
- * lane, before anything is hashed.
23
+ * lane, before anything is hashed. `bcryptHash4` and `bcryptVerify4` are the
24
+ * same for up to four. Which width is fastest depends on the core (how many
25
+ * loads it issues a cycle, how much L1 the states fill), so `bcryptCalibrate`
26
+ * measures it: it times widths one to six at cost 4 on throwaway passwords
27
+ * and answers the one with the least time per hash. Call it once, at startup.
24
28
  *
25
29
  * **The password is a `Secret<u8[]>`** (`nish:secret`). The key schedule and
26
30
  * the 2^cost rounds run inside `exposeWith`; the Blowfish state (the P-array
@@ -28,8 +32,8 @@
28
32
  * are wiped before it returns. `bcryptVerify` rebuilds the whole 60-character
29
33
  * hash and compares it with `timingSafeEqual` inside the same `exposeWith`,
30
34
  * 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
35
+ * state and digests and, in `bcryptVerifyN`, each lane's rebuilt and given
36
+ * hash. The hash strings `bcryptHash` and `bcryptHashN` return are public and are not
33
37
  * wiped, nor are the bytes they are made from.
34
38
  *
35
39
  * **The key is the reference's.** The bytes up to the first NUL, then one NUL,
@@ -56,7 +60,9 @@
56
60
  * cost the hash names, so a `$2b$31$` hash someone hands you takes 2^31
57
61
  * rounds, hours of one core. Verify only hashes you stored.
58
62
  *
59
- * **Pure.** No I/O and no C, so it builds for wasm32. A `std/threads` task
63
+ * **Pure.** No I/O and no C, so it builds for wasm32; `bcryptCalibrate`
64
+ * alone reads the monotonic clock (`clock` in `--emit-capabilities`), and the
65
+ * hashes never do. A `std/threads` task
60
66
  * may call `bcryptHash`, `bcryptVerify` and the batch: everything they wipe
61
67
  * is an array they made, never the caller's `hashes`, and the batch's state is
62
68
  * a `Secret` of a fresh array
@@ -117,6 +123,7 @@
117
123
 
118
124
  import { timingSafeEqual } from "nish/crypto/ct"
119
125
  import { Secret, exposeWith, secret, wipe } from "nish:secret"
126
+ import { monotonicNanos } from "nish:process"
120
127
 
121
128
  /** The length of a bcrypt hash string: `$2b$`, two cost digits, `$`, 22 salt and 31 digest characters. */
122
129
  export const BCRYPT_HASH_LENGTH: i32 = 60
@@ -380,12 +387,13 @@ const bcryptEncrypt = (st: u32[], lr: u32[]): void => {
380
387
  const lb: i32 = toI32(l >>> 16) & 0xff
381
388
  const lc: i32 = toI32(l >>> 8) & 0xff
382
389
  const ld: i32 = toI32(l) & 0xff
383
- r ^= st[n] ^ (((st[18 + la] + st[274 + lb]) ^ st[530 + lc]) + st[786 + ld])
390
+ r = ctSelect(0xffffffff, r ^ st[n], 0) ^ (((st[18 + la] + st[274 + lb]) ^ st[530 + lc]) + st[786 + ld])
384
391
  const ra: i32 = toI32(r >>> 24) & 0xff
385
392
  const rb: i32 = toI32(r >>> 16) & 0xff
386
393
  const rc: i32 = toI32(r >>> 8) & 0xff
387
394
  const rd: i32 = toI32(r) & 0xff
388
- l ^= st[n + 1] ^ (((st[18 + ra] + st[274 + rb]) ^ st[530 + rc]) + st[786 + rd])
395
+ l =
396
+ ctSelect(0xffffffff, l ^ st[n + 1], 0) ^ (((st[18 + ra] + st[274 + rb]) ^ st[530 + rc]) + st[786 + rd])
389
397
  }
390
398
  lr[0] = r ^ st[17]
391
399
  lr[1] = l
@@ -395,6 +403,15 @@ const bcryptEncrypt = (st: u32[], lr: u32[]): void => {
395
403
  * `BF_body`: encrypt the zero block, then each result in turn, writing the
396
404
  * pairs over the whole state, P first and then the S-boxes. The hot loop:
397
405
  * 2^(cost+1) calls of 521 blocks each.
406
+ *
407
+ * Each half-round is `x ^ P[n] ^ F(y)`, and only `F(y)` waits on the previous
408
+ * one, so `x ^ P[n]` belongs off the critical path with one XOR after the last
409
+ * S-box add. Written plainly, LLVM's Reassociate and InstCombine put `P[n]`
410
+ * after `F(y)` in 15 of 16 half-rounds, two dependent XORs instead of one
411
+ * (docs/security/crypto-bcrypt.md, "Speed"). `ctSelect` with an all-ones mask
412
+ * is `x ^ P[n]` itself, but behind the value barrier the mask is opaque, so the
413
+ * `and` it leaves cannot be reassociated into the XOR chain: the half-round
414
+ * keeps the order written, for one hoisted barrier and an `and` off the path.
398
415
  */
399
416
  const bcryptBody = (st: u32[]): void => {
400
417
  if (st.length < 1042) {
@@ -409,12 +426,14 @@ const bcryptBody = (st: u32[]): void => {
409
426
  const lb: i32 = toI32(l >>> 16) & 0xff
410
427
  const lc: i32 = toI32(l >>> 8) & 0xff
411
428
  const ld: i32 = toI32(l) & 0xff
412
- r ^= st[n] ^ (((st[18 + la] + st[274 + lb]) ^ st[530 + lc]) + st[786 + ld])
429
+ r = ctSelect(0xffffffff, r ^ st[n], 0) ^ (((st[18 + la] + st[274 + lb]) ^ st[530 + lc]) + st[786 + ld])
413
430
  const ra: i32 = toI32(r >>> 24) & 0xff
414
431
  const rb: i32 = toI32(r >>> 16) & 0xff
415
432
  const rc: i32 = toI32(r >>> 8) & 0xff
416
433
  const rd: i32 = toI32(r) & 0xff
417
- l ^= st[n + 1] ^ (((st[18 + ra] + st[274 + rb]) ^ st[530 + rc]) + st[786 + rd])
434
+ l =
435
+ ctSelect(0xffffffff, l ^ st[n + 1], 0) ^
436
+ (((st[18 + ra] + st[274 + rb]) ^ st[530 + rc]) + st[786 + rd])
418
437
  }
419
438
  const t: u32 = r
420
439
  r = l
@@ -876,44 +895,50 @@ const bcryptBytesOf = (given: u8[], hash: string): void => {
876
895
  }
877
896
 
878
897
  // ---------------------------------------------------------------------------
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.
898
+ // The batch, two to six lanes on one thread. Up to six independent hashes
899
+ // share one `Secret<u32[]>`: the lane in slot s has its Blowfish state at
900
+ // 1042s (0–6251), its expanded key at 6252 + 18s and its `$2a$` safety word at
901
+ // 6360 + s. The lanes a caller gives are packed into slots 0 to w - 1, so a
902
+ // batch of w passwords runs the w-lane kernel whatever positions they came in.
903
+ // One exposeWith per password XORs that lane's key in, and one more runs the
904
+ // expansion, the 2^cost rounds and the magic string. The cost rounds, all but
905
+ // 713 of a hash's 2^(cost+1) × 521 + 713 blocks, run on all w lanes at once
906
+ // with their encryption chains interleaved round by round, so the S-box loads
907
+ // of one lane overlap the others' and the core is not left waiting on one
908
+ // chain's latency; the expansion and the magic string run lane by lane.
886
909
  // ---------------------------------------------------------------------------
887
910
 
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
911
+ /** The most lanes a batch runs. */
912
+ const BCRYPT_BATCH_LANES: i32 = 6
913
+ /** Where the six expanded keys of 18 start in the batch state, after the six Blowfish states. */
914
+ const BCRYPT_BATCH_KEYS: i32 = 6252
915
+ /** Where the six safety words start, after the keys. */
916
+ const BCRYPT_BATCH_SAFETY: i32 = 6360
917
+ /** Words in the batch state: six Blowfish states, six expanded keys of 18 and six safety words. */
918
+ const BCRYPT_BATCH_WORDS: i32 = 6366
894
919
 
895
920
  /**
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.
921
+ * What the batch kernel reads besides the state, slot by slot: each slot's
922
+ * four salt words (slot s at 4s), each slot's `$2a$` safety word, the shared
923
+ * cost, the number of slots that run, each slot's 29-character setting and,
924
+ * for verifying, each slot's hash to compare with.
900
925
  */
901
926
  interface BcryptBatch {
902
927
  salts: u32[]
903
928
  safety: u32[]
904
929
  cost: i32
905
- mask: i32
930
+ width: i32
906
931
  prefixes: u8[][]
907
932
  hashes: string[]
908
933
  }
909
934
 
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) {
935
+ /** The six states set to π and each slot's safety word stored, for `exposeWith` on the fresh batch state. */
936
+ const bcryptInitN = (st: u32[], batch: BcryptBatch): i32 => {
937
+ if (toI32(st.length) < BCRYPT_BATCH_WORDS) {
913
938
  return 0
914
939
  }
915
940
  const safety: u32[] = batch.safety
916
- for (let k: i32 = 0; k < 4; k++) {
941
+ for (let k: i32 = 0; k < BCRYPT_BATCH_LANES; k++) {
917
942
  bcryptInitState(st, k * BCRYPT_STATE_WORDS)
918
943
  st[BCRYPT_BATCH_SAFETY + k] = k < toI32(safety.length) ? safety[k] : 0
919
944
  }
@@ -936,21 +961,21 @@ const bcryptKeyWord = (password: u8[], arg: i32): u32 => {
936
961
  }
937
962
 
938
963
  /**
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.
964
+ * The key of the lane in slot `slot` into the batch state: P XORed with it
965
+ * as `bcryptSetKey` XORs it, and the 18 words kept for the cost rounds. Each
966
+ * word is one `exposeWith` on the password, since the state is what this
967
+ * function is exposing and a `Secret` cannot be handed into it otherwise.
943
968
  */
944
- const bcryptLoadLane = (st: u32[], password: Secret<u8[]>, lane: i32): i32 => {
945
- if (st.length < 4244) {
969
+ const bcryptLoadLane = (st: u32[], password: Secret<u8[]>, slot: i32): i32 => {
970
+ if (toI32(st.length) < BCRYPT_BATCH_WORDS) {
946
971
  return 0
947
972
  }
948
- if (lane < 0 || lane > 3) {
973
+ if (slot < 0 || slot >= BCRYPT_BATCH_LANES) {
949
974
  return 0
950
975
  }
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
976
+ const at: i32 = slot * BCRYPT_STATE_WORDS
977
+ const kept: i32 = slot * 18 + BCRYPT_BATCH_KEYS
978
+ const flag: i32 = st[BCRYPT_BATCH_SAFETY + slot] !== 0 ? 32 : 0
954
979
  for (let k: i32 = 0; k < 18; k++) {
955
980
  const word: u32 = exposeWith(password, k | flag, bcryptKeyWord)
956
981
  st[at + k] ^= word
@@ -960,18 +985,159 @@ const bcryptLoadLane = (st: u32[], password: Secret<u8[]>, lane: i32): i32 => {
960
985
  return 0
961
986
  }
962
987
 
963
- /** `bcryptLoadLane` for lane 0, in the shape `exposeWith` calls. */
988
+ /** `bcryptLoadLane` for slot 0, in the shape `exposeWith` calls. */
964
989
  const bcryptLoadLane0 = (st: u32[], password: Secret<u8[]>): i32 => bcryptLoadLane(st, password, 0)
965
990
 
966
- /** `bcryptLoadLane` for lane 1. */
991
+ /** `bcryptLoadLane` for slot 1. */
967
992
  const bcryptLoadLane1 = (st: u32[], password: Secret<u8[]>): i32 => bcryptLoadLane(st, password, 1)
968
993
 
969
- /** `bcryptLoadLane` for lane 2. */
994
+ /** `bcryptLoadLane` for slot 2. */
970
995
  const bcryptLoadLane2 = (st: u32[], password: Secret<u8[]>): i32 => bcryptLoadLane(st, password, 2)
971
996
 
972
- /** `bcryptLoadLane` for lane 3. */
997
+ /** `bcryptLoadLane` for slot 3. */
973
998
  const bcryptLoadLane3 = (st: u32[], password: Secret<u8[]>): i32 => bcryptLoadLane(st, password, 3)
974
999
 
1000
+ /** `bcryptLoadLane` for slot 4. */
1001
+ const bcryptLoadLane4 = (st: u32[], password: Secret<u8[]>): i32 => bcryptLoadLane(st, password, 4)
1002
+
1003
+ /** `bcryptLoadLane` for slot 5. */
1004
+ const bcryptLoadLane5 = (st: u32[], password: Secret<u8[]>): i32 => bcryptLoadLane(st, password, 5)
1005
+
1006
+ /** `password` into slot `slot` of the batch `state`. */
1007
+ const bcryptLoadSlot = (state: Secret<u32[]>, password: Secret<u8[]>, slot: i32): void => {
1008
+ if (slot === 0) {
1009
+ exposeWith(state, password, bcryptLoadLane0)
1010
+ } else if (slot === 1) {
1011
+ exposeWith(state, password, bcryptLoadLane1)
1012
+ } else if (slot === 2) {
1013
+ exposeWith(state, password, bcryptLoadLane2)
1014
+ } else if (slot === 3) {
1015
+ exposeWith(state, password, bcryptLoadLane3)
1016
+ } else if (slot === 4) {
1017
+ exposeWith(state, password, bcryptLoadLane4)
1018
+ } else if (slot === 5) {
1019
+ exposeWith(state, password, bcryptLoadLane5)
1020
+ }
1021
+ }
1022
+
1023
+ // The cost-round kernels, one per width. Each is `bcryptBody4`'s text with
1024
+ // the lane count changed (bench/crypto/bcrypt/gen-body.mjs writes them), not
1025
+ // one loop over a lane count: the lanes' l and r must live in registers and
1026
+ // their loads be scheduled together, which a loop over an array of lanes
1027
+ // does not give.
1028
+
1029
+ /** `bcryptBody4`'s interleave on two lanes. */
1030
+ const bcryptBody2 = (st: u32[]): void => {
1031
+ if (st.length < 2084) {
1032
+ return
1033
+ }
1034
+ let l0: u32 = 0
1035
+ let r0: u32 = 0
1036
+ let l1: u32 = 0
1037
+ let r1: u32 = 0
1038
+ for (let i: i32 = 0; i + 1 < 1042; i += 2) {
1039
+ l0 ^= st[0]
1040
+ l1 ^= st[1042]
1041
+ for (let n: i32 = 1; n < 17; n += 2) {
1042
+ const l0a: i32 = toI32(l0 >>> 24) & 0xff
1043
+ const l0b: i32 = toI32(l0 >>> 16) & 0xff
1044
+ const l0c: i32 = toI32(l0 >>> 8) & 0xff
1045
+ const l0d: i32 = toI32(l0) & 0xff
1046
+ const l1a: i32 = toI32(l1 >>> 24) & 0xff
1047
+ const l1b: i32 = toI32(l1 >>> 16) & 0xff
1048
+ const l1c: i32 = toI32(l1 >>> 8) & 0xff
1049
+ const l1d: i32 = toI32(l1) & 0xff
1050
+ r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1051
+ r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1052
+ const r0a: i32 = toI32(r0 >>> 24) & 0xff
1053
+ const r0b: i32 = toI32(r0 >>> 16) & 0xff
1054
+ const r0c: i32 = toI32(r0 >>> 8) & 0xff
1055
+ const r0d: i32 = toI32(r0) & 0xff
1056
+ const r1a: i32 = toI32(r1 >>> 24) & 0xff
1057
+ const r1b: i32 = toI32(r1 >>> 16) & 0xff
1058
+ const r1c: i32 = toI32(r1 >>> 8) & 0xff
1059
+ const r1d: i32 = toI32(r1) & 0xff
1060
+ l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1061
+ l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1062
+ }
1063
+ const t0: u32 = r0
1064
+ r0 = l0
1065
+ l0 = t0 ^ st[17]
1066
+ st[i] = l0
1067
+ st[i + 1] = r0
1068
+ const t1: u32 = r1
1069
+ r1 = l1
1070
+ l1 = t1 ^ st[1059]
1071
+ st[1042 + i] = l1
1072
+ st[1042 + i + 1] = r1
1073
+ }
1074
+ }
1075
+
1076
+ /** `bcryptBody4`'s interleave on three lanes. */
1077
+ const bcryptBody3 = (st: u32[]): void => {
1078
+ if (st.length < 3126) {
1079
+ return
1080
+ }
1081
+ let l0: u32 = 0
1082
+ let r0: u32 = 0
1083
+ let l1: u32 = 0
1084
+ let r1: u32 = 0
1085
+ let l2: u32 = 0
1086
+ let r2: u32 = 0
1087
+ for (let i: i32 = 0; i + 1 < 1042; i += 2) {
1088
+ l0 ^= st[0]
1089
+ l1 ^= st[1042]
1090
+ l2 ^= st[2084]
1091
+ for (let n: i32 = 1; n < 17; n += 2) {
1092
+ const l0a: i32 = toI32(l0 >>> 24) & 0xff
1093
+ const l0b: i32 = toI32(l0 >>> 16) & 0xff
1094
+ const l0c: i32 = toI32(l0 >>> 8) & 0xff
1095
+ const l0d: i32 = toI32(l0) & 0xff
1096
+ const l1a: i32 = toI32(l1 >>> 24) & 0xff
1097
+ const l1b: i32 = toI32(l1 >>> 16) & 0xff
1098
+ const l1c: i32 = toI32(l1 >>> 8) & 0xff
1099
+ const l1d: i32 = toI32(l1) & 0xff
1100
+ const l2a: i32 = toI32(l2 >>> 24) & 0xff
1101
+ const l2b: i32 = toI32(l2 >>> 16) & 0xff
1102
+ const l2c: i32 = toI32(l2 >>> 8) & 0xff
1103
+ const l2d: i32 = toI32(l2) & 0xff
1104
+ r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1105
+ r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1106
+ r2 ^= st[2084 + n] ^ (((st[2102 + l2a] + st[2358 + l2b]) ^ st[2614 + l2c]) + st[2870 + l2d])
1107
+ const r0a: i32 = toI32(r0 >>> 24) & 0xff
1108
+ const r0b: i32 = toI32(r0 >>> 16) & 0xff
1109
+ const r0c: i32 = toI32(r0 >>> 8) & 0xff
1110
+ const r0d: i32 = toI32(r0) & 0xff
1111
+ const r1a: i32 = toI32(r1 >>> 24) & 0xff
1112
+ const r1b: i32 = toI32(r1 >>> 16) & 0xff
1113
+ const r1c: i32 = toI32(r1 >>> 8) & 0xff
1114
+ const r1d: i32 = toI32(r1) & 0xff
1115
+ const r2a: i32 = toI32(r2 >>> 24) & 0xff
1116
+ const r2b: i32 = toI32(r2 >>> 16) & 0xff
1117
+ const r2c: i32 = toI32(r2 >>> 8) & 0xff
1118
+ const r2d: i32 = toI32(r2) & 0xff
1119
+ l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1120
+ l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1121
+ l2 ^= st[2084 + n + 1] ^ (((st[2102 + r2a] + st[2358 + r2b]) ^ st[2614 + r2c]) + st[2870 + r2d])
1122
+ }
1123
+ const t0: u32 = r0
1124
+ r0 = l0
1125
+ l0 = t0 ^ st[17]
1126
+ st[i] = l0
1127
+ st[i + 1] = r0
1128
+ const t1: u32 = r1
1129
+ r1 = l1
1130
+ l1 = t1 ^ st[1059]
1131
+ st[1042 + i] = l1
1132
+ st[1042 + i + 1] = r1
1133
+ const t2: u32 = r2
1134
+ r2 = l2
1135
+ l2 = t2 ^ st[2101]
1136
+ st[2084 + i] = l2
1137
+ st[2084 + i + 1] = r2
1138
+ }
1139
+ }
1140
+
975
1141
  /**
976
1142
  * `bcryptBody` on all four lanes at once: each lane's state re-encrypted in
977
1143
  * place, the four encryption chains interleaved round by round so that one
@@ -1060,93 +1226,287 @@ const bcryptBody4 = (st: u32[]): void => {
1060
1226
  }
1061
1227
  }
1062
1228
 
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) {
1229
+ /** `bcryptBody4`'s interleave on five lanes. */
1230
+ const bcryptBody5 = (st: u32[]): void => {
1231
+ if (st.length < 5210) {
1232
+ return
1233
+ }
1234
+ let l0: u32 = 0
1235
+ let r0: u32 = 0
1236
+ let l1: u32 = 0
1237
+ let r1: u32 = 0
1238
+ let l2: u32 = 0
1239
+ let r2: u32 = 0
1240
+ let l3: u32 = 0
1241
+ let r3: u32 = 0
1242
+ let l4: u32 = 0
1243
+ let r4: u32 = 0
1244
+ for (let i: i32 = 0; i + 1 < 1042; i += 2) {
1245
+ l0 ^= st[0]
1246
+ l1 ^= st[1042]
1247
+ l2 ^= st[2084]
1248
+ l3 ^= st[3126]
1249
+ l4 ^= st[4168]
1250
+ for (let n: i32 = 1; n < 17; n += 2) {
1251
+ const l0a: i32 = toI32(l0 >>> 24) & 0xff
1252
+ const l0b: i32 = toI32(l0 >>> 16) & 0xff
1253
+ const l0c: i32 = toI32(l0 >>> 8) & 0xff
1254
+ const l0d: i32 = toI32(l0) & 0xff
1255
+ const l1a: i32 = toI32(l1 >>> 24) & 0xff
1256
+ const l1b: i32 = toI32(l1 >>> 16) & 0xff
1257
+ const l1c: i32 = toI32(l1 >>> 8) & 0xff
1258
+ const l1d: i32 = toI32(l1) & 0xff
1259
+ const l2a: i32 = toI32(l2 >>> 24) & 0xff
1260
+ const l2b: i32 = toI32(l2 >>> 16) & 0xff
1261
+ const l2c: i32 = toI32(l2 >>> 8) & 0xff
1262
+ const l2d: i32 = toI32(l2) & 0xff
1263
+ const l3a: i32 = toI32(l3 >>> 24) & 0xff
1264
+ const l3b: i32 = toI32(l3 >>> 16) & 0xff
1265
+ const l3c: i32 = toI32(l3 >>> 8) & 0xff
1266
+ const l3d: i32 = toI32(l3) & 0xff
1267
+ const l4a: i32 = toI32(l4 >>> 24) & 0xff
1268
+ const l4b: i32 = toI32(l4 >>> 16) & 0xff
1269
+ const l4c: i32 = toI32(l4 >>> 8) & 0xff
1270
+ const l4d: i32 = toI32(l4) & 0xff
1271
+ r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1272
+ r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1273
+ r2 ^= st[2084 + n] ^ (((st[2102 + l2a] + st[2358 + l2b]) ^ st[2614 + l2c]) + st[2870 + l2d])
1274
+ r3 ^= st[3126 + n] ^ (((st[3144 + l3a] + st[3400 + l3b]) ^ st[3656 + l3c]) + st[3912 + l3d])
1275
+ r4 ^= st[4168 + n] ^ (((st[4186 + l4a] + st[4442 + l4b]) ^ st[4698 + l4c]) + st[4954 + l4d])
1276
+ const r0a: i32 = toI32(r0 >>> 24) & 0xff
1277
+ const r0b: i32 = toI32(r0 >>> 16) & 0xff
1278
+ const r0c: i32 = toI32(r0 >>> 8) & 0xff
1279
+ const r0d: i32 = toI32(r0) & 0xff
1280
+ const r1a: i32 = toI32(r1 >>> 24) & 0xff
1281
+ const r1b: i32 = toI32(r1 >>> 16) & 0xff
1282
+ const r1c: i32 = toI32(r1 >>> 8) & 0xff
1283
+ const r1d: i32 = toI32(r1) & 0xff
1284
+ const r2a: i32 = toI32(r2 >>> 24) & 0xff
1285
+ const r2b: i32 = toI32(r2 >>> 16) & 0xff
1286
+ const r2c: i32 = toI32(r2 >>> 8) & 0xff
1287
+ const r2d: i32 = toI32(r2) & 0xff
1288
+ const r3a: i32 = toI32(r3 >>> 24) & 0xff
1289
+ const r3b: i32 = toI32(r3 >>> 16) & 0xff
1290
+ const r3c: i32 = toI32(r3 >>> 8) & 0xff
1291
+ const r3d: i32 = toI32(r3) & 0xff
1292
+ const r4a: i32 = toI32(r4 >>> 24) & 0xff
1293
+ const r4b: i32 = toI32(r4 >>> 16) & 0xff
1294
+ const r4c: i32 = toI32(r4 >>> 8) & 0xff
1295
+ const r4d: i32 = toI32(r4) & 0xff
1296
+ l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1297
+ l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1298
+ l2 ^= st[2084 + n + 1] ^ (((st[2102 + r2a] + st[2358 + r2b]) ^ st[2614 + r2c]) + st[2870 + r2d])
1299
+ l3 ^= st[3126 + n + 1] ^ (((st[3144 + r3a] + st[3400 + r3b]) ^ st[3656 + r3c]) + st[3912 + r3d])
1300
+ l4 ^= st[4168 + n + 1] ^ (((st[4186 + r4a] + st[4442 + r4b]) ^ st[4698 + r4c]) + st[4954 + r4d])
1301
+ }
1302
+ const t0: u32 = r0
1303
+ r0 = l0
1304
+ l0 = t0 ^ st[17]
1305
+ st[i] = l0
1306
+ st[i + 1] = r0
1307
+ const t1: u32 = r1
1308
+ r1 = l1
1309
+ l1 = t1 ^ st[1059]
1310
+ st[1042 + i] = l1
1311
+ st[1042 + i + 1] = r1
1312
+ const t2: u32 = r2
1313
+ r2 = l2
1314
+ l2 = t2 ^ st[2101]
1315
+ st[2084 + i] = l2
1316
+ st[2084 + i + 1] = r2
1317
+ const t3: u32 = r3
1318
+ r3 = l3
1319
+ l3 = t3 ^ st[3143]
1320
+ st[3126 + i] = l3
1321
+ st[3126 + i + 1] = r3
1322
+ const t4: u32 = r4
1323
+ r4 = l4
1324
+ l4 = t4 ^ st[4185]
1325
+ st[4168 + i] = l4
1326
+ st[4168 + i + 1] = r4
1327
+ }
1328
+ }
1329
+
1330
+ /** `bcryptBody4`'s interleave on six lanes. */
1331
+ const bcryptBody6 = (st: u32[]): void => {
1332
+ if (st.length < 6252) {
1333
+ return
1334
+ }
1335
+ let l0: u32 = 0
1336
+ let r0: u32 = 0
1337
+ let l1: u32 = 0
1338
+ let r1: u32 = 0
1339
+ let l2: u32 = 0
1340
+ let r2: u32 = 0
1341
+ let l3: u32 = 0
1342
+ let r3: u32 = 0
1343
+ let l4: u32 = 0
1344
+ let r4: u32 = 0
1345
+ let l5: u32 = 0
1346
+ let r5: u32 = 0
1347
+ for (let i: i32 = 0; i + 1 < 1042; i += 2) {
1348
+ l0 ^= st[0]
1349
+ l1 ^= st[1042]
1350
+ l2 ^= st[2084]
1351
+ l3 ^= st[3126]
1352
+ l4 ^= st[4168]
1353
+ l5 ^= st[5210]
1354
+ for (let n: i32 = 1; n < 17; n += 2) {
1355
+ const l0a: i32 = toI32(l0 >>> 24) & 0xff
1356
+ const l0b: i32 = toI32(l0 >>> 16) & 0xff
1357
+ const l0c: i32 = toI32(l0 >>> 8) & 0xff
1358
+ const l0d: i32 = toI32(l0) & 0xff
1359
+ const l1a: i32 = toI32(l1 >>> 24) & 0xff
1360
+ const l1b: i32 = toI32(l1 >>> 16) & 0xff
1361
+ const l1c: i32 = toI32(l1 >>> 8) & 0xff
1362
+ const l1d: i32 = toI32(l1) & 0xff
1363
+ const l2a: i32 = toI32(l2 >>> 24) & 0xff
1364
+ const l2b: i32 = toI32(l2 >>> 16) & 0xff
1365
+ const l2c: i32 = toI32(l2 >>> 8) & 0xff
1366
+ const l2d: i32 = toI32(l2) & 0xff
1367
+ const l3a: i32 = toI32(l3 >>> 24) & 0xff
1368
+ const l3b: i32 = toI32(l3 >>> 16) & 0xff
1369
+ const l3c: i32 = toI32(l3 >>> 8) & 0xff
1370
+ const l3d: i32 = toI32(l3) & 0xff
1371
+ const l4a: i32 = toI32(l4 >>> 24) & 0xff
1372
+ const l4b: i32 = toI32(l4 >>> 16) & 0xff
1373
+ const l4c: i32 = toI32(l4 >>> 8) & 0xff
1374
+ const l4d: i32 = toI32(l4) & 0xff
1375
+ const l5a: i32 = toI32(l5 >>> 24) & 0xff
1376
+ const l5b: i32 = toI32(l5 >>> 16) & 0xff
1377
+ const l5c: i32 = toI32(l5 >>> 8) & 0xff
1378
+ const l5d: i32 = toI32(l5) & 0xff
1379
+ r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1380
+ r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1381
+ r2 ^= st[2084 + n] ^ (((st[2102 + l2a] + st[2358 + l2b]) ^ st[2614 + l2c]) + st[2870 + l2d])
1382
+ r3 ^= st[3126 + n] ^ (((st[3144 + l3a] + st[3400 + l3b]) ^ st[3656 + l3c]) + st[3912 + l3d])
1383
+ r4 ^= st[4168 + n] ^ (((st[4186 + l4a] + st[4442 + l4b]) ^ st[4698 + l4c]) + st[4954 + l4d])
1384
+ r5 ^= st[5210 + n] ^ (((st[5228 + l5a] + st[5484 + l5b]) ^ st[5740 + l5c]) + st[5996 + l5d])
1385
+ const r0a: i32 = toI32(r0 >>> 24) & 0xff
1386
+ const r0b: i32 = toI32(r0 >>> 16) & 0xff
1387
+ const r0c: i32 = toI32(r0 >>> 8) & 0xff
1388
+ const r0d: i32 = toI32(r0) & 0xff
1389
+ const r1a: i32 = toI32(r1 >>> 24) & 0xff
1390
+ const r1b: i32 = toI32(r1 >>> 16) & 0xff
1391
+ const r1c: i32 = toI32(r1 >>> 8) & 0xff
1392
+ const r1d: i32 = toI32(r1) & 0xff
1393
+ const r2a: i32 = toI32(r2 >>> 24) & 0xff
1394
+ const r2b: i32 = toI32(r2 >>> 16) & 0xff
1395
+ const r2c: i32 = toI32(r2 >>> 8) & 0xff
1396
+ const r2d: i32 = toI32(r2) & 0xff
1397
+ const r3a: i32 = toI32(r3 >>> 24) & 0xff
1398
+ const r3b: i32 = toI32(r3 >>> 16) & 0xff
1399
+ const r3c: i32 = toI32(r3 >>> 8) & 0xff
1400
+ const r3d: i32 = toI32(r3) & 0xff
1401
+ const r4a: i32 = toI32(r4 >>> 24) & 0xff
1402
+ const r4b: i32 = toI32(r4 >>> 16) & 0xff
1403
+ const r4c: i32 = toI32(r4 >>> 8) & 0xff
1404
+ const r4d: i32 = toI32(r4) & 0xff
1405
+ const r5a: i32 = toI32(r5 >>> 24) & 0xff
1406
+ const r5b: i32 = toI32(r5 >>> 16) & 0xff
1407
+ const r5c: i32 = toI32(r5 >>> 8) & 0xff
1408
+ const r5d: i32 = toI32(r5) & 0xff
1409
+ l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1410
+ l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1411
+ l2 ^= st[2084 + n + 1] ^ (((st[2102 + r2a] + st[2358 + r2b]) ^ st[2614 + r2c]) + st[2870 + r2d])
1412
+ l3 ^= st[3126 + n + 1] ^ (((st[3144 + r3a] + st[3400 + r3b]) ^ st[3656 + r3c]) + st[3912 + r3d])
1413
+ l4 ^= st[4168 + n + 1] ^ (((st[4186 + r4a] + st[4442 + r4b]) ^ st[4698 + r4c]) + st[4954 + r4d])
1414
+ l5 ^= st[5210 + n + 1] ^ (((st[5228 + r5a] + st[5484 + r5b]) ^ st[5740 + r5c]) + st[5996 + r5d])
1415
+ }
1416
+ const t0: u32 = r0
1417
+ r0 = l0
1418
+ l0 = t0 ^ st[17]
1419
+ st[i] = l0
1420
+ st[i + 1] = r0
1421
+ const t1: u32 = r1
1422
+ r1 = l1
1423
+ l1 = t1 ^ st[1059]
1424
+ st[1042 + i] = l1
1425
+ st[1042 + i + 1] = r1
1426
+ const t2: u32 = r2
1427
+ r2 = l2
1428
+ l2 = t2 ^ st[2101]
1429
+ st[2084 + i] = l2
1430
+ st[2084 + i + 1] = r2
1431
+ const t3: u32 = r3
1432
+ r3 = l3
1433
+ l3 = t3 ^ st[3143]
1434
+ st[3126 + i] = l3
1435
+ st[3126 + i + 1] = r3
1436
+ const t4: u32 = r4
1437
+ r4 = l4
1438
+ l4 = t4 ^ st[4185]
1439
+ st[4168 + i] = l4
1440
+ st[4168 + i + 1] = r4
1441
+ const t5: u32 = r5
1442
+ r5 = l5
1443
+ l5 = t5 ^ st[5227]
1444
+ st[5210 + i] = l5
1445
+ st[5210 + i + 1] = r5
1446
+ }
1447
+ }
1448
+
1449
+ /** The cost-round kernel for `width` lanes, slots 0 to `width` - 1. */
1450
+ const bcryptBodyN = (st: u32[], width: i32): void => {
1451
+ if (width === 2) {
1452
+ bcryptBody2(st)
1453
+ } else if (width === 3) {
1454
+ bcryptBody3(st)
1455
+ } else if (width === 4) {
1456
+ bcryptBody4(st)
1457
+ } else if (width === 5) {
1458
+ bcryptBody5(st)
1459
+ } else if (width === 6) {
1460
+ bcryptBody6(st)
1461
+ }
1462
+ }
1463
+
1464
+ /** `bcryptEncrypt` under the state at `at`: one block, `lr[0]` and `lr[1]` in place. */
1465
+ const bcryptEncryptAt = (st: u32[], at: i32, lr: u32[]): void => {
1466
+ if (at < 0 || at + BCRYPT_STATE_WORDS > toI32(st.length)) {
1066
1467
  return
1067
1468
  }
1068
- if (lr.length < 8) {
1469
+ if (lr.length < 2) {
1069
1470
  return
1070
1471
  }
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]
1472
+ let l: u32 = lr[0] ^ st[at]
1473
+ let r: u32 = lr[1]
1079
1474
  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) {
1475
+ const la: i32 = toI32(l >>> 24) & 0xff
1476
+ const lb: i32 = toI32(l >>> 16) & 0xff
1477
+ const lc: i32 = toI32(l >>> 8) & 0xff
1478
+ const ld: i32 = toI32(l) & 0xff
1479
+ r ^= st[at + n] ^ (((st[at + 18 + la] + st[at + 274 + lb]) ^ st[at + 530 + lc]) + st[at + 786 + ld])
1480
+ const ra: i32 = toI32(r >>> 24) & 0xff
1481
+ const rb: i32 = toI32(r >>> 16) & 0xff
1482
+ const rc: i32 = toI32(r >>> 8) & 0xff
1483
+ const rd: i32 = toI32(r) & 0xff
1484
+ l ^= st[at + n + 1] ^ (((st[at + 18 + ra] + st[at + 274 + rb]) ^ st[at + 530 + rc]) + st[at + 786 + rd])
1485
+ }
1486
+ lr[0] = r ^ st[at + 17]
1487
+ lr[1] = l
1488
+ }
1489
+
1490
+ /** Each running slot's P XORed with its kept expanded key, as `bcryptXorKey`. */
1491
+ const bcryptXorKeyN = (st: u32[], width: i32): void => {
1492
+ if (toI32(st.length) < BCRYPT_BATCH_WORDS) {
1134
1493
  return
1135
1494
  }
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]
1495
+ for (let k: i32 = 0; k < width && k < BCRYPT_BATCH_LANES; k++) {
1496
+ const at: i32 = k * BCRYPT_STATE_WORDS
1497
+ const kept: i32 = k * 18 + BCRYPT_BATCH_KEYS
1498
+ for (let i: i32 = 0; i < 18; i++) {
1499
+ st[at + i] ^= st[kept + i]
1500
+ }
1141
1501
  }
1142
1502
  }
1143
1503
 
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) {
1504
+ /** Each running slot's P XORed with its salt words, as `bcryptXorSalt`. */
1505
+ const bcryptXorSaltN = (st: u32[], salts: u32[], width: i32): void => {
1506
+ if (salts.length < 24) {
1147
1507
  return
1148
1508
  }
1149
- for (let k: i32 = 0; k < 4; k++) {
1509
+ for (let k: i32 = 0; k < width && k < BCRYPT_BATCH_LANES; k++) {
1150
1510
  bcryptXorSalt(
1151
1511
  st,
1152
1512
  k * BCRYPT_STATE_WORDS,
@@ -1159,87 +1519,109 @@ const bcryptXorSalt4 = (st: u32[], salts: u32[]): void => {
1159
1519
  }
1160
1520
 
1161
1521
  /**
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.
1522
+ * The salted expansion of the state at `at`, `BF_crypt`'s first pass: each
1523
+ * block XORed with the salt words `salts[4k..]` in turn, encrypted, and
1524
+ * written over P and the S-boxes.
1166
1525
  */
1167
- const bcryptEks4 = (st: u32[], salts: u32[], cost: i32, digests: u8[]): void => {
1168
- if (st.length < 4244 || salts.length < 16) {
1526
+ const bcryptExpandAt = (st: u32[], at: i32, salts: u32[], k: i32, lr: u32[]): void => {
1527
+ if (lr.length < 2 || 4 * k + 3 >= toI32(salts.length)) {
1169
1528
  return
1170
1529
  }
1171
- const lr: u32[] = new Array<u32>(8)
1172
- for (let i: i32 = 0; i < 1042; i += 2) {
1530
+ lr[0] = 0
1531
+ lr[1] = 0
1532
+ for (let i: i32 = 0; i < BCRYPT_STATE_WORDS; i += 2) {
1173
1533
  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
- }
1534
+ lr[0] ^= salts[4 * k + half]
1535
+ lr[1] ^= salts[4 * k + half + 1]
1536
+ bcryptEncryptAt(st, at, lr)
1537
+ if (at + i + 1 < toI32(st.length)) {
1538
+ st[at + i] = lr[0]
1539
+ st[at + i + 1] = lr[1]
1185
1540
  }
1186
1541
  }
1542
+ }
1543
+
1544
+ /** `bcryptMagic` under the state at `at`: one pair of the magic string, into `dst` at `to`. */
1545
+ const bcryptMagicAt = (st: u32[], at: i32, lr: u32[], dst: u8[], to: i32, l: u32, r: u32): void => {
1546
+ if (lr.length < 2) {
1547
+ return
1548
+ }
1549
+ lr[0] = l
1550
+ lr[1] = r
1551
+ for (let n: i32 = 0; n < 64; n++) {
1552
+ bcryptEncryptAt(st, at, lr)
1553
+ }
1554
+ bcryptPutBlock(dst, to, lr[0], lr[1])
1555
+ }
1556
+
1557
+ /**
1558
+ * The batch's hash, on the batch state with its keys loaded: the salted
1559
+ * expansion, 2^cost rounds and the magic string on slots 0 to `width` - 1,
1560
+ * and the 31 digest characters of slot k into `digests` at 31k, which the
1561
+ * caller made and wipes. The block and digest copies are wiped here.
1562
+ */
1563
+ const bcryptEksN = (st: u32[], salts: u32[], cost: i32, width: i32, digests: u8[]): void => {
1564
+ if (toI32(st.length) < BCRYPT_BATCH_WORDS || salts.length < 24 || width < 2 || width > BCRYPT_BATCH_LANES) {
1565
+ return
1566
+ }
1567
+ const lr: u32[] = new Array<u32>(2)
1568
+ for (let k: i32 = 0; k < width; k++) {
1569
+ bcryptExpandAt(st, k * BCRYPT_STATE_WORDS, salts, k, lr)
1570
+ }
1187
1571
 
1188
1572
  let rounds: u32 = toU32(1) << toU32(cost)
1189
1573
  while (rounds !== 0) {
1190
- bcryptXorKey4(st)
1191
- bcryptBody4(st)
1192
- bcryptXorSalt4(st, salts)
1193
- bcryptBody4(st)
1574
+ bcryptXorKeyN(st, width)
1575
+ bcryptBodyN(st, width)
1576
+ bcryptXorSaltN(st, salts, width)
1577
+ bcryptBodyN(st, width)
1194
1578
  rounds -= 1
1195
1579
  }
1196
1580
 
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)
1581
+ const digest: u8[] = new Array<u8>(24)
1582
+ for (let k: i32 = 0; k < width; k++) {
1583
+ const at: i32 = k * BCRYPT_STATE_WORDS
1584
+ bcryptMagicAt(st, at, lr, digest, 0, 0x4f727068, 0x65616e42)
1585
+ bcryptMagicAt(st, at, lr, digest, 8, 0x65686f6c, 0x64657253)
1586
+ bcryptMagicAt(st, at, lr, digest, 16, 0x63727944, 0x6f756274)
1587
+ bcryptEncodeInto(digests, 31 * k, digest, 23)
1207
1588
  }
1208
1589
  wipe(lr)
1209
1590
  wipe(digest)
1210
- wipe(one)
1211
1591
  }
1212
1592
 
1213
1593
  /**
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.
1594
+ * The running slots' 60-byte hashes, slot k at 60k, for `exposeWith` on the
1595
+ * loaded batch state. They are the hashes `bcryptHashN` returns, so they are
1596
+ * not secret and are not wiped; the digest characters they are joined from
1597
+ * are wiped here.
1218
1598
  */
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
- }
1599
+ const bcryptHashNExposed = (st: u32[], batch: BcryptBatch): u8[] => {
1600
+ const out: u8[] = new Array<u8>(BCRYPT_BATCH_LANES * BCRYPT_HASH_LENGTH)
1601
+ const digests: u8[] = new Array<u8>(31 * BCRYPT_BATCH_LANES)
1602
+ bcryptEksN(st, batch.salts, batch.cost, batch.width, digests)
1603
+ for (let k: i32 = 0; k < batch.width && k < toI32(batch.prefixes.length); k++) {
1604
+ bcryptJoin(out, BCRYPT_HASH_LENGTH * k, batch.prefixes[k], digests, k)
1227
1605
  }
1228
1606
  wipe(digests)
1229
1607
  return out
1230
1608
  }
1231
1609
 
1232
1610
  /**
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.
1611
+ * Bit k set for each running slot whose rebuilt hash is `batch.hashes[k]`,
1612
+ * for `exposeWith` on the loaded batch state. The digest characters are built
1613
+ * and wiped here, so the bits are all that leaves.
1236
1614
  */
1237
- const bcryptVerify4Exposed = (st: u32[], batch: BcryptBatch): i32 => {
1238
- const digests: u8[] = new Array<u8>(124)
1239
- bcryptEks4(st, batch.salts, batch.cost, digests)
1615
+ const bcryptVerifyNExposed = (st: u32[], batch: BcryptBatch): i32 => {
1616
+ const digests: u8[] = new Array<u8>(31 * BCRYPT_BATCH_LANES)
1617
+ bcryptEksN(st, batch.salts, batch.cost, batch.width, digests)
1240
1618
  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])) {
1619
+ for (
1620
+ let k: i32 = 0;
1621
+ k < batch.width && k < toI32(batch.prefixes.length) && k < toI32(batch.hashes.length);
1622
+ k++
1623
+ ) {
1624
+ if (bcryptJoinedOk(batch.prefixes[k], digests, k, batch.hashes[k])) {
1243
1625
  same |= 1 << k
1244
1626
  }
1245
1627
  }
@@ -1247,59 +1629,53 @@ const bcryptVerify4Exposed = (st: u32[], batch: BcryptBatch): i32 => {
1247
1629
  return same
1248
1630
  }
1249
1631
 
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
1632
  /**
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.
1633
+ * The batch `state` set up: the six states set to π, the safety words stored
1634
+ * and the password of caller lane `lanes[s]` loaded into slot s for each of
1635
+ * the `batch.width` slots. A lane no slot names is not read.
1272
1636
  */
1273
- const bcryptLoad4 = (
1637
+ const bcryptLoadN = (
1274
1638
  state: Secret<u32[]>,
1275
1639
  p0: Secret<u8[]>,
1276
1640
  p1: Secret<u8[]> | null,
1277
1641
  p2: Secret<u8[]> | null,
1278
1642
  p3: Secret<u8[]> | null,
1643
+ p4: Secret<u8[]> | null,
1644
+ p5: Secret<u8[]> | null,
1645
+ lanes: i32[],
1279
1646
  batch: BcryptBatch
1280
1647
  ): 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)
1648
+ exposeWith(state, batch, bcryptInitN)
1649
+ for (let s: i32 = 0; s < batch.width && s < toI32(lanes.length); s++) {
1650
+ const k: i32 = lanes[s]
1651
+ if (k === 0) {
1652
+ bcryptLoadSlot(state, p0, s)
1653
+ } else if (k === 1 && p1 !== null) {
1654
+ bcryptLoadSlot(state, p1, s)
1655
+ } else if (k === 2 && p2 !== null) {
1656
+ bcryptLoadSlot(state, p2, s)
1657
+ } else if (k === 3 && p3 !== null) {
1658
+ bcryptLoadSlot(state, p3, s)
1659
+ } else if (k === 4 && p4 !== null) {
1660
+ bcryptLoadSlot(state, p4, s)
1661
+ } else if (k === 5 && p5 !== null) {
1662
+ bcryptLoadSlot(state, p5, s)
1663
+ }
1294
1664
  }
1295
1665
  }
1296
1666
 
1297
- /** Lane k's 60-byte hash out of the batch's `all`, as a string. */
1667
+ /** Slot k's 60-byte hash out of the batch's `all`, as a string. */
1298
1668
  const bcryptLaneString = (all: u8[], k: i32): string =>
1299
1669
  String.fromBytes(all, BCRYPT_HASH_LENGTH * k, BCRYPT_HASH_LENGTH * (k + 1))
1300
1670
 
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 => {
1671
+ /** Bit k set for each lane given: lane 0 always, lanes 1–5 when not null. */
1672
+ const bcryptMaskOf = (
1673
+ p1: Secret<u8[]> | null,
1674
+ p2: Secret<u8[]> | null,
1675
+ p3: Secret<u8[]> | null,
1676
+ p4: Secret<u8[]> | null,
1677
+ p5: Secret<u8[]> | null
1678
+ ): i32 => {
1303
1679
  let mask: i32 = 1
1304
1680
  if (p1 !== null) {
1305
1681
  mask |= 2
@@ -1310,82 +1686,129 @@ const bcryptMaskOf = (p1: Secret<u8[]> | null, p2: Secret<u8[]> | null, p3: Secr
1310
1686
  if (p3 !== null) {
1311
1687
  mask |= 8
1312
1688
  }
1689
+ if (p4 !== null) {
1690
+ mask |= 16
1691
+ }
1692
+ if (p5 !== null) {
1693
+ mask |= 32
1694
+ }
1313
1695
  return mask
1314
1696
  }
1315
1697
 
1698
+ /** The lanes whose bit is set in `mask`, lowest first: the caller lane each slot runs. */
1699
+ const bcryptSlotsOf = (mask: i32): i32[] => {
1700
+ const lanes: i32[] = []
1701
+ for (let k: i32 = 0; k < BCRYPT_BATCH_LANES; k++) {
1702
+ if ((mask & (1 << k)) !== 0) {
1703
+ lanes.push(k)
1704
+ }
1705
+ }
1706
+ return lanes
1707
+ }
1708
+
1316
1709
  /**
1317
- * The `$2b$` hashes of up to four passwords at once, each as `bcryptHash`
1710
+ * The `$2b$` hashes of up to six passwords at once, each as `bcryptHash`
1318
1711
  * makes it: lane k with the 16 bytes of `salts` at 16k and 2^`cost` rounds.
1319
1712
  * 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.
1713
+ * read. The w lanes given run as one computation on one thread with their
1714
+ * cost rounds interleaved, whatever positions they are in; one lane runs as
1715
+ * `bcryptHash`. Which w is fastest per hash depends on the core
1716
+ * (`bcryptCalibrate` measures it). An `Err` for `salts` of another length
1717
+ * than 96 or a cost outside 4–31.
1324
1718
  */
1325
- export const bcryptHash4 = (
1719
+ export const bcryptHashN = (
1326
1720
  p0: Secret<u8[]>,
1327
1721
  p1: Secret<u8[]> | null,
1328
1722
  p2: Secret<u8[]> | null,
1329
1723
  p3: Secret<u8[]> | null,
1724
+ p4: Secret<u8[]> | null,
1725
+ p5: Secret<u8[]> | null,
1330
1726
  salts: u8[],
1331
1727
  cost: i32
1332
1728
  ): Result<string[], string> => {
1333
- if (salts.length !== 64) {
1334
- return Err("bcrypt: the salts are not 64 bytes")
1729
+ if (salts.length !== 96) {
1730
+ return Err("bcrypt: the salts are not 96 bytes")
1335
1731
  }
1336
1732
  if (cost < BCRYPT_MIN_COST || cost > BCRYPT_MAX_COST) {
1337
1733
  return Err("bcrypt: the cost is outside 4 to 31")
1338
1734
  }
1339
- const mask: i32 = bcryptMaskOf(p1, p2, p3)
1340
- const hashes: string[] = ["", "", "", ""]
1341
- if (mask === 1) {
1735
+ const lanes: i32[] = bcryptSlotsOf(bcryptMaskOf(p1, p2, p3, p4, p5))
1736
+ if (lanes.length === 1) {
1342
1737
  const one: Result<string, string> = bcryptHash(p0, bcryptSaltOf(salts, 0), cost)
1343
1738
  if (one.isErr()) {
1344
1739
  return Err(one.error)
1345
1740
  }
1346
- hashes[0] = one.value
1347
- return Ok(hashes)
1741
+ return Ok([one.value, "", "", "", "", ""])
1348
1742
  }
1349
- const words: u32[] = new Array<u32>(16)
1743
+ const words: u32[] = new Array<u32>(24)
1350
1744
  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))
1745
+ for (let s: i32 = 0; s < toI32(lanes.length); s++) {
1746
+ prefixes.push(bcryptBatchSalt(words, salts, lanes[s], s, cost))
1357
1747
  }
1358
1748
  const none: string[] = []
1359
1749
  const batch: BcryptBatch = {
1360
1750
  salts: words,
1361
- safety: new Array<u32>(4),
1751
+ safety: new Array<u32>(BCRYPT_BATCH_LANES),
1362
1752
  cost: cost,
1363
- mask: mask,
1753
+ width: toI32(lanes.length),
1364
1754
  prefixes: prefixes,
1365
1755
  hashes: none,
1366
1756
  }
1367
1757
  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)
1758
+ bcryptLoadN(state, p0, p1, p2, p3, p4, p5, lanes, batch)
1759
+ const all: u8[] = exposeWith(state, batch, bcryptHashNExposed)
1370
1760
  wipe(state)
1761
+ // Slot s ran lane lanes[s]; a lane not given stays "".
1762
+ const out: string[] = ["", "", "", "", "", ""]
1763
+ for (let s: i32 = 0; s < toI32(lanes.length); s++) {
1764
+ out[lanes[s]] = bcryptLaneString(all, s)
1765
+ }
1766
+ return Ok(out)
1767
+ }
1768
+
1769
+ /**
1770
+ * `bcryptHashN` for up to four passwords, `salts` 64 bytes: the same hashes,
1771
+ * four strings. An `Err` for `salts` of another length than 64 or a cost
1772
+ * outside 4–31.
1773
+ */
1774
+ export const bcryptHash4 = (
1775
+ p0: Secret<u8[]>,
1776
+ p1: Secret<u8[]> | null,
1777
+ p2: Secret<u8[]> | null,
1778
+ p3: Secret<u8[]> | null,
1779
+ salts: u8[],
1780
+ cost: i32
1781
+ ): Result<string[], string> => {
1782
+ if (salts.length !== 64) {
1783
+ return Err("bcrypt: the salts are not 64 bytes")
1784
+ }
1785
+ const wide: u8[] = new Array<u8>(96)
1786
+ for (let i: i32 = 0; i < 64 && i < toI32(salts.length); i++) {
1787
+ wide[i] = salts[i]
1788
+ }
1789
+ const made: Result<string[], string> = bcryptHashN(p0, p1, p2, p3, null, null, wide, cost)
1790
+ if (made.isErr()) {
1791
+ return Err(made.error)
1792
+ }
1793
+ const six: string[] = made.value
1371
1794
  const out: string[] = []
1372
- for (let k: i32 = 0; k < 4; k++) {
1373
- out.push((mask & (1 << k)) !== 0 ? bcryptLaneString(all, k) : "")
1795
+ for (let k: i32 = 0; k < 4 && k < toI32(six.length); k++) {
1796
+ out.push(six[k])
1374
1797
  }
1375
1798
  return Ok(out)
1376
1799
  }
1377
1800
 
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[] => {
1801
+ /** Lane `k`'s salt from the 96 bytes of `salts` into `words` at 4s as four words, and its 29-byte setting. */
1802
+ const bcryptBatchSalt = (words: u32[], salts: u8[], k: i32, s: i32, cost: i32): u8[] => {
1380
1803
  const salt: u8[] = bcryptSaltOf(salts, k)
1381
1804
  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]
1805
+ for (let j: i32 = 0; j < 4 && j < toI32(four.length) && 4 * s + j < toI32(words.length); j++) {
1806
+ words[4 * s + j] = four[j]
1384
1807
  }
1385
1808
  return bcryptSettingOf(salt, cost)
1386
1809
  }
1387
1810
 
1388
- /** The 16 salt bytes of lane `k` from the 64 of a batch. */
1811
+ /** The 16 salt bytes of lane `k` from the bytes of a batch. */
1389
1812
  const bcryptSaltOf = (salts: u8[], k: i32): u8[] => {
1390
1813
  const salt: u8[] = new Array<u8>(16)
1391
1814
  for (let j: i32 = 0; j < 16 && 16 * k + j < toI32(salts.length); j++) {
@@ -1394,7 +1817,7 @@ const bcryptSaltOf = (salts: u8[], k: i32): u8[] => {
1394
1817
  return salt
1395
1818
  }
1396
1819
 
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. */
1820
+ /** The 60 bytes of slot `k`'s hash into `out` at `at`: its 29-byte setting, then its 31 digest characters from the batch's. */
1398
1821
  const bcryptJoin = (out: u8[], at: i32, prefix: u8[], digests: u8[], k: i32): void => {
1399
1822
  bcryptPut(out, at, prefix)
1400
1823
  const o: i32 = at + BCRYPT_SETTING_LENGTH
@@ -1408,7 +1831,7 @@ const bcryptJoin = (out: u8[], at: i32, prefix: u8[], digests: u8[], k: i32): vo
1408
1831
  }
1409
1832
 
1410
1833
  /**
1411
- * Whether each of `hashes` (four strings) is its lane's password's, as
1834
+ * Whether each of `hashes` (six strings) is its lane's password's, as
1412
1835
  * `bcryptVerify` decides it: lane 0 always, a null lane `false` and its
1413
1836
  * string not read. Lanes are grouped by the cost their hash names and each
1414
1837
  * group of two or more runs as one interleaved computation; a group of one
@@ -1416,19 +1839,21 @@ const bcryptJoin = (out: u8[], at: i32, prefix: u8[], digests: u8[], k: i32): vo
1416
1839
  * call an `Err` naming the lane ("bcrypt: lane 2: …") before any hashing, so
1417
1840
  * one bad row in a batch cannot be read as a wrong password.
1418
1841
  */
1419
- export const bcryptVerify4 = (
1842
+ export const bcryptVerifyN = (
1420
1843
  p0: Secret<u8[]>,
1421
1844
  p1: Secret<u8[]> | null,
1422
1845
  p2: Secret<u8[]> | null,
1423
1846
  p3: Secret<u8[]> | null,
1847
+ p4: Secret<u8[]> | null,
1848
+ p5: Secret<u8[]> | null,
1424
1849
  hashes: string[]
1425
1850
  ): Result<boolean[], string> => {
1426
- if (hashes.length !== 4) {
1427
- return Err("bcrypt: a batch is four hashes")
1851
+ if (hashes.length !== 6) {
1852
+ return Err("bcrypt: a batch is six hashes")
1428
1853
  }
1429
- const mask: i32 = bcryptMaskOf(p1, p2, p3)
1854
+ const mask: i32 = bcryptMaskOf(p1, p2, p3, p4, p5)
1430
1855
  const lanes: BcryptParsed[] = []
1431
- for (let k: i32 = 0; k < 4 && k < toI32(hashes.length); k++) {
1856
+ for (let k: i32 = 0; k < BCRYPT_BATCH_LANES && k < toI32(hashes.length); k++) {
1432
1857
  if ((mask & (1 << k)) !== 0) {
1433
1858
  const parsed: Result<BcryptParsed, string> = bcryptParseHash(hashes[k])
1434
1859
  if (parsed.isErr()) {
@@ -1443,41 +1868,75 @@ export const bcryptVerify4 = (
1443
1868
  })
1444
1869
  }
1445
1870
  }
1446
- const ok: boolean[] = [false, false, false, false]
1447
- let done: i32 = ~mask & 15
1871
+ const ok: boolean[] = [false, false, false, false, false, false]
1872
+ let done: i32 = ~mask & 63
1448
1873
  for (
1449
1874
  let k: i32 = 0;
1450
- k < 4 && k < toI32(lanes.length) && k < toI32(hashes.length) && k < toI32(ok.length);
1875
+ k < BCRYPT_BATCH_LANES && k < toI32(lanes.length) && k < toI32(hashes.length) && k < toI32(ok.length);
1451
1876
  k++
1452
1877
  ) {
1453
1878
  if ((done & (1 << k)) === 0) {
1454
1879
  const cost: i32 = lanes[k].setting.cost
1455
1880
  let group: i32 = 0
1456
- for (let j: i32 = k; j < 4 && j < toI32(lanes.length); j++) {
1881
+ for (let j: i32 = k; j < BCRYPT_BATCH_LANES && j < toI32(lanes.length); j++) {
1457
1882
  if ((done & (1 << j)) === 0 && lanes[j].setting.cost === cost) {
1458
1883
  group |= 1 << j
1459
1884
  }
1460
1885
  }
1461
1886
  done |= group
1462
1887
  if (group === 1 << k) {
1463
- const one: boolean = bcryptLaneOk(p0, p1, p2, p3, k, lanes[k], hashes[k])
1888
+ const one: boolean = bcryptLaneOk(p0, p1, p2, p3, p4, p5, k, lanes[k], hashes[k])
1464
1889
  if (k < toI32(ok.length)) {
1465
1890
  ok[k] = one
1466
1891
  }
1467
1892
  } else {
1468
- bcryptVerifyGroup(p0, p1, p2, p3, group, lanes, hashes, ok)
1893
+ bcryptVerifyGroup(p0, p1, p2, p3, p4, p5, group, lanes, hashes, ok)
1469
1894
  }
1470
1895
  }
1471
1896
  }
1472
1897
  return Ok(ok)
1473
1898
  }
1474
1899
 
1900
+ /**
1901
+ * `bcryptVerifyN` for up to four passwords and four hashes: the same
1902
+ * answers, four of them, and the same lane errors.
1903
+ */
1904
+ export const bcryptVerify4 = (
1905
+ p0: Secret<u8[]>,
1906
+ p1: Secret<u8[]> | null,
1907
+ p2: Secret<u8[]> | null,
1908
+ p3: Secret<u8[]> | null,
1909
+ hashes: string[]
1910
+ ): Result<boolean[], string> => {
1911
+ if (hashes.length !== 4) {
1912
+ return Err("bcrypt: a batch is four hashes")
1913
+ }
1914
+ const six: string[] = []
1915
+ for (let k: i32 = 0; k < toI32(hashes.length); k++) {
1916
+ six.push(hashes[k])
1917
+ }
1918
+ six.push("")
1919
+ six.push("")
1920
+ const said: Result<boolean[], string> = bcryptVerifyN(p0, p1, p2, p3, null, null, six)
1921
+ if (said.isErr()) {
1922
+ return Err(said.error)
1923
+ }
1924
+ const all: boolean[] = said.value
1925
+ const out: boolean[] = []
1926
+ for (let k: i32 = 0; k < 4 && k < toI32(all.length); k++) {
1927
+ out.push(all[k])
1928
+ }
1929
+ return Ok(out)
1930
+ }
1931
+
1475
1932
  /** Whether lane `k`'s password, hashed alone with `lane`'s setting, gives `hash`. */
1476
1933
  const bcryptLaneOk = (
1477
1934
  p0: Secret<u8[]>,
1478
1935
  p1: Secret<u8[]> | null,
1479
1936
  p2: Secret<u8[]> | null,
1480
1937
  p3: Secret<u8[]> | null,
1938
+ p4: Secret<u8[]> | null,
1939
+ p5: Secret<u8[]> | null,
1481
1940
  k: i32,
1482
1941
  lane: BcryptParsed,
1483
1942
  hash: string
@@ -1494,10 +1953,16 @@ const bcryptLaneOk = (
1494
1953
  if (k === 3 && p3 !== null) {
1495
1954
  return bcryptVerifyOne(p3, lane, hash)
1496
1955
  }
1956
+ if (k === 4 && p4 !== null) {
1957
+ return bcryptVerifyOne(p4, lane, hash)
1958
+ }
1959
+ if (k === 5 && p5 !== null) {
1960
+ return bcryptVerifyOne(p5, lane, hash)
1961
+ }
1497
1962
  return false
1498
1963
  }
1499
1964
 
1500
- /** Whether lane `k`'s hash joined from `prefix` and the batch's digests is `hash`. */
1965
+ /** Whether slot `k`'s hash joined from `prefix` and the batch's digests is `hash`. */
1501
1966
  const bcryptJoinedOk = (prefix: u8[], digests: u8[], k: i32, hash: string): boolean => {
1502
1967
  const joined: u8[] = new Array<u8>(BCRYPT_HASH_LENGTH)
1503
1968
  bcryptJoin(joined, 0, prefix, digests, k)
@@ -1510,48 +1975,119 @@ const bcryptVerifyGroup = (
1510
1975
  p1: Secret<u8[]> | null,
1511
1976
  p2: Secret<u8[]> | null,
1512
1977
  p3: Secret<u8[]> | null,
1978
+ p4: Secret<u8[]> | null,
1979
+ p5: Secret<u8[]> | null,
1513
1980
  group: i32,
1514
1981
  lanes: BcryptParsed[],
1515
1982
  hashes: string[],
1516
1983
  ok: boolean[]
1517
1984
  ): void => {
1518
- const words: u32[] = new Array<u32>(16)
1519
- const safety: u32[] = new Array<u32>(4)
1985
+ const words: u32[] = new Array<u32>(24)
1986
+ const safety: u32[] = new Array<u32>(BCRYPT_BATCH_LANES)
1520
1987
  const prefixes: u8[][] = []
1988
+ // Copies, so no callee is handed the caller's `hashes` (the thread rule).
1521
1989
  const given: string[] = []
1990
+ // The lanes loaded, in slot order: slot s holds lane slots[s].
1991
+ const slots: i32[] = []
1522
1992
  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
1993
+ for (const k of bcryptSlotsOf(group)) {
1994
+ const s: i32 = toI32(slots.length)
1995
+ if (s < toI32(safety.length) && k >= 0 && k < toI32(lanes.length) && k < toI32(hashes.length)) {
1996
+ const lane: BcryptParsed = lanes[k]
1997
+ const hash: string = hashes[k]
1998
+ const setting: BcryptSetting = lane.setting
1533
1999
  cost = setting.cost
1534
- safety[k] = setting.safety
2000
+ safety[s] = setting.safety
1535
2001
  for (let j: i32 = 0; j < 4 && j < toI32(setting.salt.length); j++) {
1536
- words[4 * k + j] = setting.salt[j]
2002
+ words[4 * s + j] = setting.salt[j]
1537
2003
  }
2004
+ given.push(hash)
2005
+ prefixes.push(lane.prefix)
2006
+ slots.push(k)
1538
2007
  }
1539
2008
  }
1540
2009
  const batch: BcryptBatch = {
1541
2010
  salts: words,
1542
2011
  safety: safety,
1543
2012
  cost: cost,
1544
- mask: group,
2013
+ width: toI32(prefixes.length),
1545
2014
  prefixes: prefixes,
1546
2015
  hashes: given,
1547
2016
  }
1548
2017
  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)
2018
+ bcryptLoadN(state, p0, p1, p2, p3, p4, p5, slots, batch)
2019
+ const same: i32 = exposeWith(state, batch, bcryptVerifyNExposed)
1551
2020
  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
2021
+ for (let s: i32 = 0; s < toI32(slots.length); s++) {
2022
+ const k: i32 = slots[s]
2023
+ if (k >= 0 && k < toI32(ok.length)) {
2024
+ ok[k] = (same & (1 << s)) !== 0
2025
+ }
2026
+ }
2027
+ }
2028
+
2029
+ /** The time a width that never hashed is given: an hour, in nanoseconds. */
2030
+ const BCRYPT_CALIBRATE_NEVER: i64 = 3600000000000
2031
+
2032
+ /** Throwaway passwords hashed when calibrating: one byte each, not secret, wiped all the same. */
2033
+ const bcryptCalibrateKey = (k: i32): Secret<u8[]> => secret([toU8(0x41 + k)])
2034
+
2035
+ /** Nanoseconds of one cost-4 `bcryptHashN` of `width` throwaway passwords, the least of a few runs. */
2036
+ const bcryptCalibrateTime = (width: i32): i64 => {
2037
+ const salts: u8[] = new Array<u8>(96)
2038
+ let best: i64 = -1
2039
+ for (let run: i32 = 0; run < 3; run++) {
2040
+ const p0: Secret<u8[]> = bcryptCalibrateKey(0)
2041
+ const p1: Secret<u8[]> | null = width > 1 ? bcryptCalibrateKey(1) : null
2042
+ const p2: Secret<u8[]> | null = width > 2 ? bcryptCalibrateKey(2) : null
2043
+ const p3: Secret<u8[]> | null = width > 3 ? bcryptCalibrateKey(3) : null
2044
+ const p4: Secret<u8[]> | null = width > 4 ? bcryptCalibrateKey(4) : null
2045
+ const p5: Secret<u8[]> | null = width > 5 ? bcryptCalibrateKey(5) : null
2046
+ const start: i64 = monotonicNanos()
2047
+ const made: Result<string[], string> = bcryptHashN(p0, p1, p2, p3, p4, p5, salts, BCRYPT_MIN_COST)
2048
+ const took: i64 = monotonicNanos() - start
2049
+ if (made.isOk() && (best < 0 || took < best)) {
2050
+ best = took
2051
+ }
2052
+ wipe(p0)
2053
+ if (p1 !== null) {
2054
+ wipe(p1)
2055
+ }
2056
+ if (p2 !== null) {
2057
+ wipe(p2)
2058
+ }
2059
+ if (p3 !== null) {
2060
+ wipe(p3)
2061
+ }
2062
+ if (p4 !== null) {
2063
+ wipe(p4)
2064
+ }
2065
+ if (p5 !== null) {
2066
+ wipe(p5)
2067
+ }
2068
+ }
2069
+ // No run hashed: an hour, so this width never wins and the products stay in range.
2070
+ return best < 0 ? BCRYPT_CALIBRATE_NEVER : best
2071
+ }
2072
+
2073
+ /**
2074
+ * The batch width, 1 to 6, that hashes fastest per password on this core:
2075
+ * each width timed on throwaway passwords at cost 4, the least time of a few
2076
+ * runs divided by the width. It measures rather than guesses, since the best
2077
+ * width turns on the core's load ports, its registers and what else is
2078
+ * running, and it keeps nothing between calls, so call it once, at startup,
2079
+ * and keep the answer. A few tens of milliseconds of one core.
2080
+ */
2081
+ export const bcryptCalibrate = (): i32 => {
2082
+ let best: i32 = 1
2083
+ let bestTime: i64 = bcryptCalibrateTime(1)
2084
+ for (let w: i32 = 2; w <= BCRYPT_BATCH_LANES; w++) {
2085
+ const took: i64 = bcryptCalibrateTime(w)
2086
+ // took / w < bestTime / best, without dividing.
2087
+ if (took * toI64(best) < bestTime * toI64(w)) {
2088
+ best = w
2089
+ bestTime = took
1555
2090
  }
1556
2091
  }
2092
+ return best
1557
2093
  }