pbkdf2.js 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { hash as assertHash, number as assertNumber } from './_assert.js';
  2. import { hmac } from './hmac.js';
  3. import { createView, toBytes, checkOpts, asyncLoop } from './utils.js';
  4. // Common prologue and epilogue for sync/async functions
  5. function pbkdf2Init(hash, _password, _salt, _opts) {
  6. assertHash(hash);
  7. const opts = checkOpts({ dkLen: 32, asyncTick: 10 }, _opts);
  8. const { c, dkLen, asyncTick } = opts;
  9. assertNumber(c);
  10. assertNumber(dkLen);
  11. assertNumber(asyncTick);
  12. if (c < 1)
  13. throw new Error('PBKDF2: iterations (c) should be >= 1');
  14. const password = toBytes(_password);
  15. const salt = toBytes(_salt);
  16. // DK = PBKDF2(PRF, Password, Salt, c, dkLen);
  17. const DK = new Uint8Array(dkLen);
  18. // U1 = PRF(Password, Salt + INT_32_BE(i))
  19. const PRF = hmac.create(hash, password);
  20. const PRFSalt = PRF._cloneInto().update(salt);
  21. return { c, dkLen, asyncTick, DK, PRF, PRFSalt };
  22. }
  23. function pbkdf2Output(PRF, PRFSalt, DK, prfW, u) {
  24. PRF.destroy();
  25. PRFSalt.destroy();
  26. if (prfW)
  27. prfW.destroy();
  28. u.fill(0);
  29. return DK;
  30. }
  31. /**
  32. * PBKDF2-HMAC: RFC 2898 key derivation function
  33. * @param hash - hash function that would be used e.g. sha256
  34. * @param password - password from which a derived key is generated
  35. * @param salt - cryptographic salt
  36. * @param opts - {c, dkLen} where c is work factor and dkLen is output message size
  37. */
  38. export function pbkdf2(hash, password, salt, opts) {
  39. const { c, dkLen, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, opts);
  40. let prfW; // Working copy
  41. const arr = new Uint8Array(4);
  42. const view = createView(arr);
  43. const u = new Uint8Array(PRF.outputLen);
  44. // DK = T1 + T2 + ⋯ + Tdklen/hlen
  45. for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) {
  46. // Ti = F(Password, Salt, c, i)
  47. const Ti = DK.subarray(pos, pos + PRF.outputLen);
  48. view.setInt32(0, ti, false);
  49. // F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc
  50. // U1 = PRF(Password, Salt + INT_32_BE(i))
  51. (prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u);
  52. Ti.set(u.subarray(0, Ti.length));
  53. for (let ui = 1; ui < c; ui++) {
  54. // Uc = PRF(Password, Uc−1)
  55. PRF._cloneInto(prfW).update(u).digestInto(u);
  56. for (let i = 0; i < Ti.length; i++)
  57. Ti[i] ^= u[i];
  58. }
  59. }
  60. return pbkdf2Output(PRF, PRFSalt, DK, prfW, u);
  61. }
  62. export async function pbkdf2Async(hash, password, salt, opts) {
  63. const { c, dkLen, asyncTick, DK, PRF, PRFSalt } = pbkdf2Init(hash, password, salt, opts);
  64. let prfW; // Working copy
  65. const arr = new Uint8Array(4);
  66. const view = createView(arr);
  67. const u = new Uint8Array(PRF.outputLen);
  68. // DK = T1 + T2 + ⋯ + Tdklen/hlen
  69. for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) {
  70. // Ti = F(Password, Salt, c, i)
  71. const Ti = DK.subarray(pos, pos + PRF.outputLen);
  72. view.setInt32(0, ti, false);
  73. // F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc
  74. // U1 = PRF(Password, Salt + INT_32_BE(i))
  75. (prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u);
  76. Ti.set(u.subarray(0, Ti.length));
  77. await asyncLoop(c - 1, asyncTick, () => {
  78. // Uc = PRF(Password, Uc−1)
  79. PRF._cloneInto(prfW).update(u).digestInto(u);
  80. for (let i = 0; i < Ti.length; i++)
  81. Ti[i] ^= u[i];
  82. });
  83. }
  84. return pbkdf2Output(PRF, PRFSalt, DK, prfW, u);
  85. }
  86. //# sourceMappingURL=pbkdf2.js.map