argon2.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. import { number as assertNumber } from './_assert.js';
  2. import { toBytes, u8, u32 } from './utils.js';
  3. import { blake2b } from './blake2b.js';
  4. import { add3H, add3L, rotr32H, rotr32L, rotrBH, rotrBL, rotrSH, rotrSL } from './_u64.js';
  5. const ARGON2_SYNC_POINTS = 4;
  6. const toBytesOptional = (buf) => (buf !== undefined ? toBytes(buf) : new Uint8Array([]));
  7. function mul(a, b) {
  8. const aL = a & 0xffff;
  9. const aH = a >>> 16;
  10. const bL = b & 0xffff;
  11. const bH = b >>> 16;
  12. const ll = Math.imul(aL, bL);
  13. const hl = Math.imul(aH, bL);
  14. const lh = Math.imul(aL, bH);
  15. const hh = Math.imul(aH, bH);
  16. const BUF = ((ll >>> 16) + (hl & 0xffff) + lh) | 0;
  17. const h = ((hl >>> 16) + (BUF >>> 16) + hh) | 0;
  18. return { h, l: (BUF << 16) | (ll & 0xffff) };
  19. }
  20. function relPos(areaSize, relativePos) {
  21. // areaSize - 1 - ((areaSize * ((relativePos ** 2) >>> 32)) >>> 32)
  22. return areaSize - 1 - mul(areaSize, mul(relativePos, relativePos).h).h;
  23. }
  24. function mul2(a, b) {
  25. // 2 * a * b (via shifts)
  26. const { h, l } = mul(a, b);
  27. return { h: ((h << 1) | (l >>> 31)) & 4294967295, l: (l << 1) & 4294967295 };
  28. }
  29. function blamka(Ah, Al, Bh, Bl) {
  30. const { h: Ch, l: Cl } = mul2(Al, Bl);
  31. // A + B + (2 * A * B)
  32. const Rll = add3L(Al, Bl, Cl);
  33. return { h: add3H(Rll, Ah, Bh, Ch), l: Rll | 0 };
  34. }
  35. // Temporary block buffer
  36. const A2_BUF = new Uint32Array(256);
  37. function G(a, b, c, d) {
  38. let Al = A2_BUF[2 * a], Ah = A2_BUF[2 * a + 1]; // prettier-ignore
  39. let Bl = A2_BUF[2 * b], Bh = A2_BUF[2 * b + 1]; // prettier-ignore
  40. let Cl = A2_BUF[2 * c], Ch = A2_BUF[2 * c + 1]; // prettier-ignore
  41. let Dl = A2_BUF[2 * d], Dh = A2_BUF[2 * d + 1]; // prettier-ignore
  42. ({ h: Ah, l: Al } = blamka(Ah, Al, Bh, Bl));
  43. ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
  44. ({ Dh, Dl } = { Dh: rotr32H(Dh, Dl), Dl: rotr32L(Dh, Dl) });
  45. ({ h: Ch, l: Cl } = blamka(Ch, Cl, Dh, Dl));
  46. ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
  47. ({ Bh, Bl } = { Bh: rotrSH(Bh, Bl, 24), Bl: rotrSL(Bh, Bl, 24) });
  48. ({ h: Ah, l: Al } = blamka(Ah, Al, Bh, Bl));
  49. ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });
  50. ({ Dh, Dl } = { Dh: rotrSH(Dh, Dl, 16), Dl: rotrSL(Dh, Dl, 16) });
  51. ({ h: Ch, l: Cl } = blamka(Ch, Cl, Dh, Dl));
  52. ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });
  53. ({ Bh, Bl } = { Bh: rotrBH(Bh, Bl, 63), Bl: rotrBL(Bh, Bl, 63) });
  54. (A2_BUF[2 * a] = Al), (A2_BUF[2 * a + 1] = Ah);
  55. (A2_BUF[2 * b] = Bl), (A2_BUF[2 * b + 1] = Bh);
  56. (A2_BUF[2 * c] = Cl), (A2_BUF[2 * c + 1] = Ch);
  57. (A2_BUF[2 * d] = Dl), (A2_BUF[2 * d + 1] = Dh);
  58. }
  59. // prettier-ignore
  60. function P(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12, v13, v14, v15) {
  61. G(v00, v04, v08, v12);
  62. G(v01, v05, v09, v13);
  63. G(v02, v06, v10, v14);
  64. G(v03, v07, v11, v15);
  65. G(v00, v05, v10, v15);
  66. G(v01, v06, v11, v12);
  67. G(v02, v07, v08, v13);
  68. G(v03, v04, v09, v14);
  69. }
  70. function block(x, xPos, yPos, outPos, needXor) {
  71. for (let i = 0; i < 256; i++)
  72. A2_BUF[i] = x[xPos + i] ^ x[yPos + i];
  73. // columns
  74. for (let i = 0; i < 128; i += 16) {
  75. // prettier-ignore
  76. P(i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, i + 8, i + 9, i + 10, i + 11, i + 12, i + 13, i + 14, i + 15);
  77. }
  78. // rows
  79. for (let i = 0; i < 16; i += 2) {
  80. // prettier-ignore
  81. P(i, i + 1, i + 16, i + 17, i + 32, i + 33, i + 48, i + 49, i + 64, i + 65, i + 80, i + 81, i + 96, i + 97, i + 112, i + 113);
  82. }
  83. if (needXor)
  84. for (let i = 0; i < 256; i++)
  85. x[outPos + i] ^= A2_BUF[i] ^ x[xPos + i] ^ x[yPos + i];
  86. else
  87. for (let i = 0; i < 256; i++)
  88. x[outPos + i] = A2_BUF[i] ^ x[xPos + i] ^ x[yPos + i];
  89. }
  90. // Variable-Length Hash Function H'
  91. function Hp(A, dkLen) {
  92. const A8 = u8(A);
  93. const T = new Uint32Array(1);
  94. const T8 = u8(T);
  95. T[0] = dkLen;
  96. // Fast path
  97. if (dkLen <= 64)
  98. return blake2b.create({ dkLen }).update(T8).update(A8).digest();
  99. const out = new Uint8Array(dkLen);
  100. let V = blake2b.create({}).update(T8).update(A8).digest();
  101. let pos = 0;
  102. // First block
  103. out.set(V.subarray(0, 32));
  104. pos += 32;
  105. // Rest blocks
  106. for (; dkLen - pos > 64; pos += 32)
  107. out.set((V = blake2b(V)).subarray(0, 32), pos);
  108. // Last block
  109. out.set(blake2b(V, { dkLen: dkLen - pos }), pos);
  110. return u32(out);
  111. }
  112. function indexAlpha(r, s, laneLen, segmentLen, index, randL, sameLane = false) {
  113. let area;
  114. if (0 == r) {
  115. if (0 == s)
  116. area = index - 1;
  117. else if (sameLane)
  118. area = s * segmentLen + index - 1;
  119. else
  120. area = s * segmentLen + (index == 0 ? -1 : 0);
  121. }
  122. else if (sameLane)
  123. area = laneLen - segmentLen + index - 1;
  124. else
  125. area = laneLen - segmentLen + (index == 0 ? -1 : 0);
  126. const startPos = r !== 0 && s !== ARGON2_SYNC_POINTS - 1 ? (s + 1) * segmentLen : 0;
  127. const rel = relPos(area, randL);
  128. // NOTE: check about overflows here
  129. // absPos = (startPos + relPos) % laneLength;
  130. return (startPos + rel) % laneLen;
  131. }
  132. function argon2Init(type, password, salt, opts) {
  133. password = toBytes(password);
  134. salt = toBytes(salt);
  135. let { p, dkLen, m, t, version, key, personalization, maxmem, onProgress } = {
  136. ...opts,
  137. version: opts.version || 0x13,
  138. dkLen: opts.dkLen || 32,
  139. maxmem: 2 ** 32,
  140. };
  141. // Validation
  142. assertNumber(p);
  143. assertNumber(dkLen);
  144. assertNumber(m);
  145. assertNumber(t);
  146. assertNumber(version);
  147. if (dkLen < 4 || dkLen >= 2 ** 32)
  148. throw new Error('Argon2: dkLen should be at least 4 bytes');
  149. if (p < 1 || p >= 2 ** 32)
  150. throw new Error('Argon2: p (parallelism) should be at least 1');
  151. if (t < 1 || t >= 2 ** 32)
  152. throw new Error('Argon2: t (iterations) should be at least 1');
  153. if (m < 8 * p)
  154. throw new Error(`Argon2: memory should be at least 8*p bytes`);
  155. if (version !== 16 && version !== 19)
  156. throw new Error(`Argon2: unknown version=${version}`);
  157. password = toBytes(password);
  158. if (password.length < 0 || password.length >= 2 ** 32)
  159. throw new Error('Argon2: password should be less than 4 GB');
  160. salt = toBytes(salt);
  161. if (salt.length < 8)
  162. throw new Error('Argon2: salt should be at least 8 bytes');
  163. key = toBytesOptional(key);
  164. personalization = toBytesOptional(personalization);
  165. if (onProgress !== undefined && typeof onProgress !== 'function')
  166. throw new Error('progressCb should be function');
  167. // Params
  168. const lanes = p;
  169. // m' = 4 * p * floor (m / 4p)
  170. const mP = 4 * p * Math.floor(m / (ARGON2_SYNC_POINTS * p));
  171. //q = m' / p columns
  172. const laneLen = Math.floor(mP / p);
  173. const segmentLen = Math.floor(laneLen / ARGON2_SYNC_POINTS);
  174. // H0
  175. const h = blake2b.create({});
  176. const BUF = new Uint32Array(1);
  177. const BUF8 = u8(BUF);
  178. for (const i of [p, dkLen, m, t, version, type]) {
  179. if (i < 0 || i >= 2 ** 32)
  180. throw new Error(`Argon2: wrong parameter=${i}, expected uint32`);
  181. BUF[0] = i;
  182. h.update(BUF8);
  183. }
  184. for (let i of [password, salt, key, personalization]) {
  185. BUF[0] = i.length;
  186. h.update(BUF8).update(i);
  187. }
  188. const H0 = new Uint32Array(18);
  189. const H0_8 = u8(H0);
  190. h.digestInto(H0_8);
  191. // 256 u32 = 1024 (BLOCK_SIZE)
  192. const memUsed = mP * 256;
  193. if (memUsed < 0 || memUsed >= 2 ** 32 || memUsed > maxmem) {
  194. throw new Error(`Argon2: wrong params (memUsed=${memUsed} maxmem=${maxmem}), should be less than 2**32`);
  195. }
  196. const B = new Uint32Array(memUsed);
  197. // Fill first blocks
  198. for (let l = 0; l < p; l++) {
  199. const i = 256 * laneLen * l;
  200. // B[i][0] = H'^(1024)(H_0 || LE32(0) || LE32(i))
  201. H0[17] = l;
  202. H0[16] = 0;
  203. B.set(Hp(H0, 1024), i);
  204. // B[i][1] = H'^(1024)(H_0 || LE32(1) || LE32(i))
  205. H0[16] = 1;
  206. B.set(Hp(H0, 1024), i + 256);
  207. }
  208. let perBlock = () => { };
  209. if (onProgress) {
  210. const totalBlock = t * ARGON2_SYNC_POINTS * p * segmentLen;
  211. // Invoke callback if progress changes from 10.01 to 10.02
  212. // Allows to draw smooth progress bar on up to 8K screen
  213. const callbackPer = Math.max(Math.floor(totalBlock / 10000), 1);
  214. let blockCnt = 0;
  215. perBlock = () => {
  216. blockCnt++;
  217. if (onProgress && (!(blockCnt % callbackPer) || blockCnt === totalBlock))
  218. onProgress(blockCnt / totalBlock);
  219. };
  220. }
  221. return { type, mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock };
  222. }
  223. function argon2Output(B, p, laneLen, dkLen) {
  224. const B_final = new Uint32Array(256);
  225. for (let l = 0; l < p; l++)
  226. for (let j = 0; j < 256; j++)
  227. B_final[j] ^= B[256 * (laneLen * l + laneLen - 1) + j];
  228. return u8(Hp(B_final, dkLen));
  229. }
  230. function processBlock(B, address, l, r, s, index, laneLen, segmentLen, lanes, offset, prev, dataIndependent, needXor) {
  231. if (offset % laneLen)
  232. prev = offset - 1;
  233. let randL, randH;
  234. if (dataIndependent) {
  235. if (index % 128 === 0) {
  236. address[256 + 12]++;
  237. block(address, 256, 2 * 256, 0, false);
  238. block(address, 0, 2 * 256, 0, false);
  239. }
  240. randL = address[2 * (index % 128)];
  241. randH = address[2 * (index % 128) + 1];
  242. }
  243. else {
  244. const T = 256 * prev;
  245. randL = B[T];
  246. randH = B[T + 1];
  247. }
  248. // address block
  249. const refLane = r === 0 && s === 0 ? l : randH % lanes;
  250. const refPos = indexAlpha(r, s, laneLen, segmentLen, index, randL, refLane == l);
  251. const refBlock = laneLen * refLane + refPos;
  252. // B[i][j] = G(B[i][j-1], B[l][z])
  253. block(B, 256 * prev, 256 * refBlock, offset * 256, needXor);
  254. }
  255. function argon2(type, password, salt, opts) {
  256. const { mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock } = argon2Init(type, password, salt, opts);
  257. // Pre-loop setup
  258. // [address, input, zero_block] format so we can pass single U32 to block function
  259. const address = new Uint32Array(3 * 256);
  260. address[256 + 6] = mP;
  261. address[256 + 8] = t;
  262. address[256 + 10] = type;
  263. for (let r = 0; r < t; r++) {
  264. const needXor = r !== 0 && version === 0x13;
  265. address[256 + 0] = r;
  266. for (let s = 0; s < ARGON2_SYNC_POINTS; s++) {
  267. address[256 + 4] = s;
  268. const dataIndependent = type == 1 /* Types.Argon2i */ || (type == 2 /* Types.Argon2id */ && r === 0 && s < 2);
  269. for (let l = 0; l < p; l++) {
  270. address[256 + 2] = l;
  271. address[256 + 12] = 0;
  272. let startPos = 0;
  273. if (r === 0 && s === 0) {
  274. startPos = 2;
  275. if (dataIndependent) {
  276. address[256 + 12]++;
  277. block(address, 256, 2 * 256, 0, false);
  278. block(address, 0, 2 * 256, 0, false);
  279. }
  280. }
  281. // current block postion
  282. let offset = l * laneLen + s * segmentLen + startPos;
  283. // previous block position
  284. let prev = offset % laneLen ? offset - 1 : offset + laneLen - 1;
  285. for (let index = startPos; index < segmentLen; index++, offset++, prev++) {
  286. perBlock();
  287. processBlock(B, address, l, r, s, index, laneLen, segmentLen, lanes, offset, prev, dataIndependent, needXor);
  288. }
  289. }
  290. }
  291. }
  292. return argon2Output(B, p, laneLen, dkLen);
  293. }
  294. export const argon2d = (password, salt, opts) => argon2(0 /* Types.Argond2d */, password, salt, opts);
  295. export const argon2i = (password, salt, opts) => argon2(1 /* Types.Argon2i */, password, salt, opts);
  296. export const argon2id = (password, salt, opts) => argon2(2 /* Types.Argon2id */, password, salt, opts);
  297. //# sourceMappingURL=argon2.js.map