/*
* Copyright 2018 Scytl Secure Electronic Voting SA
*
* All rights reserved
*
* See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
*/
/* jshint node:true */
'use strict';
var Policy = require('scytl-cryptopolicy');
var validator = require('./input-validator');
var codec = require('scytl-codec');
var sjcl = require('sjcl');
module.exports = PbkdfDeriver;
/**
* @class PbkdfDeriver
* @classdesc The PBKDF deriver API. To instantiate this object, use the method
* {@link PbkdfService.newDeriver}.
* @hideconstructor
* @param {Policy}
* policy The cryptographic policy to use.
*/
function PbkdfDeriver(policy) {
var keyLength_ = policy.primitives.keyDerivation.pbkdf.keyLengthBytes;
var minSaltLength_ = policy.primitives.keyDerivation.pbkdf.minSaltLengthBytes;
var numIterations_ = policy.primitives.keyDerivation.pbkdf.numIterations;
var hashAlgorithm_ = policy.primitives.keyDerivation.pbkdf.hashAlgorithm;
/**
* Derives a key, using a PBKDF.
*
* @function derive
* @memberof PbkdfDeriver
* @param {string}
* password The password from which to derive the key.
* @param {Uint8Array|string}
* salt The salt from which to derive the key. NOTE: Salt
* of type string
will be UTF-8 encoded.
* @returns {Uint8Array} The derived key.
* @throws {Error}
* If the input data validation fails.
*/
this.derive = function(password, salt) {
if (typeof salt === 'string') {
salt = codec.utf8Encode(salt);
}
checkData(password, salt);
var generateHmac = function(key) {
var generator = new sjcl.misc.hmac(key, getDigester(hashAlgorithm_));
this.encrypt = function() {
return generator.encrypt.apply(generator, arguments);
};
};
// NOTE: The following Base64 encoding/decoding should be replaced with the
// use of 'sjcl.codec.bytes.toBits/fromBits' when/if the latter becomes part
// of the default sjcl build.
var derivedKey = sjcl.misc.pbkdf2(
password, sjcl.codec.base64.toBits(codec.base64Encode(salt)),
numIterations_, keyLength_ * 8, generateHmac);
return codec.base64Decode(sjcl.codec.base64.fromBits(derivedKey));
};
function getDigester(algorithm) {
if (algorithm ===
Policy.options.primitives.keyDerivation.pbkdf.hashAlgorithm.SHA256) {
return sjcl.hash.sha256;
} else {
throw new Error(
'Hash algorithm \'' + algorithm + '\' is not recognized.');
}
}
function checkData(password, salt) {
validator.checkIsType(
password, 'string', 'Password from which to derive key');
validator.checkIsInstanceOf(
salt, Uint8Array, 'Uint8Array', 'Salt from which to derive key');
if (salt.length < minSaltLength_) {
throw new Error(
'The salt byte length ' + salt.length +
' is less than the minimum allowed salt length ' + minSaltLength_ +
' set by the cryptographic policy.');
}
}
}