UNPKG

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