/* * 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 */ cryptolib.modules.primitives = cryptolib.modules.primitives || {}; /** * @namespace primitives/securerandom */ cryptolib.modules.primitives.securerandom = function(box) { 'use strict'; box.primitives = box.primitives || {}; box.primitives.securerandom = {}; /** * A module that holds secure random functionalities. * * @exports primitives/securerandom */ box.primitives.securerandom.factory = {}; var policies = { secureRandom: {provider: box.policies.primitives.secureRandom.provider} }; var converters, exceptions; cryptolib('commons', function(box) { converters = new box.commons.utils.Converters(); exceptions = box.commons.exceptions; }); /** * A factory class for creating random generators. * * @class */ box.primitives.securerandom.factory.SecureRandomFactory = function() { }; box.primitives.securerandom.factory.SecureRandomFactory.prototype = { /** * Instantiates a random byte generator. * * @function * @returns {primitives/securerandom.CryptoScytlRandomBytes} */ getCryptoRandomBytes: function() { try { if (policies.secureRandom.provider === Config.primitives.secureRandom.provider.SCYTL) { return this.getCryptoScytlRandomBytes(); } else { throw new exceptions.CryptoLibException( 'No suitable provider was provided.'); } } catch (error) { throw new exceptions.CryptoLibException( 'A CryptoRandomBytes could not be obtained.', error); } }, /** * Instantiates a Scytl random byte generator. * * @function * @returns {primitives/securerandom.CryptoScytlRandomBytes} */ getCryptoScytlRandomBytes: function() { return new CryptoScytlRandomBytes(); }, /** * Instantiates a random integer generator. * * @function * @returns {primitives/securerandom.CryptoScytlRandomBytes} */ getCryptoRandomInteger: function() { try { if (policies.secureRandom.provider === Config.primitives.secureRandom.provider.SCYTL) { return this.getCryptoScytlRandomInteger(); } else { throw new exceptions.CryptoLibException( 'No suitable provider was provided.'); } } catch (error) { throw new exceptions.CryptoLibException( 'A CryptoRandomBytes could not be obtained.', error); } }, /** * Instantiates a Scytl random integer generator. * * @function * @returns {primitives/securerandom.CryptoScytlRandomBytes} */ getCryptoScytlRandomInteger: function() { return new CryptoScytlRandomInteger(); } }; var validate = function(length) { try { if (typeof length !== 'number') { throw new exceptions.CryptoLibException( 'The given length is not a number'); } if (length < 1) { throw new exceptions.CryptoLibException( 'The given length should be a strictly positive integer.'); } if (length > box.MAXIMUM_LENGTH_TO_GENERATE_DATA) { throw new exceptions.CryptoLibException( 'The given length should be lower than or equal to ' + box.MAXIMUM_LENGTH_TO_GENERATE_DATA); } } catch (error) { throw new exceptions.CryptoLibException( 'Random generation failed.', error); } }; /** * The SCYTL Bytes Random Generator * * @class * @memberof primitives/securerandom */ function CryptoScytlRandomBytes() {} CryptoScytlRandomBytes.prototype = { /** * Generates secure random bytes. * * @function * @param {number} * numBytes number of bytes to be generated.. * @returns {string} secure random string. */ nextRandom: function(length) { validate(length); var randomBytes = box.prng.generate(length); return randomBytes.toString(); }, /** * Method to be compliant with the Forge library. * * @function * @param {number} * numBytes number of bytes to be generated.. * @returns {string} secure random string. */ getBytesSync: function(length) { return this.nextRandom(length); } }; /** * The SCYTL Integer Random Generator * * @class * @memberof primitives/securerandom */ function CryptoScytlRandomInteger() {} CryptoScytlRandomInteger.prototype = { /** * Generates a secure random big integer (Forge). * * @function * @params {number} length the bit length of the random integer. * @returns {object} secure random big integer. */ nextRandom: function(length) { validate(length); var n = ((new box.forge.jsbn.BigInteger('10')).pow(length)) .subtract(box.forge.jsbn.BigInteger.ONE); var bitLength = n.bitLength(); var numberOfBytes = bitLength / box.NUMBER_OF_BITS_PER_BYTE; numberOfBytes = Math.floor(numberOfBytes); var modBytes = bitLength % box.NUMBER_OF_BITS_PER_BYTE; if (modBytes !== 0) { numberOfBytes++; } var generatedInteger; do { var randomBytes = box.prng.generate(numberOfBytes); randomBytes = '\x00' + randomBytes; randomBytes = converters.bytesFromString(randomBytes); generatedInteger = new box.forge.jsbn.BigInteger(randomBytes); } while ((generatedInteger.compareTo(forge.jsbn.BigInteger.ZERO) <= 0) || (generatedInteger.compareTo(n) > 0)); return generatedInteger; }, /** * Generates a secure random big integer according to the specified bit * length. * * @function * @params {number} bitsLength the bit length of the random integer. * @returns {object} Secure random big integer. */ nextRandomByBits: function(bitsLength) { var byteLength = Math.ceil(bitsLength / box.NUMBER_OF_BITS_PER_BYTE); var modBytes = box.NUMBER_OF_BITS_PER_BYTE - (bitsLength % box.NUMBER_OF_BITS_PER_BYTE); var randomBytes = box.prng.generate(byteLength); randomBytes = '\x00' + randomBytes; randomBytes = converters.bytesFromString(randomBytes); var generatedInteger = new box.forge.jsbn.BigInteger(randomBytes); generatedInteger = generatedInteger.shiftRight(modBytes); return generatedInteger; } }; };