fcoin/lib/crypto/pbkdf2-browser.js
2017-07-31 18:21:02 -07:00

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}.`);
}
}