async.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. 'use strict';
  2. var Buffer = require('safe-buffer').Buffer;
  3. var checkParameters = require('./precondition');
  4. var defaultEncoding = require('./default-encoding');
  5. var sync = require('./sync');
  6. var toBuffer = require('./to-buffer');
  7. var ZERO_BUF;
  8. var subtle = global.crypto && global.crypto.subtle;
  9. var toBrowser = {
  10. sha: 'SHA-1',
  11. 'sha-1': 'SHA-1',
  12. sha1: 'SHA-1',
  13. sha256: 'SHA-256',
  14. 'sha-256': 'SHA-256',
  15. sha384: 'SHA-384',
  16. 'sha-384': 'SHA-384',
  17. 'sha-512': 'SHA-512',
  18. sha512: 'SHA-512'
  19. };
  20. var checks = [];
  21. var nextTick;
  22. function getNextTick() {
  23. if (nextTick) {
  24. return nextTick;
  25. }
  26. if (global.process && global.process.nextTick) {
  27. nextTick = global.process.nextTick;
  28. } else if (global.queueMicrotask) {
  29. nextTick = global.queueMicrotask;
  30. } else if (global.setImmediate) {
  31. nextTick = global.setImmediate;
  32. } else {
  33. nextTick = global.setTimeout;
  34. }
  35. return nextTick;
  36. }
  37. function browserPbkdf2(password, salt, iterations, length, algo) {
  38. return subtle.importKey('raw', password, { name: 'PBKDF2' }, false, ['deriveBits']).then(function (key) {
  39. return subtle.deriveBits({
  40. name: 'PBKDF2',
  41. salt: salt,
  42. iterations: iterations,
  43. hash: {
  44. name: algo
  45. }
  46. }, key, length << 3);
  47. }).then(function (res) {
  48. return Buffer.from(res);
  49. });
  50. }
  51. function checkNative(algo) {
  52. if (global.process && !global.process.browser) {
  53. return Promise.resolve(false);
  54. }
  55. if (!subtle || !subtle.importKey || !subtle.deriveBits) {
  56. return Promise.resolve(false);
  57. }
  58. if (checks[algo] !== undefined) {
  59. return checks[algo];
  60. }
  61. ZERO_BUF = ZERO_BUF || Buffer.alloc(8);
  62. var prom = browserPbkdf2(ZERO_BUF, ZERO_BUF, 10, 128, algo)
  63. .then(
  64. function () { return true; },
  65. function () { return false; }
  66. );
  67. checks[algo] = prom;
  68. return prom;
  69. }
  70. function resolvePromise(promise, callback) {
  71. promise.then(function (out) {
  72. getNextTick()(function () {
  73. callback(null, out);
  74. });
  75. }, function (e) {
  76. getNextTick()(function () {
  77. callback(e);
  78. });
  79. });
  80. }
  81. module.exports = function (password, salt, iterations, keylen, digest, callback) {
  82. if (typeof digest === 'function') {
  83. callback = digest;
  84. digest = undefined;
  85. }
  86. checkParameters(iterations, keylen);
  87. password = toBuffer(password, defaultEncoding, 'Password');
  88. salt = toBuffer(salt, defaultEncoding, 'Salt');
  89. if (typeof callback !== 'function') {
  90. throw new Error('No callback provided to pbkdf2');
  91. }
  92. digest = digest || 'sha1';
  93. var algo = toBrowser[digest.toLowerCase()];
  94. if (!algo || typeof global.Promise !== 'function') {
  95. getNextTick()(function () {
  96. var out;
  97. try {
  98. out = sync(password, salt, iterations, keylen, digest);
  99. } catch (e) {
  100. callback(e);
  101. return;
  102. }
  103. callback(null, out);
  104. });
  105. return;
  106. }
  107. resolvePromise(checkNative(algo).then(function (resp) {
  108. if (resp) {
  109. return browserPbkdf2(password, salt, iterations, keylen, algo);
  110. }
  111. return sync(password, salt, iterations, keylen, digest);
  112. }), callback);
  113. };