12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376 |
- /*
- * 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.commons = cryptolib.modules.commons || {};
- /**
- * @namespace commons/mathematical
- */
- cryptolib.modules.commons.mathematical = function(box) {
- 'use strict';
- box.commons = box.commons || {};
- /**
- * A module the defines mathematical groups, elements, and operations that
- * can be performed
- *
- * @exports commons/mathematical
- */
- box.commons.mathematical = {};
- var BigInteger = box.forge.jsbn.BigInteger;
- var exceptions;
- var converters;
- cryptolib('commons.exceptions', 'commons.utils', function(box) {
- exceptions = box.commons.exceptions;
- converters = new box.commons.utils.Converters();
- });
- /**
- * Given p and Zp representing 'Integers (mod p)' group, a Zp subgroup is a
- * group where the elements are a subset of elements from Zp.
- *
- * The order of the subgroup is q, that is the number of elements. Both Zp
- * and the ZpSubgroup are finite cyclic groups, it means that all elements
- * can be generated exponentiating a special group element called generator.
- *
- * When the p and q are related with the restriction p = 2q + 1 the subgroup
- * is also defined as 'Quadratic Residue'.
- *
- * @class
- * @param valueG
- * {forge.jsbn.BigInteger} The generator of the subgroup, as a
- * BigInteger. This value must be a group element different from
- * one.
- * @param valueP
- * {forge.jsbn.BigInteger} The modulus, as a BigInteger.
- * @param valueQ
- * {forge.jsbn.BigInteger} The order of the subgroup, as a
- * BigInteger.
- *
- * @throws CryptoLibException
- * if 0 < q < p restriction is not accomplished
- * @throws CryptoLibException
- * if subgroup generator is nor between 2 and p-1
- *
- */
- box.commons.mathematical.ZpSubgroup = function(valueG, valueP, valueQ) {
- if ((typeof valueG === 'undefined') || (typeof valueP === 'undefined') ||
- (typeof valueQ === 'undefined')) {
- throw new exceptions.CryptoLibException(
- 'The given parameters should be initialized');
- }
- if ((BigInteger.ONE.compareTo(valueG) >= 0) ||
- (valueP.compareTo(valueG) <= 0)) {
- throw new exceptions.CryptoLibException(
- 'The generator should be between 2 and p-1');
- }
- if ((BigInteger.ZERO.compareTo(valueQ) >= 0) ||
- (valueP.compareTo(valueQ) <= 0)) {
- throw new exceptions.CryptoLibException(
- 'The relationship between the values of the p and q parameters should be such that 0 < q < p');
- }
- var _p = valueP;
- var _q = valueQ;
- var generator = new box.commons.mathematical.ZpGroupElement(valueG, _p, _q);
- var identity =
- new box.commons.mathematical.ZpGroupElement(BigInteger.ONE, _p, _q);
- /**
- * @function
- * @returns the identity element of the group.
- */
- this.getIdentity = function() {
- return identity;
- };
- /**
- * @function
- * @returns the q parameter (the order of the group).
- */
- this.getQ = function() {
- return _q;
- };
- /**
- * @function
- * @returns the p parameter (the modulus of the group).
- */
- this.getP = function() {
- return _p;
- };
- /**
- * @function
- * @returns {box.commons.mathematical.ZpGroupElement} the generator
- * element of the group.
- */
- this.getGenerator = function() {
- return generator;
- };
- };
- box.commons.mathematical.ZpSubgroup.prototype = {
- /**
- * Checks whether a given element is a member of this MathematicalGroup.
- *
- * An element is a member of the group if:
- * <ul>
- * <li><code>element</code> has an integer value between
- * <code>1</code> and <code>p-1</code>:
- * <code>(0 < element < p)</code></li>
- * <li><code>element<sup>q</sup> mod p = 1</code></li>
- * </ul>
- *
- * @function
- * @param element
- * {ZpGroupElement} the element whose group membership should
- * be checked.
- * @returns true if the given element is member of the group, otherwise
- * false.
- */
- isGroupMember: function(element) {
- if (typeof element !== 'undefined' &&
- element.getP().equals(this.getP())) {
- var modPow = element.getElementValue().modPow(this.getQ(), this.getP());
- return BigInteger.ONE.equals(modPow);
- } else {
- return false;
- }
- },
- /**
- * Check if this group is equal to the received group.
- *
- * @function
- * @param group
- * {ZpSubgroup} the group which should be checked against
- * this group for equality.
- * @returns true if the given group has the same <code>q</code>, the
- * same <code>p</code>, and the same <code>generator</code>.
- */
- equals: function(group) {
- if (typeof group !== 'undefined' &&
- group.getGenerator().equals(this.getGenerator()) &&
- group.getQ().equals(this.getQ()) &&
- group.getP().equals(this.getP())) {
- return true;
- } else {
- return false;
- }
- },
- /**
- * Check if this group is a quadratic residue group, which is defined
- * such that p = 2q + 1.
- *
- * @function
- * @returns true if the given group is a quadratic residue group.
- */
- isQuadraticResidueGroup: function() {
- return this.getP().equals(
- new BigInteger('2').multiply(this.getQ()).add(BigInteger.ONE));
- },
- /**
- * Displays a string representation of this mathematical group JSON.
- * <p>
- * Note: in order to permit interoperability between libraries, this
- * representation should match the equivalent representation in Java.
- *
- * @function
- * @returns a string representation of this mathematical group JSON.
- */
- stringify: function() {
- return JSON.stringify({
- zpSubgroup: {
- p: converters.base64FromBigInteger(this.getP()),
- q: converters.base64FromBigInteger(this.getQ()),
- g: converters.base64FromBigInteger(
- this.getGenerator().getElementValue())
- }
- });
- }
- };
- /**
- * Class representing elements of a Zp group.
- *
- * <p>
- * Note: This constructor does not check whether the given value is a valid
- * member of the subgroup. To check subgroup membership the function
- * <code>zpSubgroup.isGroupMember(groupElement)</code> can be called,
- * however this is a computationally expensive operation.
- *
- * @class
- * @param value
- * {forge.jsbn.BigInteger} Value of the element (not null). The
- * value must be between <code>[1..p-1]</code>
- * @param groupP
- * {forge.jsbn.BigInteger} The p parameter, as a BigInteger.
- * @param groupQ
- * {forge.jsbn.BigInteger} The q parameter, as a BigInteger.
- * @throws CryptoLibException
- * if there are any problems with the inputs.
- */
- box.commons.mathematical.ZpGroupElement = function(value, groupP, groupQ) {
- if ((typeof value === 'undefined')) {
- throw new exceptions.CryptoLibException(
- 'The received value is not the expected object');
- }
- if ((BigInteger.ZERO.compareTo(value) >= 0) ||
- (groupP.compareTo(value) <= 0)) {
- throw new exceptions.CryptoLibException(
- 'The value of the element should be between 1 and p-1. P: ' + groupP +
- ', value: ' + value);
- }
- if ((BigInteger.ZERO.compareTo(groupQ) >= 0) ||
- (groupP.compareTo(groupQ) <= 0)) {
- throw new exceptions.CryptoLibException(
- 'The relationship between the values of the p and q parameters should hold that 0 < q < p');
- }
- var v = value;
- var p = groupP;
- var q = groupQ;
- /**
- * Performs a basic check to verify if this element belongs to the same
- * group as <code>element</code> by checking if the p and q parameters
- * of both are equal.
- * <p>
- * Note: this function does NOT perform the mathematical operation
- * needed to actually confirm that a value is an element of a group,
- * which involves modular exponentiation and is very computationally
- * costly. Instead, this function simply examines the p and q parameters
- * that are stored within both elements.
- *
- * @function
- * @param element
- * {ZpGroupElement} element to by multiplied with this
- * element.
- * @throws CryptoLibException
- * if the p and q parameters of the group that the received
- * element belongs to are not equal to the p and q
- * parameters of this element.
- */
- this._validateReceivedElementIsFromSameGroup = function(element) {
- if (typeof element === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'The received value is not the expected object');
- }
- if ((!this.getP().equals(element.getP())) ||
- (!this.getQ().equals(element.getQ()))) {
- throw new exceptions.CryptoLibException(
- 'Operations can only be performed on group elements which are members of the same Zp subgroup');
- }
- };
- /**
- * Performs a basic check to verify if this element belongs to the same
- * group as <code>exponent</code> by checking if the p and q
- * parameters of both are equal.
- * <p>
- * Note: this function does NOT perform the mathematical operation
- * needed to actually confirm that an exponent belongs to a particular
- * group, which would involve confirming the following:
- * <p>
- * <code>0 >= exponent value <= p-1</code>
- *
- * @function
- * @param exponent
- * {Exponent} element to by multiplied with this element.
- * @throws CryptoLibException
- * if p and q parameters of the group that the received
- * element belongs to are not equal to the p and q
- * parameters of this element.
- */
- this._validateReceivedExponent = function(exponent) {
- if (typeof exponent === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'The received value is not the expected object');
- }
- if (!this.getQ().equals(exponent.getQ())) {
- throw new exceptions.CryptoLibException(
- 'The exponent should have the same q as this Zp group element');
- }
- };
- /**
- * @function
- * @returns The value of this element.
- */
- this.getElementValue = function() {
- return v;
- };
- /**
- * @function
- * @returns The p parameter of the mathematical group to which this
- * element belongs.
- */
- this.getP = function() {
- return p;
- };
- /**
- * @function
- * @returns The q parameter of the mathematical group to which this
- * element belongs.
- */
- this.getQ = function() {
- return q;
- };
- };
- box.commons.mathematical.ZpGroupElement.prototype = {
- /**
- * Multiple this element by the received element. The operation is
- * performed mod p. Performs a basic check to confirm that this element
- * and the received element belong to the same group.
- *
- * @function
- * @param element
- * {ZpGroupElement} element to by multiplied with this
- * element.
- * @returns <code>(this * element) mod p</code>, as a ZpGroupElement.
- * @throws CryptoLibException
- * if the p and q parameters of the group that the received
- * element belongs to are not equal to the p and q
- * parameters of this element.
- */
- multiply: function(element) {
- this._validateReceivedElementIsFromSameGroup(element);
- var result = this.getElementValue()
- .multiply(element.getElementValue())
- .mod(this.getP());
- return new box.commons.mathematical.ZpGroupElement(
- result, this.getP(), this.getQ());
- },
- /**
- * Exponentiate this element to the received exponent.
- *
- * @function
- * @param exponent
- * {Exponent} the exponent used to raise the value of the
- * group element.
- * @returns <code>(this<sup>exponent</sup>) mod p</code>, as a
- * ZpGroupElement.
- * @throws CryptoLibException
- * if the p and q parameters of the group that the received
- * exponent belongs to are not equal to the p and q
- * parameters of this element.
- */
- exponentiate: function(exponent) {
- this._validateReceivedExponent(exponent);
- var result =
- this.getElementValue().modPow(exponent.getValue(), this.getP());
- return new box.commons.mathematical.ZpGroupElement(
- result, this.getP(), this.getQ());
- },
- /**
- * Get the inverse of this element, mod p.
- *
- * @function
- * @returns the inverse of this element mod p, as a ZpGroupElement.
- */
- invert: function() {
- var result = this.getElementValue().modInverse(this.getP());
- return new box.commons.mathematical.ZpGroupElement(
- result, this.getP(), this.getQ());
- },
- /**
- * Check if this element is equal to the received element.
- * <p>
- * Elements are considered equal if:
- * <ul>
- * <li>They have the same value.</li>
- * <li>They belong to the same group (which is confirmed here by
- * checking if their p and q parameters are equal).</li>
- * </ul>
- *
- * @function
- * @param element
- * {ZpGroupElement} the element which should be checked
- * against this element for equality.
- * @returns true if the given group element has the same <code>v</code>,
- * the same <code>p</code> and the same <code>q</code>.
- */
- equals: function(element) {
- if (typeof element !== 'undefined' &&
- element.getElementValue().equals(this.getElementValue()) &&
- element.getP().equals(this.getP()) &&
- element.getQ().equals(this.getQ())) {
- return true;
- } else {
- return false;
- }
- },
- /**
- * Displays a string representation of this Zp group element JSON.
- * <p>
- * Note: in order to permit interoperability between libraries, this
- * representation should match the equivalent representation in Java.
- *
- * @function
- * @returns a string representation of Zp group element JSON.
- */
- stringify: function() {
- return JSON.stringify({
- zpGroupElement: {
- p: converters.base64FromBigInteger(this.getP()),
- q: converters.base64FromBigInteger(this.getQ()),
- value: converters.base64FromBigInteger(this.getElementValue())
- }
- });
- }
- };
- /**
- * Represents an exponent for a particular mathematical group.
- * <p>
- * The value of an exponent must be within the range [0..q-1]. If the
- * received value is not within this range, then the value that will be
- * assigned to the created Exponent will be calculated as follows:
- * <p>
- * <code>value = value mod q</code>
- *
- * @class
- *
- * @param q
- * {forge.jsbn.BigInteger} the order of the mathematical group
- * for which this is an exponent.
- * @param exponentValue
- * {forge.jsbn.BigInteger} the value of the exponent.
- * @throws CryptoLibException
- * if there are any problems with the inputs.
- */
- box.commons.mathematical.Exponent = function(valueQ, exponentValue) {
- if (valueQ === 'undefined' || valueQ.compareTo(BigInteger.ZERO) === 0) {
- throw new exceptions.CryptoLibException(
- 'Q (the order of the group) cannot be null or zero');
- }
- var _q = valueQ;
- /**
- * Gets the value to set for this Exponent. This value has to be a
- * number between <code>0</code> and <code>q-1</code> (inclusive),
- * so if it is less than <code>0</code> or greater than
- * <code>q-1</code>, then <code>mod q</code> has to be applied.
- *
- * @function
- * @param {forge.jsbn.BigInteger}
- * exponentValue the value of the exponent.
- * @returns the value to set to this exponent.
- */
- function getExponent(exponentValue) {
- if ((_q.compareTo(exponentValue) > 0) &&
- (BigInteger.ZERO.compareTo(exponentValue) <= 0)) {
- return exponentValue;
- } else {
- return exponentValue.mod(_q);
- }
- }
- var _value = getExponent(exponentValue);
- /**
- * Checks if this exponent and the received exponent belong to the same
- * group.
- *
- * @function
- * @param exponent
- * {Exponent} the exponent which should be checked to see if
- * it belongs to the same group as this exponent.
- * @throws CryptoLibException
- * if the received exponent is undefined or does not belong
- * to the same group as this exponent.
- */
- this._confirmSameGroup = function(exponent) {
- if (typeof exponent === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'The received exponent is not the expected object');
- }
- if (!_q.equals(exponent.getQ())) {
- throw new exceptions.CryptoLibException(
- 'Operations may only be performed with exponents of the same mathematical group order');
- }
- };
- /**
- * @function
- * @returns The numeric value of the exponent.
- */
- this.getValue = function() {
- return _value;
- };
- /**
- * @function
- * @returns The numeric value of the exponent.
- */
- this.getQ = function() {
- return _q;
- };
- };
- box.commons.mathematical.Exponent.prototype = {
- /**
- * Returns an Exponent whose value is:
- * <p>
- * <code>(this + exponent) mod q</code>
- *
- * @param exponent
- * {Exponent} the exponent to be added to this exponent.
- * @returns <code>(this + exponent) mod q</code>
- */
- add: function(exponent) {
- this._confirmSameGroup(exponent);
- var result = this.getValue().add(exponent.getValue()).mod(this.getQ());
- return new box.commons.mathematical.Exponent(this.getQ(), result);
- },
- /**
- * Returns an Exponent whose value is:
- * <p>
- * <code>(this - exponent) mod q</code>
- *
- * @param exponent
- * {Exponent} the exponent to be subtracted from this
- * exponent.
- * @returns <code>(this - exponent) mod q</code>
- */
- subtract: function(exponent) {
- this._confirmSameGroup(exponent);
- var result =
- this.getValue().subtract(exponent.getValue()).mod(this.getQ());
- return new box.commons.mathematical.Exponent(this.getQ(), result);
- },
- /**
- * Returns an Exponent whose value is:
- * <p>
- * <code>(this * exponent) mod q</code>
- *
- * @param exponent
- * {Exponent} the exponent to be multiplied with this
- * exponent.
- * @returns <code>(this * exponent) mod q</code>
- */
- multiply: function(exponent) {
- this._confirmSameGroup(exponent);
- var result =
- this.getValue().multiply(exponent.getValue()).mod(this.getQ());
- return new box.commons.mathematical.Exponent(this.getQ(), result);
- },
- /**
- * Returns an Exponent whose value is <code>(-this) mod q</code>
- *
- * @returns <code>(-this mod q)</code>
- */
- negate: function() {
- return new box.commons.mathematical.Exponent(
- this.getQ(), this.getValue().negate().mod(this.getQ()));
- },
- /**
- * Check if this exponent is equal to the received exponent.
- * <p>
- * Elements are considered equal if:
- * <ul>
- * <li>They have the same value.</li>
- * <li>They belong to the groups with the same order.</li>
- * </ul>
- *
- * @param {Exponent}
- * exponent the exponent to be checked against this exponent
- * for equality.
- * @returns true if the given exponent has the same <code>value</code>
- * and belongs to the same <code>group</code> as this
- * exponent.
- */
- equals: function(exponent) {
- if (exponent.getValue().equals(this.getValue()) &&
- exponent.getQ().equals(this.getQ())) {
- return true;
- } else {
- return false;
- }
- },
- /**
- * Displays a string representation of the exponent JSON.
- * <p>
- * Note: in order to permit interoperability between libraries, this
- * representation should match the equivalent representation in Java.
- *
- * @function
- * @returns a string representation of the exponent JSON.
- */
- stringify: function() {
- return JSON.stringify({
- exponent: {
- q: converters.base64FromBigInteger(this.getQ()),
- value: converters.base64FromBigInteger(this.getValue())
- }
- });
- }
- };
- /**
- * Provides utility functionality for working with mathematical groups.
- *
- * @class groupUtils
- * @memberof commons/mathematical
- */
- box.commons.mathematical.groupUtils = (function() {
- /**
- * Deserializes a Zp Subgroup string representation to a ZpSubgroup
- * object.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param groupJson
- * {JSON} a JSON representation of a Zp subgroup.
- * @returns a new ZpSubgroup, created from the received string.
- */
- function deserializeGroup(groupJson) {
- var parsed = JSON.parse(groupJson);
- var p = converters.base64ToBigInteger(parsed.zpSubgroup.p);
- var q = converters.base64ToBigInteger(parsed.zpSubgroup.q);
- var g = converters.base64ToBigInteger(parsed.zpSubgroup.g);
- return new box.commons.mathematical.ZpSubgroup(g, p, q);
- }
- /**
- * Deserializes a Zp Subgroup element string to a ZpGroupElement object.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param groupElementJson
- * {JSON} a JSON representation of a Zp Subgroup element
- * element.
- * @returns a new ZpGroupElement, created from the received string.
- */
- function deserializeGroupElement(groupElementJson) {
- var parsed = JSON.parse(groupElementJson);
- var value = converters.base64ToBigInteger(parsed.zpGroupElement.value);
- var p = converters.base64ToBigInteger(parsed.zpGroupElement.p);
- var q = converters.base64ToBigInteger(parsed.zpGroupElement.q);
- return new box.commons.mathematical.ZpGroupElement(value, p, q);
- }
- /**
- * Deserializes an exponent string to an Exponent object.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param exponentJson
- * {JSON} a JSON representation of an exponent.
- * @returns a new Exponent, created from the received string.
- */
- function deserializeExponent(exponentJson) {
- var parsed = JSON.parse(exponentJson);
- var q = converters.base64ToBigInteger(parsed.exponent.q);
- var value = converters.base64ToBigInteger(parsed.exponent.value);
- return new box.commons.mathematical.Exponent(q, value);
- }
- /**
- * Compress a list of group elements.
- * <P>
- * Note: an exception will be thrown if an array is received which
- * contains any elements that are not elements of the mathematical
- * group.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param group
- * {ZpSubgroup} the mathematical group.
- * @param elementsToBeCompressedArray
- * {object} the array of group elements to be compressed.
- * @returns the result of the compression process, as a group element.
- * @throws CryptoLibException
- * if there are any problems with the inputs.
- */
- function compressGroupElements(group, elementsToBeCompressedArray) {
- validateArray(elementsToBeCompressedArray);
- validateGroup(group);
- var elem = elementsToBeCompressedArray[0];
- for (var i = 1; i < elementsToBeCompressedArray.length; i++) {
- elem = elem.multiply(elementsToBeCompressedArray[i]);
- }
- return elem;
- }
- /**
- * Compress a list of exponents.
- * <P>
- * Note: an exception will be thrown if an array is received which
- * contains any exponents that are not exponents of the mathematical
- * group.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param group
- * {ZpSubgroup} the mathematical group
- * @param exponentsToBeCompressedArray
- * {object} the array of exponents to be compressed
- * @returns the result of the compression process, as an exponent.
- * @throws CryptoLibException
- * if there are any problems with the inputs.
- */
- function compressExponents(group, exponentsToBeCompressedArray) {
- validateArray(exponentsToBeCompressedArray);
- validateGroup(group);
- validateExponents(group.getQ(), exponentsToBeCompressedArray);
- var elem = exponentsToBeCompressedArray[0];
- for (var i = 1; i < exponentsToBeCompressedArray.length; i++) {
- elem = elem.add(exponentsToBeCompressedArray[i]);
- }
- return elem;
- }
- /**
- * Builds a new array of elements from the input array, where the final
- * elements from the input array are compressed into a simple element.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param group
- * {ZpSubgroup} the mathematical group that this compressor
- * operates on.
- * @param inputArray
- * {object} the input array of group elements.
- * @param numElementsRequiredInNewList
- * {number} the number of elements that should be in the
- * output array.
- * @returns a new array of group elements.
- * @throws CryptoLibException
- * if there are any problems with the inputs.
- */
- function buildListWithCompressedFinalElement(
- group, inputArray, numElementsRequiredInNewList) {
- validateArray(inputArray);
- validateGroup(group);
- if (numElementsRequiredInNewList < 1) {
- throw new exceptions.CryptoLibException(
- 'The number of elements in the output list must be at least 1');
- }
- var offset = numElementsRequiredInNewList - 1;
- var outputArray = inputArray.slice(0, offset);
- var lastElement = compressGroupElements(
- group, inputArray.slice(offset, inputArray.length));
- outputArray.push(lastElement);
- return outputArray;
- }
- /**
- * Builds a new array of exponents from the input array, where the final
- * exponents from the input array are compressed into a simple
- * exponents.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param group
- * {ZpSubgroup} the mathematical group that this compressor
- * operates on.
- * @param inputArray
- * {object} the input array of exponents.
- * @param numExponentsRequiredInNewList
- * {number} the number of exponents that should be in the
- * output array.
- * @returns a new array of group elements.
- * @throws CryptoLibException
- * if there are any problems with the inputs.
- */
- function buildListWithCompressedFinalExponent(
- group, inputArray, numExponentsRequiredInNewList) {
- validateArray(inputArray);
- validateGroup(group);
- if (numExponentsRequiredInNewList < 1) {
- throw new exceptions.CryptoLibException(
- 'The number of exponents in the output list must be at least 1');
- }
- var offset = numExponentsRequiredInNewList - 1;
- var outputArray = inputArray.slice(0, offset);
- var lastExponent =
- compressExponents(group, inputArray.slice(offset, inputArray.length));
- outputArray.push(lastExponent);
- return outputArray;
- }
- /**
- * Given two arrays of group elements, this method produces a new list
- * of group elements that represents the two input lists combined.
- * <P>
- * The two arrays must only contain values that are elements of the
- * mathematical group that this divider operates on. If either array
- * contains any element that is not a group element then an exception
- * will be thrown.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param arrayElements1
- * {object} an input array of Zp group elements.
- * @param arrayElements2
- * {object} an input array of Zp group elements.
- * @returns a new list of group elements.
- * @throws CryptoLibException
- * if there are any problems with the inputs.
- */
- function divide(arrayElements1, arrayElements2) {
- validateArray(arrayElements1);
- validateArray(arrayElements2);
- if (arrayElements1.length !== arrayElements2.length) {
- throw new exceptions.CryptoLibException(
- 'Both arrays should have the same length');
- }
- var newList = [];
- var newElement;
- for (var i = 0; i < arrayElements1.length; i++) {
- newElement = arrayElements1[i].multiply(arrayElements2[i].invert());
- newList.push(newElement);
- }
- return newList;
- }
- /**
- * Given an array of group elements and one exponent, this method
- * produces a new list of group elements that represents the
- * exponentiation of the group elements to the exponent.
- * <P>
- * The array must contain values that are elements of the mathematical
- * group that this exponentiation activity operates on. If the array
- * contains any element that is not a group element then an exception
- * will be thrown.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param {array} arrayElements
- * an input array of Zp group elements.
- * @param {object} exponent
- * An Exponent.
- * @param {object} group
- * The mathematical group that all of the elements in
- * arrayElements belong to.
- * @param {boolean} validateElements An Exponent.
- * @return A new list of group elements.
- *
- */
- function exponentiateArrays(
- arrayElements, exponent, group, validateElements) {
- validateArray(arrayElements);
- validateExponent(exponent);
- validateGroup(group);
- // If requested, validate all elements prior to any operations.
- if (typeof validateElements !== 'undefined' && validateElements) {
- for (var j = 0; j < arrayElements.length; j++) {
- if (!group.isGroupMember(arrayElements[j])) {
- throw new exceptions.CryptoLibException(
- 'The element does not belong to the group');
- }
- }
- }
- var newList = [];
- var newElement;
- for (var i = 0; i < arrayElements.length; i++) {
- newElement = arrayElements[i].exponentiate(exponent);
- newList.push(newElement);
- }
- return newList;
- }
- /**
- * Returns a new random exponent for the received mathematical group.
- * <P>
- * The value of the created exponent will be between <code>0</code>
- * and q-1.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param group
- * {ZpSubgroup} the mathematical group.
- * @param cryptoRandomInteger
- * {CryptoScytlRandomInteger} the source of randomness.
- * @param {boolean}
- * useShortExponent true if a short exponent is to be
- * generated. Default value is false.
- * @returns a new random exponent.
- * @throws CryptoLibException
- * if there are any problems with the inputs.
- */
- function generateRandomExponent(
- group, cryptoRandomInteger, useShortExponent) {
- validateGroup(group);
- if (typeof cryptoRandomInteger === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'The given parameters should be initialized');
- }
- var _useShortExponent = true;
- if (!useShortExponent) {
- _useShortExponent = false;
- }
- var q = group.getQ();
- var qBitLength = q.bitLength();
- var randomExponentLength;
- if (_useShortExponent) {
- if (qBitLength < box.SHORT_EXPONENT_BIT_LENGTH) {
- throw new exceptions.CryptoLibException(
- 'Zp subgroup order bit length must be greater than or equal to short exponent bit length : ' +
- box.SHORT_EXPONENT_BIT_LENGTH + '; Found ' + qBitLength);
- }
- randomExponentLength = box.SHORT_EXPONENT_BIT_LENGTH;
- } else {
- randomExponentLength = qBitLength;
- }
- var randomExponentValue;
- var randomExponentFound = false;
- while (!randomExponentFound) {
- randomExponentValue =
- cryptoRandomInteger.nextRandomByBits(randomExponentLength);
- if (randomExponentValue.compareTo(q) < 0) {
- randomExponentFound = true;
- }
- }
- return new box.commons.mathematical.Exponent(q, randomExponentValue);
- }
- /**
- * Returns a group element symmetric key which is obtained by
- * exponentiating the generator of a given mathematical group to a
- * random exponent <code>k</code>.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param group
- * {ZpSubgroup} the mathematical group.
- * @param cryptoRandomInteger
- * {CryptoScytlRandomInteger} the source of randomness.
- * @returns the secret key, as a group element.
- * @throws CryptoLibException
- * if there are any problems with the inputs.
- */
- function generateGroupElementSecretKey(group, cryptoRandomInteger) {
- var exponent = generateRandomExponent(group, cryptoRandomInteger);
- var groupElementSecretKey = group.getGenerator().exponentiate(exponent);
- return groupElementSecretKey;
- }
- /**
- * Build a ZpSubgroup from the p and g parameters.
- * <P>
- * NOTE: This method builds a particular type of mathematical group with
- * property that:
- * <P>
- * P = (Q * 2) + 1
- * <P>
- * This property holds for all groups generated using this method, but
- * this property does not hold for all mathematical groups.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param p
- * {forge.jsbn.BigInteger} the p parameter of the group.
- * @param g
- * {forge.jsbn.BigInteger} the generator of the group.
- * @returns the generated ZpSubgroup.
- * @throws CryptoLibException
- * if there are any problems with the inputs.
- */
- function buildZpSubgroupFromPAndG(p, g) {
- validateBuildFromPAndGInputs(p, g);
- var q = p.subtract(BigInteger.ONE).divide(new BigInteger('2'));
- return new box.commons.mathematical.ZpSubgroup(g, p, q);
- }
- /**
- * Build a random ZpSubgroup whose p parameter will have the specified
- * bit length. The certainty that both p and q are both prime is
- * specified by certainty.
- * <P>
- * NOTE: This method builds a particular type of Zp subgroup with
- * property that:
- * <P>
- * p = (q * 2) + 1
- * <P>
- * This property holds for all mathematical groups generated using this
- * method, but this property does not hold for all mathematical groups.
- * <P>
- * Note: the minimum bit length of the p parameter that is permitted by
- * this method is 2. If a bit length less than 2 is requested then a
- * CryptoLibException will be thrown.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param bitLengthOfP
- * {number} the bit length that the p parameter of the
- * generated group should be.
- * @param cryptoRandomInteger
- * {CryptoScytlRandomInteger} an instance of
- * CryptoRandomInteger that can be used as a source of random
- * integers.
- * @param certainty
- * {number} the required level of certainty that the q
- * parameter of the generated group is prime.
- * @returns the generated ZpSubgroup.
- * @throws CryptoLibException
- * if there are any problems with the inputs.
- */
- function buildRandomZpSubgroupFromLengthOfP(
- bitLengthOfP, cryptoRandomBytes, certainty) {
- validateBuildFromLengthPInputs(
- bitLengthOfP, cryptoRandomBytes, certainty);
- var p, q;
- var twoBI = new BigInteger('2');
- var randomGroupFound = false;
- var options = {prng: cryptoRandomBytes};
- var callback = function(err, num) {
- if (err) {
- throw new exceptions.CryptoLibException(
- 'Error while generating prime ' + err);
- }
- p = num;
- q = num.subtract(BigInteger.ONE).divide(twoBI);
- };
- while (!randomGroupFound) {
- forge.prime.generateProbablePrime(bitLengthOfP, options, callback);
- if (q.isProbablePrime(certainty)) {
- randomGroupFound = true;
- }
- }
- var generator = findSmallestGenerator(p, q);
- return new box.commons.mathematical.ZpSubgroup(generator, p, q);
- }
- /**
- * Given an input array containing possible group members, this method
- * cycles through that array, checking if each element is a group
- * member. This method attempts to find the specified number of group
- * members. Once that number has been found, the method will return a
- * new array containing the specified number of group members.
- * <p>
- * If the required number of group members are not found in the input
- * array then a CryptoLibException will be thrown.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @param possibleGroupMembersArray
- * {object} an array containing possible group members.
- * @param group
- * {ZpSubgroup} the Zp subgroup for which the specified
- * number of group members are required.
- * @param numMembersRequired
- * {number} the number of group members of the specified
- * group that should be added to the output list.
- * @returns a new array containing the specified number of group
- * elements, which have been read from the input array.
- * @throws CryptoLibException
- * if there are any problems with the inputs.
- */
- function extractNumberOfGroupMembersFromListOfPossibleMembers(
- possibleGroupMembersArray, group, numMembersRequired) {
- validateExtractFromListInputs(
- possibleGroupMembersArray, group, numMembersRequired);
- var outputArray = [];
- var membersFound = 0;
- var candidate;
- for (var i = 0; i < possibleGroupMembersArray.length; i++) {
- candidate = new box.commons.mathematical.ZpGroupElement(
- possibleGroupMembersArray[i], group.getP(), group.getQ());
- if (group.isGroupMember(candidate)) {
- outputArray.push(candidate);
- membersFound++;
- }
- if (membersFound === numMembersRequired) {
- return outputArray;
- }
- }
- if (membersFound !== numMembersRequired) {
- throw new exceptions.CryptoLibException(
- '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);
- }
- }
- /**
- * Find the smallest generator of a mathematical group.
- * <p>
- * Note: starts with a candidate generator value (two is the initial
- * candidate generator value), and checks if the candidate generator is
- * a group memeber. If that candidate is a group memeber then return
- * that value, else increment the candidate by one and again check if
- * the candidate is a group member. Continue this process until the
- * candidate value is less than the parameter p.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @params p {number} the p parameter of the group.
- * @params q {number} the q parameter of the group.
- * @throws CryptoLibException
- */
- function findSmallestGenerator(p, q) {
- 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 exceptions.CryptoLibException(
- 'Failed to find a generator, p was: ' + p + ', q was: ' + q);
- }
- return g;
- }
- /**
- * Validates a group.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @params group {ZpSubgroup} the group to be validated.
- * @throws CryptoLibException
- * if the received group is undefined.
- */
- function validateGroup(group) {
- if (typeof group === 'undefined' || null === group) {
- throw new exceptions.CryptoLibException(
- 'The given group should be initialized');
- }
- }
- function validateExponent(exponent) {
- if (typeof exponent === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'The given exponent should be initialized');
- }
- }
- /**
- * Validates that an array is initialized and non-empty.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @params inputArray {object} an array to be validated.
- * @throws CryptoLibException
- * if the received array is not initialized or is empty.
- */
- function validateArray(inputArray) {
- if (typeof inputArray === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'The given array should be initialized');
- }
- if (!(inputArray instanceof Array)) {
- throw new exceptions.CryptoLibException(
- 'The given array are not from the expected type');
- }
- if (inputArray.length < 1) {
- throw new exceptions.CryptoLibException(
- 'The given array cannot be empty');
- }
- }
- /**
- * Performs a very basic validation on the received inputs.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @params p {object} the first parameter.
- * @params g {object} the second parameter.
- * @throws CryptoLibException
- * if either (or both) inputs are undefined.
- */
- function validateBuildFromPAndGInputs(p, g) {
- if ((typeof p === 'undefined') || (typeof g === 'undefined')) {
- throw new exceptions.CryptoLibException('p or g are incorrect');
- }
- }
- /**
- * Performs a some basic validations on the received inputs.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @params bitLengthOfP {number} bit length of p parameter.
- * @params cryptoRandomBytes {CryptoScytlRandomBytes} a secure source of
- * random bytes.
- * @params certainty {number} a certainty level.
- * @throws CryptoLibException
- * if any of the inputs fail any of the validations.
- */
- function validateBuildFromLengthPInputs(
- bitLengthOfP, cryptoRandomBytes, certainty) {
- if (bitLengthOfP < 2) {
- throw new exceptions.CryptoLibException(
- 'The bit length should be higher');
- }
- if (typeof cryptoRandomBytes === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'The random generator should be initialized');
- }
- if (certainty < box.MINIMUM_PRIME_CERTAINTY_LEVEL) {
- throw new exceptions.CryptoLibException('Certainty should be higher');
- }
- }
- /**
- * Performs some basic validations on the received inputs.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @params inputArray {object} an array.
- * @params group {ZpSubgroup} a Zp subgroup.
- * @params numMembersRequired {number} an integer value.
- * @throws CryptoLibException
- * if any of the inputs fail any of the validations.
- */
- function validateExtractFromListInputs(
- inputArray, group, numMembersRequired) {
- validateArray(inputArray);
- validateGroup(group);
- if (typeof numMembersRequired === 'undefined') {
- throw new exceptions.CryptoLibException(
- 'The given objects should be initialized');
- }
- if (typeof numMembersRequired !== 'number') {
- throw new exceptions.CryptoLibException(
- 'The given objects are not from the expected type');
- }
- if (numMembersRequired < 1 || numMembersRequired > inputArray.length) {
- throw new exceptions.CryptoLibException(
- 'The given number of required elements cannot be higher than the number of elements of the array');
- }
- }
- /**
- * Validates that all of the exponents in the received list are
- * exponents are members of the received group, by checking if the
- * groups that they contain are equal.
- *
- * @function
- * @memberof commons/mathematical.groupUtils
- * @params group {ZpSubgroup} a Zp subgroup.
- * @params exponents {object} a list of exponents.
- */
- function validateExponents(q, exponents) {
- for (var i = 0; i < exponents.length; i++) {
- if (!exponents[i].getQ().equals(q)) {
- throw new exceptions.CryptoLibException(
- 'The list of exponents contained an exponent which does not belong to the group with the same order.');
- }
- }
- }
- return {
- buildZpSubgroupFromPAndG: buildZpSubgroupFromPAndG,
- buildRandomZpSubgroupFromLengthOfP: buildRandomZpSubgroupFromLengthOfP,
- extractNumberOfGroupMembersFromListOfPossibleMembers:
- extractNumberOfGroupMembersFromListOfPossibleMembers,
- generateRandomExponent: generateRandomExponent,
- generateGroupElementSecretKey: generateGroupElementSecretKey,
- compressGroupElements: compressGroupElements,
- buildListWithCompressedFinalElement: buildListWithCompressedFinalElement,
- deserializeGroup: deserializeGroup,
- deserializeGroupElement: deserializeGroupElement,
- deserializeExponent: deserializeExponent,
- compressExponents: compressExponents,
- buildListWithCompressedFinalExponent:
- buildListWithCompressedFinalExponent,
- divide: divide,
- exponentiateArrays: exponentiateArrays
- };
- })();
- };
|