async.js 2.9 KB

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