102 lines
2.2 KiB
JavaScript
102 lines
2.2 KiB
JavaScript
/*!
|
|
* pbkdf2.js - pbkdf2 for bcoin
|
|
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
|
|
* https://github.com/bcoin-org/bcoin
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* @module crypto.pbkdf2-browser
|
|
* @ignore
|
|
*/
|
|
|
|
const digest = require('./digest');
|
|
const crypto = global.crypto || global.msCrypto || {};
|
|
const subtle = crypto.subtle || {};
|
|
|
|
/**
|
|
* Perform key derivation using PBKDF2.
|
|
* @param {Buffer} key
|
|
* @param {Buffer} salt
|
|
* @param {Number} iter
|
|
* @param {Number} len
|
|
* @param {String} alg
|
|
* @returns {Buffer}
|
|
*/
|
|
|
|
exports.derive = function derive(key, salt, iter, len, alg) {
|
|
const size = digest.hash(alg, Buffer.alloc(0)).length;
|
|
const blocks = Math.ceil(len / size);
|
|
const out = Buffer.allocUnsafe(len);
|
|
const buf = Buffer.allocUnsafe(salt.length + 4);
|
|
const block = Buffer.allocUnsafe(size);
|
|
let pos = 0;
|
|
|
|
salt.copy(buf, 0);
|
|
|
|
for (let i = 0; i < blocks; i++) {
|
|
buf.writeUInt32BE(i + 1, salt.length, true);
|
|
let mac = digest.hmac(alg, buf, key);
|
|
mac.copy(block, 0);
|
|
for (let j = 1; j < iter; j++) {
|
|
mac = digest.hmac(alg, mac, key);
|
|
for (let k = 0; k < size; k++)
|
|
block[k] ^= mac[k];
|
|
}
|
|
block.copy(out, pos);
|
|
pos += size;
|
|
}
|
|
|
|
return out;
|
|
};
|
|
|
|
/**
|
|
* Execute pbkdf2 asynchronously.
|
|
* @param {Buffer} key
|
|
* @param {Buffer} salt
|
|
* @param {Number} iter
|
|
* @param {Number} len
|
|
* @param {String} alg
|
|
* @returns {Promise}
|
|
*/
|
|
|
|
exports.deriveAsync = async function deriveAsync(key, salt, iter, len, alg) {
|
|
const algo = { name: 'PBKDF2' };
|
|
const use = ['deriveBits'];
|
|
|
|
if (!subtle.importKey || !subtle.deriveBits)
|
|
return exports.derive(key, salt, iter, len, alg);
|
|
|
|
const options = {
|
|
name: 'PBKDF2',
|
|
salt: salt,
|
|
iterations: iter,
|
|
hash: getHash(alg)
|
|
};
|
|
|
|
const imported = await subtle.importKey('raw', key, algo, false, use);
|
|
const data = await subtle.deriveBits(options, imported, len * 8);
|
|
|
|
return Buffer.from(data);
|
|
};
|
|
|
|
/*
|
|
* Helpers
|
|
*/
|
|
|
|
function getHash(name) {
|
|
switch (name) {
|
|
case 'sha1':
|
|
return 'SHA-1';
|
|
case 'sha256':
|
|
return 'SHA-256';
|
|
case 'sha384':
|
|
return 'SHA-384';
|
|
case 'sha512':
|
|
return 'SHA-512';
|
|
default:
|
|
throw new Error(`Algorithm not supported: ${name}.`);
|
|
}
|
|
}
|