/* * 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.'); } } }