PBKDF2Async.js 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. (function(){
  2. var C = (typeof window === 'undefined') ? require('./Crypto').Crypto : window.Crypto;
  3. // Shortcuts
  4. var util = C.util,
  5. charenc = C.charenc,
  6. UTF8 = charenc.UTF8,
  7. Binary = charenc.Binary;
  8. if (!C.nextTick) {
  9. // node.js has setTime out but prefer process.nextTick
  10. if (typeof process != 'undefined' && typeof process.nextTick !== 'undefined') {
  11. C.nextTick = process.nextTick;
  12. } else if (typeof setTimeout !== 'undefined') {
  13. C.nextTick = function (callback) {
  14. setTimeout(callback, 0);
  15. };
  16. }
  17. }
  18. C.PBKDF2Async = function (password, salt, keylen, callback, options) {
  19. // Convert to byte arrays
  20. if (password.constructor == String) password = UTF8.stringToBytes(password);
  21. if (salt.constructor == String) salt = UTF8.stringToBytes(salt);
  22. /* else, assume byte arrays already */
  23. // Defaults
  24. var hasher = options && options.hasher || C.SHA1,
  25. iterations = options && options.iterations || 1;
  26. // Progress callback option
  27. var progressChangeHandler = options && options.onProgressChange;
  28. var totalIterations = Math.ceil(keylen / hasher._digestsize) * iterations;
  29. function fireProgressChange(currentIteration) {
  30. if (progressChangeHandler) {
  31. var iterationsSoFar = derivedKeyBytes.length / hasher._digestsize * iterations + currentIteration;
  32. setTimeout(function () {
  33. progressChangeHandler(Math.round(iterationsSoFar / totalIterations * 100));
  34. }, 0);
  35. }
  36. }
  37. // Pseudo-random function
  38. function PRF(password, salt) {
  39. return C.HMAC(hasher, salt, password, { asBytes: true });
  40. }
  41. var nextTick = C.nextTick;
  42. // Generate key
  43. var derivedKeyBytes = [],
  44. blockindex = 1;
  45. var outer, inner;
  46. nextTick(outer = function () {
  47. if (derivedKeyBytes.length < keylen) {
  48. var block = PRF(password, salt.concat(util.wordsToBytes([blockindex])));
  49. fireProgressChange(1);
  50. var u = block, i = 1;
  51. nextTick(inner = function () {
  52. if (i < iterations) {
  53. u = PRF(password, u);
  54. for (var j = 0; j < block.length; j++) block[j] ^= u[j];
  55. i++;
  56. fireProgressChange(i);
  57. nextTick(inner);
  58. } else {
  59. derivedKeyBytes = derivedKeyBytes.concat(block);
  60. blockindex++;
  61. nextTick(outer);
  62. }
  63. });
  64. } else {
  65. // Truncate excess bytes
  66. derivedKeyBytes.length = keylen;
  67. callback(
  68. options && options.asBytes ? derivedKeyBytes :
  69. options && options.asString ? Binary.bytesToString(derivedKeyBytes) :
  70. util.bytesToHex(derivedKeyBytes));
  71. }
  72. });
  73. };
  74. })();