103 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			103 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| var Buffer = require('safe-buffer').Buffer
 | |
| 
 | |
| var checkParameters = require('./precondition')
 | |
| var defaultEncoding = require('./default-encoding')
 | |
| var sync = require('./sync')
 | |
| var toBuffer = require('./to-buffer')
 | |
| 
 | |
| var ZERO_BUF
 | |
| var subtle = global.crypto && global.crypto.subtle
 | |
| var toBrowser = {
 | |
|   sha: 'SHA-1',
 | |
|   'sha-1': 'SHA-1',
 | |
|   sha1: 'SHA-1',
 | |
|   sha256: 'SHA-256',
 | |
|   'sha-256': 'SHA-256',
 | |
|   sha384: 'SHA-384',
 | |
|   'sha-384': 'SHA-384',
 | |
|   'sha-512': 'SHA-512',
 | |
|   sha512: 'SHA-512'
 | |
| }
 | |
| var checks = []
 | |
| function checkNative (algo) {
 | |
|   if (global.process && !global.process.browser) {
 | |
|     return Promise.resolve(false)
 | |
|   }
 | |
|   if (!subtle || !subtle.importKey || !subtle.deriveBits) {
 | |
|     return Promise.resolve(false)
 | |
|   }
 | |
|   if (checks[algo] !== undefined) {
 | |
|     return checks[algo]
 | |
|   }
 | |
|   ZERO_BUF = ZERO_BUF || Buffer.alloc(8)
 | |
|   var prom = browserPbkdf2(ZERO_BUF, ZERO_BUF, 10, 128, algo)
 | |
|     .then(function () {
 | |
|       return true
 | |
|     }).catch(function () {
 | |
|       return false
 | |
|     })
 | |
|   checks[algo] = prom
 | |
|   return prom
 | |
| }
 | |
| 
 | |
| function browserPbkdf2 (password, salt, iterations, length, algo) {
 | |
|   return subtle.importKey(
 | |
|     'raw', password, { name: 'PBKDF2' }, false, ['deriveBits']
 | |
|   ).then(function (key) {
 | |
|     return subtle.deriveBits({
 | |
|       name: 'PBKDF2',
 | |
|       salt: salt,
 | |
|       iterations: iterations,
 | |
|       hash: {
 | |
|         name: algo
 | |
|       }
 | |
|     }, key, length << 3)
 | |
|   }).then(function (res) {
 | |
|     return Buffer.from(res)
 | |
|   })
 | |
| }
 | |
| 
 | |
| function resolvePromise (promise, callback) {
 | |
|   promise.then(function (out) {
 | |
|     process.nextTick(function () {
 | |
|       callback(null, out)
 | |
|     })
 | |
|   }, function (e) {
 | |
|     process.nextTick(function () {
 | |
|       callback(e)
 | |
|     })
 | |
|   })
 | |
| }
 | |
| module.exports = function (password, salt, iterations, keylen, digest, callback) {
 | |
|   if (typeof digest === 'function') {
 | |
|     callback = digest
 | |
|     digest = undefined
 | |
|   }
 | |
| 
 | |
|   digest = digest || 'sha1'
 | |
|   var algo = toBrowser[digest.toLowerCase()]
 | |
| 
 | |
|   if (!algo || typeof global.Promise !== 'function') {
 | |
|     return process.nextTick(function () {
 | |
|       var out
 | |
|       try {
 | |
|         out = sync(password, salt, iterations, keylen, digest)
 | |
|       } catch (e) {
 | |
|         return callback(e)
 | |
|       }
 | |
|       callback(null, out)
 | |
|     })
 | |
|   }
 | |
| 
 | |
|   checkParameters(iterations, keylen)
 | |
|   password = toBuffer(password, defaultEncoding, 'Password')
 | |
|   salt = toBuffer(salt, defaultEncoding, 'Salt')
 | |
|   if (typeof callback !== 'function') throw new Error('No callback provided to pbkdf2')
 | |
| 
 | |
|   resolvePromise(checkNative(algo).then(function (resp) {
 | |
|     if (resp) return browserPbkdf2(password, salt, iterations, keylen, algo)
 | |
| 
 | |
|     return sync(password, salt, iterations, keylen, digest)
 | |
|   }), callback)
 | |
| }
 |