pbkdf2.ts 3.7 KB

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