/* * 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 ZpSubgroup = require('./zp-subgroup'); var Exponent = require('./exponent'); var MathematicalGroupHandler = require('./group-handler'); var constants = require('./constants'); var validator = require('./input-validator'); var forge = require('node-forge'); var BigInteger = forge.jsbn.BigInteger; module.exports = MathematicalRandomGenerator; /** * @class MathematicalRandomGenerator * @classdesc The mathematical random generator API. To instantiate this object, * use the method {@link MathematicalService.newRandomGenerator}. * @hideconstructor * @param {SeureRandomService} * secureRandomService The secure random service to use. */ function MathematicalRandomGenerator(secureRandomService) { var randomGenerator_ = secureRandomService.newRandomGenerator(); var mathGroupHandler_ = new MathematicalGroupHandler(); /** * Generates a random Zp group element belonging to the Zp subgroup provided * as input. * * @function nextZpGroupElement * @memberof MathematicalRandomGenerator * @param {ZpSubgroup} * group The Zp subgroup to which the Zp group element belongs. * @param {Object} * [options] An object containing optional arguments. * @param {SecureRandomGenerator} * [options.secureRandomGenerator=Internal generator used] The * secure random generator to use. * @param {boolean} * [options.useShortExponent=false] If true, then * a short exponent is to be generated for the exponentiation * used to generate the Zp group element. * @returns {ZpGroupElement} The generated Zp group element. * @throws {Error} * If the input validation fails. */ this.nextZpGroupElement = function(group, options) { validator.checkIsInstanceOf( group, ZpSubgroup, 'ZpSubgroup', 'Zp subgroup to which randomly generated element is to belong'); var exponent = this.nextExponent(group, options); return group.generator.exponentiate(exponent); }; /** * Generates a random exponent associated with the Zp subgroup provided as * input. The value of the exponent will be within the range [0, * q-1]. * * @function nextExponent * @memberof MathematicalRandomGenerator * @param {ZpSubgroup} * group The Zp subgroup to which the exponent is to be * associated. * @param {Object} * [options] An object containing optional arguments. * @param {SecureRandomGenerator} * [options.secureRandomGenerator=Internal generator is used] The * secure random generator to use. * @param {boolean} * [options.useShortExponent=false] If true, then * a short exponent is to be generated. * @returns {Exponent} The generated exponent. * @throws {Error} * If the input validation fails. */ this.nextExponent = function(group, options) { validator.checkIsInstanceOf( group, ZpSubgroup, 'ZpSubgroup', 'Zp subgroup to which randomly generated exponent is to be associated'); options = options || {}; var randomGenerator = options.secureRandomGenerator || randomGenerator_; validator.checkIsObjectWithProperties( randomGenerator, 'Random BigInteger generator for random exponent generation'); var useShortExponent = options.useShortExponent || false; var q = group.q; var qBitLength = q.bitLength(); var randomExponentBitLength; if (useShortExponent) { if (qBitLength < constants.SHORT_EXPONENT_BIT_LENGTH) { throw new Error( 'Zp subgroup order bit length must be greater than or equal to short exponent bit length : ' + constants.SHORT_EXPONENT_BIT_LENGTH + '; Found ' + qBitLength); } randomExponentBitLength = constants.SHORT_EXPONENT_BIT_LENGTH; } else { randomExponentBitLength = qBitLength; } var randomExponentValue; var randomExponentFound = false; while (!randomExponentFound) { randomExponentValue = randomGenerator.nextBigInteger(randomExponentBitLength); if (randomExponentValue.compareTo(q) < 0) { randomExponentFound = true; } } return new Exponent(q, randomExponentValue); }; /** * Generates a random quadratic residue Zp subgroup, that has the specified * bit length for its modulus p and the specified certainty * of having its modulus and the order q being prime. *

* This method generates a Zp subgroup that satisfies the condition: *

* p = * (q * 2) + 1 *

* Note: The minimum bit length of the modulus p that * is permitted by this method is 2. If a bit length less than 2 is * requested, then an Error will be thrown. * * @function nextQuadraticResidueGroup * @memberof MathematicalRandomGenerator * @param {number} * pBitLength The modulus bit length of the Zp subgroup to * generate. * @param {number} * certainty The level of certainty that the order q * of the generated group is prime. * @param {Object} * [options] An object containing optional arguments. * @param {SecureRandomGenerator} * [options.secureRandomGenerator=Internal generator used] The * secure random generator to use. * @returns {ZpSubgroup} The generated quadratic residue group. * @throws {Error} * If the input validation fails. */ this.nextQuadraticResidueGroup = function(pBitLength, certainty, options) { checkQrGroupGenerationData(pBitLength, certainty); options = options || {}; var randomGenerator = options.secureRandomGenerator || randomGenerator_; validator.checkIsObjectWithProperties( randomGenerator, 'Random bytes generator for random quadratic residue Zp subgroup generation'); var p, q; var randomGroupFound = false; var primeOptions = {prng: randomGenerator}; var callback = function(err, num) { if (err) { throw new Error('Error while generating prime number; ' + err); } p = num; q = num.subtract(BigInteger.ONE).divide(new BigInteger('2')); }; while (!randomGroupFound) { forge.prime.generateProbablePrime(pBitLength, primeOptions, callback); if (q.isProbablePrime(certainty)) { randomGroupFound = true; } } var g = mathGroupHandler_.findMinGenerator(p, q); return new ZpSubgroup(p, q, g); }; function checkQrGroupGenerationData(pBitLength, certainty) { validator.checkIsPositiveNumber(pBitLength); validator.checkIsPositiveNumber(certainty); if (pBitLength < 2) { throw new Error( 'Bit length: ' + pBitLength + ' of modulus p provided as input for generating quadratic residue group is less than minimum required value of 2.'); } if (certainty < constants.MINIMUM_PRIME_CERTAINTY_LEVEL) { throw new Error( 'Certainty level: ' + certainty + ' provided as input for generating quadratic residue group is less than minimum required value of ' + constants.MINIMUM_PRIME_CERTAINTY_LEVEL); } } }