/* * 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, jasmine:true */ 'use strict'; var cryptoPolicy = require('scytl-cryptopolicy'); var pbkdf = require('../lib/index'); var codec = require('scytl-codec'); var forge = require('node-forge'); describe('The PBKDF module should be able to ...', function() { var PASSWORD = 'Test'; var SALT = 'TestTestTestTestTestTestTestTest'; var salt_; var tooSmallsalt_; var deriver_; var key_; var policy_; beforeAll(function() { salt_ = codec.utf8Encode(SALT); tooSmallsalt_ = codec.utf8Encode('1'); deriver_ = pbkdf.newService().newDeriver(); key_ = deriver_.derive(PASSWORD, salt_); }); beforeEach(function() { policy_ = cryptoPolicy.newInstance(); }); describe('create a PBKDF service that should be able to ..', function() { describe('create a PBKDF deriver that should be able to', function() { it('derive a key of the expected length, when using the default cryptographic policy.', function() { var key = deriver_.derive(PASSWORD, salt_); expect(key).toBeDefined(); expect(key.length) .toBe(policy_.primitives.keyDerivation.pbkdf.keyLengthBytes); }); it('derive a key of the expected length, when using a specified cryptographic policy.', function() { policy_.primitives.keyDerivation.pbkdf.keyLengthBytes = cryptoPolicy.options.primitives.keyDerivation.pbkdf .keyLengthBytes.KL_32; var pbkdfService = pbkdf.newService({policy: policy_}); var key = pbkdfService.newDeriver().derive(PASSWORD, salt_); expect(key).toBeDefined(); expect(key.length) .toBe(cryptoPolicy.options.primitives.keyDerivation.pbkdf .keyLengthBytes.KL_32); }); it('derive the same key every time, for a given cryptographic policy.', function() { var key = deriver_.derive(PASSWORD, salt_); expect(key).toBeDefined(); expect(key).toEqual(key_); }); it('derive the expected key if salt is provided as string instead of bytes', function() { var key = deriver_.derive(PASSWORD, SALT); expect(key).toBeDefined(); expect(key).toEqual(key_); }); it('derive different keys when different numbers of iterations are specified.', function() { policy_.primitives.keyDerivation.pbkdf.numIterations = cryptoPolicy.options.primitives.keyDerivation.pbkdf.numIterations .I_1; var pbkdfService = pbkdf.newService({policy: policy_}); var key = pbkdfService.newDeriver().derive(PASSWORD, salt_); expect(key).toBeDefined(); expect(key).not.toEqual(key_); }); it('derive different keys when different passwords are used.', function() { var key = deriver_.derive(PASSWORD + 'a', salt_); expect(key).toBeDefined(); expect(key).not.toEqual(key_); }); it('derive different keys when different salts are used.', function() { var key = deriver_.derive(PASSWORD, codec.utf8Encode(SALT + 'a')); expect(key).toBeDefined(); expect(key).not.toEqual(key_); }); it('derive the same key for the SJCL and Forge cryptographic service providers, given a random password.', function() { var password = generateRandomString(Math.floor(Math.random() * 128) + 1); // Derive key using SJCL. var derivedKeyFromSjcl = deriver_.derive(password, salt_); // Derive key using Forge. var numIterations = policy_.primitives.keyDerivation.pbkdf.numIterations; var keyLengthBytes = policy_.primitives.keyDerivation.pbkdf.keyLengthBytes; var derivedKeyFromForgeBinaryEncoded = forge.pkcs5.pbkdf2( codec.binaryEncode(codec.utf8Encode(password)), codec.binaryEncode(salt_), numIterations, keyLengthBytes, getHashGenerator( policy_.primitives.keyDerivation.pbkdf.hashAlgorithm, cryptoPolicy.options.primitives.keyDerivation.pbkdf .hashAlgorithm)); var derivedKeyFromForge = codec.binaryDecode(derivedKeyFromForgeBinaryEncoded); expect(derivedKeyFromForge).toEqual(derivedKeyFromSjcl); }); it('throw an error when a salt length less than the minimum value specified in the cryptographic policy is used', function() { expect(function() { deriver_.derive(PASSWORD, tooSmallsalt_); }).toThrow(); }); }); }); }); function generateRandomString(length) { var allowedChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,-./:;<=>?@[\]^_`{|}~'; var str = ''; for (var i = 0; i < length; i++) { str += allowedChars.charAt(Math.floor(Math.random() * allowedChars.length)); } return str; } function getHashGenerator(algorithm, config) { if (algorithm === config.SHA256) { return forge.sha256.create(); } else { throw 'Hash algorithm \'' + algorithm + '\' is unsupported'; } }