argon2.ts 12 KB

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