123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- /*
- * 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 ZpGroupElement = require('./zp-group-element');
- var Exponent = require('./exponent');
- var validator = require('./input-validator');
- var forge = require('node-forge');
- var BigInteger = forge.jsbn.BigInteger;
- module.exports = MathematicalGroupHandler;
- /**
- * @class MathematicalGroupHandler
- * @classdesc The mathematical group handler API. To instantiate this object,
- * use the method {@link MathematicalService.newGroupHandler}.
- * @hideconstructor
- */
- function MathematicalGroupHandler() {}
- MathematicalGroupHandler.prototype = {
- /**
- * Exponentiates an array of Zp group elements with the exponent provided as
- * input.
- * <p>
- * The array of Zp group elements must only contain members of the Zp
- * subgroup provided as input. Otherwise, an error will be thrown.
- *
- * @function exponentiateElements
- * @memberof MathematicalGroupHandler
- * @param {ZpSubgroup}
- * group The Zp subgroup to which the elements must belong.
- * @param {ZpGroupElement[]}
- * elements The array of Zp group elements to exponentiate.
- * @param {Exponent}
- * exponent The exponent to use for the exponentiation
- * @param {boolean}
- * validate If set to true, then the elements are validated
- * @return {ZpGroupElement[]} The array of exponentiated group elements.
- * @throws {Error}
- * If the input data or group membership validation fails.
- */
- exponentiateElements: function(group, elements, exponent, validate) {
- validator.checkIsInstanceOf(
- group, ZpSubgroup, 'ZpSubgroup',
- 'Zp subgroup of elements to exponentiate', group);
- checkZpGroupElements(elements, 'Zp group elements to exponentiate', group);
- checkExponent(
- exponent, 'Exponent to use for exponentiating Zp group elements',
- group.q);
- if (typeof validate !== 'undefined') {
- validator.checkIsDefinedAndNotNull(
- validate, 'Zp group element exponentiation \"validate\" flag');
- validator.checkIsType(
- validate, 'boolean',
- 'Zp group element exponentiation \"validate\" flag');
- }
- if (validate) {
- for (var i = 0; i < elements.length; i++) {
- if (!group.isGroupMember(elements[i])) {
- throw new Error(
- 'Found Zp group element with value: ' + elements[i].value +
- ' that does not belong to Zp subgroup provided as input: p = ' +
- group.p + ' ; q = ' + group.q);
- }
- }
- }
- var exponentiatedElements = [];
- for (var j = 0; j < elements.length; j++) {
- exponentiatedElements.push(elements[j].exponentiate(exponent));
- }
- return exponentiatedElements;
- },
- /**
- * Divides the elements of one array of Zp group elements by the
- * corresponding members of another array of Zp group elements and creates a
- * new array of Zp group elements from the results of this operation.
- * <p>
- * The two arrays must only contain elements that belong to the Zp subgroup
- * group that this divider operates on. If either array contains any element
- * that is does not belong to this Zp subgroup, then an Error will be
- * thrown.
- *
- * @function divideElements
- * @memberof MathematicalGroupHandler
- * @param {ZpGroupElement[]}
- * dividendElements The array of Zp group elements to act as the
- * <code>dividend</code>.
- * @param {ZpGroupElement[]}
- * divisorElements The array Zp group elements to act as the
- * <code>divisor</code>.
- * @returns {ZpGroupElement[]} The array of <code>quotient</code> Zp group
- * elements resulting from this operation.
- * @throws {Error}
- * If the input or group membership validation fails.
- */
- divideElements: function(dividendElements, divisorElements) {
- checkDivisionData(dividendElements, divisorElements);
- var quotientArray = [];
- var quotient;
- for (var i = 0; i < dividendElements.length; i++) {
- quotient = dividendElements[i].multiply(divisorElements[i].invert());
- quotientArray.push(quotient);
- }
- return quotientArray;
- },
- /**
- * Iterates through an array of Zp group elements, checking if each element
- * is a member of the Zp subgroup provided as input.
- * <p>
- * If any element is found that is not a member of the Zp subgroup, then an
- * Error will be thrown.
- *
- * @function checkGroupMembership
- * @memberof MathematicalGroupHandler
- * @param {ZpSubgroup}
- * group The Zp subgroup to which the Zp group elements provided
- * as input must belong.
- * @param {ZpGroupElement[]}
- * elements The array of Zp group elements to check for group
- * membership.
- * @throws {Error}
- * If the input validation fails or any Zp group element in the
- * array fails the group membership check.
- */
- checkGroupMembership: function(group, elements) {
- validator.checkIsInstanceOf(
- group, ZpSubgroup, 'ZpSubgroup',
- 'Zp subgroup for group membership check');
- checkZpGroupElements(
- elements, 'Zp group elements for group membership check');
- for (var i = 0; i < elements.length; i++) {
- if (!group.isGroupMember(elements[i])) {
- throw new Error(
- 'Zp group element with value: ' + elements[i].value +
- ' is not a member of the Zp subgroup with modulus p: ' + group.p +
- ' and order q: ' + group.q);
- }
- }
- },
- /**
- * Iterates through an array of Zp group element values, checking if each
- * value is a member of the Zp subgroup provided as input. If a given value
- * is a member of the subgroup, it is encapsulated in a ZpGroupElement
- * object and added to an output array. The method stops iterating once it
- * has found the number of Zp subgroup members provided as input. At that
- * point, the output array containing all of the members found is returned.
- * <p>
- * If the required number of Zp subgroup members are not found then an Error
- * will be thrown.
- *
- * @function extractGroupMembers
- * @memberof MathematicalGroupHandler
- * @param {ZpSubgroup}
- * group The Zp subgroup to which the output Zp group elements
- * must belong.
- * @param {forge.jsbn.BigInteger[]}
- * values The array of Zp group element values from which to
- * obtain the members.
- * @param {number}
- * numMembersRequired The required number of Zp subgroup members
- * to be returned.
- * @returns {ZpGroupElement[]} The array of requested Zp subgroup members.
- * @throws {Error}
- * If the input validation fails or the required number of Zp
- * subgroup members was not found.
- */
- extractGroupMembers: function(group, values, numMembersRequired) {
- checkExtractionData(values, group, numMembersRequired);
- var outputArray = [];
- var membersFound = 0;
- var candidate;
- for (var i = 0; i < values.length; i++) {
- candidate = new ZpGroupElement(group.p, group.q, values[i]);
- if (group.isGroupMember(candidate)) {
- outputArray.push(candidate);
- membersFound++;
- }
- if (membersFound === numMembersRequired) {
- return outputArray;
- }
- }
- if (membersFound !== numMembersRequired) {
- throw new Error(
- 'Did not find the required number of group members in the given list. The required number of was ' +
- numMembersRequired + ', number of members found was ' +
- membersFound);
- }
- },
- /**
- * Finds the smallest generator for a given Zp subgroup.
- * <p>
- * This method starts with a candidate generator element of value 2 and
- * checks if this element is a group member. If that element is a group
- * member then it is returned. Otherwise, the candidate element value is
- * incremented by 1 the check is performed again. This process continues
- * until a generator is found or the candidate element value equals the p
- * parameter of the Zp subgroup, in which case an Error is thrown.
- *
- * @function findMinGenerator
- * @memberof MathematicalGroupHandler
- * @param {forge.jsbn.BigInteger}
- * p The modulus of the Zp subgroup.
- * @param {forge.jsbn.BigInteger}
- * q The order of the Zp subgroup.
- * @returns {forge.jsbn.BigInteger} The smallest generator.
- * @throws {Error}
- * If the input data validation failes or a generator cannot be
- * found.
- */
- findMinGenerator: function(p, q) {
- validator.checkZpSubgroupModulus(
- p, 'Modulus p of Zp subgroup for which to find smallest generator');
- validator.checkZpSubgroupOrder(
- q, 'Order q of Zp subgroup for which to find smallest generator', p);
- var g = new BigInteger('2');
- var generatorFound = false;
- while ((!generatorFound) && (g.compareTo(p) < 0)) {
- if (g.modPow(q, p).equals(BigInteger.ONE)) {
- generatorFound = true;
- } else {
- g = g.add(BigInteger.ONE);
- }
- }
- if (!generatorFound) {
- throw new Error(
- 'Failed to find a generator for modulus p: ' + p +
- ' and order q: ' + q);
- }
- return g;
- }
- };
- function checkDivisionData(dividendElements, divisorElements) {
- checkZpGroupElements(
- dividendElements,
- 'Dividend Zp group elements to use for element array division operation');
- checkZpGroupElements(
- divisorElements,
- 'Divisor Zp group elements to use for element array division operation');
- var numDividendElements = dividendElements.length;
- var numDivisorElements = divisorElements.length;
- if (numDividendElements !== numDivisorElements) {
- throw new Error(
- 'Expected number of divisor elements to equal number of dividend elements: ' +
- numDividendElements + ' ; Found: ' + numDivisorElements);
- }
- }
- function checkExtractionData(values, group, numMembersRequired) {
- validator.checkIsNonEmptyArray(
- values, 'Zp group element values from which to extract group members');
- validator.checkIsInstanceOf(
- group, ZpSubgroup, 'ZpSubgroup',
- 'Zp subgroup to which extracted members are to belong');
- validator.checkIsPositiveNumber(
- numMembersRequired,
- 'Number of group members required by extraction process');
- if (numMembersRequired > values.length) {
- throw new Error(
- 'Required number of group members provided as input: ' +
- numMembersRequired +
- ' is greater then the number of element values provided as input: ' +
- values.length);
- }
- }
- function checkZpGroupElements(elements, label, group) {
- validator.checkZpGroupElements(elements, label, group);
- for (var i = 0; i < elements.length; i++) {
- validator.checkIsInstanceOf(
- elements[i], ZpGroupElement, 'ZpGroupElement',
- 'element ' + i + ' of ' + label);
- }
- }
- function checkExponent(exponent, label, q) {
- validator.checkExponent(exponent, label, q);
- validator.checkIsInstanceOf(exponent, Exponent, 'Exponent', label);
- }
|