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