/* * 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 elGamal = require('scytl-elgamal'); var mathematical = require('scytl-mathematical'); var forge = require('node-forge'); module.exports = CommonTestData; var BigInteger = forge.jsbn.BigInteger; /** * Provides the common data and functions needed by the zero-knowledge proof of * knowledge unit tests. */ function CommonTestData() { var mathService = mathematical.newService(); var mathRandomGenerator_ = mathService.newRandomGenerator(); var elGamalService_ = elGamal.newService(); var p_ = new BigInteger( '25701322016688441576103032148674110774925156427397637132202577741938936247112125492211659378990027134829873026348759004510642939209382852797923423233606398455768339503774439196473285206499419017249519687083918862292745452369770073017370862703937184066962152692474974647384317528679859283948201449898123774491462507974828236623365121967170401847974506359775053259024902177376974036791676038887180065668218851769610708183278395407393103098286493672450097566567641277262751905925272593655979627326843104589717100845609213331825322091528671293329804211235906640910489075537455976735341285644494144220105240375300711488393'); var q_ = new BigInteger( '81259987775362040571620137377019110900790585487969528507141757273696249070213'); var g_ = new BigInteger( '21303916314604998551484796420422871007468543247831789236879215369694842734099405650535148287249791144057375987697205687518340016316179892851284240278138045703633698036256611527988640313051528625117090689542380890153876064768602704006774164341226969138952495075758675629665109761968589385514561688161321002676900768077022957299407868881468752268501397255241070908793893954188678747644443826465068701078202311876709764302561277744548084228639372458598925393551439452959527949768006498598646518365505226629313181567048632516237514971091380204424481498506499338185144271546651990709723565928420753129878206710329253950578'); var group_ = mathService.newZpSubgroup(p_, q_, g_); var anotherP_ = new BigInteger( '22115396014581083143553195792384915983375949993753365299824374182778648887213105675373952839695834312125954513956465041609718392131788741228259266178434294275575978809365653903991346366383637500069010795276889266879661859362340655085050484891149389971190945080643692669920059928787982790808824970597361825060408436004650739931286605415552480363242364319544423959620330010413847743583301091182870021747196927889763205955078275353307312408480155002017730305672359814713050632922694161739281353676187308501682728826507718002850698488786266115311952718697872338756199429262811547458969260835283688979875294699655014592833'); var anotherQ_ = new BigInteger( '79783631514788897688995869254536231266441581267248557197866166084121363399063'); var anotherG_ = new BigInteger( '9484337033848536557358945372517536146444081505783459082983213315915020922388822299017798461933010391712259501201285084235930755858547311934865298396110947362913733900057881561201738951725697128822802249693116611294569213895789823297341865759919045291915705783128173481781221176839434866976235031358969583527093743179257827355453820268832913547364509208164670680381449515691300440305333919098105147193087681183020489724214850151272955879869448467832934822639783688344322183540621808345771717172404141548242866493539424279296365911355186761897533654055060787119955689115570505208857288933136456026541073605147828202749'); var anotherGroup_ = mathService.newZpSubgroup(anotherP_, anotherQ_, anotherG_); /** * @function getP * @returns {forge.jsbn.BigInteger} The modulus of a Zp subgroup. */ this.getP = function() { return p_; }; /** * @function getQ * @returns {forge.jsbn.BigInteger} The order of a Zp subgroup. */ this.getQ = function() { return q_; }; /** * @function getG * @returns {forge.jsbn.BigInteger} The generator of a Zp subgroup. */ this.getG = function() { return g_; }; /** * @function getGroup * @returns {ZpSubgroup} A Zp subgroup. */ this.getGroup = function() { return group_; }; /** * @function getAnotherP * @returns {forge.jsbn.BigInteger} The modulus of another Zp subgroup. */ this.getAnotherP = function() { return anotherP_; }; /** * @function getAnotherQ * @returns {forge.jsbn.BigInteger} The order of another Zp subgroup. */ this.getAnotherQ = function() { return anotherQ_; }; /** * @function getAnotherG * @returns {forge.jsbn.BigInteger} The generator of another Zp subgroup. */ this.getAnotherG = function() { return anotherG_; }; /** * @function getAnotherGroup * @returns {ZpSubgroup} Another Zp subgroup. */ this.getAnotherGroup = function() { return anotherGroup_; }; /** * Generates a random string. * * @function generateRandomString * @param {number} * length The number of characters that are to constitute the * string. * @returns {string} The random string. */ this.generateRandomString = function(length) { var charset = 'abcdefghijklmnopqrstuvwxyz0123456789'; var string = ''; for (var i = 0; i < length; i++) { string += charset.charAt(Math.floor(Math.random() * charset.length)); } return string; }; /** * Generates a random integer whose value is within a specified range. * * @function generateRandomInteger * @param {number} * min The minimum value of the integer. * @param {number} * max The maximum value of the integer. * @returns {number} The random integer. */ this.generateRandomInteger = function(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }; /** * Generates an ElGamal key pair. * * @function generateKeyPair * @param {ZpSubgroup} * group The Zp subgroup to which the public key elements belong * and to which the private key exponents are associated. * @param {number} * numElements The number of Zp group elements in the public key. * @returns {ElGamalKeyPair} The ElGamal key pair. */ this.generateKeyPair = function(group, numElements) { var elements = []; var exponents = []; var exponent; var element; for (var i = 0; i < numElements; i++) { exponent = mathRandomGenerator_.nextExponent(group); exponents.push(exponent); element = group.generator.exponentiate(exponent); elements.push(element); } var publicKey = elGamalService_.newPublicKey(group, elements); var privateKey = elGamalService_.newPrivateKey(group, exponents); return elGamalService_.newKeyPair(publicKey, privateKey); }; /** * Generates a random array of Zp group elements. * * @function generateRandomGroupElements * @param {ZpSubgroup} * group The Zp subgroup to which the Zp group elements belong. * @param {number} * numElements The number of Zp group elements to generate. * @returns {ZpGroupElement[]} The array of Zp group elements. */ this.generateRandomGroupElements = function(group, numElements) { var elements = []; for (var i = 0; i < numElements; i++) { elements.push(mathRandomGenerator_.nextZpGroupElement(group)); } return elements; }; /** * Exponentiates an array of Zp group base elements with an exponent. * * @function exponentiateElements * @param {ZpGroupElement[]} * baseElements The Zp group base elements to exponentiate. * @param {Exponent} * exponent The exponent to use for the exponentiation. * @return {ZpGroupElement[]} The array of exponentiated Zp group base * elements. */ this.exponentiateElements = function(baseElements, exponent) { var exponentiatedElements = []; for (var i = 0; i < baseElements.length; i++) { exponentiatedElements.push(baseElements[i].exponentiate(exponent)); } return exponentiatedElements; }; /** * * Generates an array of simple plaintext Zp group elements. By definition, * all of these elements will have the same value. The number of elements * requested must be an even number * * @function generateSimplePlaintext * @param {ZpSubgroup} * group The Zp subgroup to which the simple plaintext Zp group * elements belong. * @param {number} * numElements The number of simple plaintext Zp group elements * to generate. * @returns {ZpGroupElement[]} The array of simple plaintext Zp group * elements. * @throws {Error} * If the number of simple plaintext Zp group elements requested * was an odd number. */ this.generateSimplePlaintext = function(group, numElements) { var simplePlaintext = []; if ((numElements % 2) !== 0) { throw new Error( 'Simple plaintext must be generated using an even number of elements; Found ' + numElements); } var element = mathRandomGenerator_.nextZpGroupElement(group); for (var i = 0; i < numElements; i++) { simplePlaintext.push(element); } return simplePlaintext; }; /** * Generates an array of non-simple plaintext Zp group elements. By * definition, the array is non-simple because if it contains at least 2 * elements whose values are different. * * @function generateNonSimplePlaintext * @param {ZpSubgroup} * group The Zp subgroup to which all of the generated Zp group * elements belong. * @param {number} * numElements The number of non-simple Zp group elements to * generate. * @returns {ZpGroupElement[]} The array of non-simple Zp group elements. */ this.generateNonSimplePlaintext = function(group, numElements) { var nonSimplePlaintext = []; var element = mathRandomGenerator_.nextZpGroupElement(group); nonSimplePlaintext.push(element); for (var i = 1; i < numElements; i++) { do { element = mathRandomGenerator_.nextZpGroupElement(group); } while (element.value.equals(nonSimplePlaintext[i - 1].value)); nonSimplePlaintext.push(element); } return nonSimplePlaintext; }; /** * Retrieves an ElGamal public subkey from an existing ElGamal public key. * * @function getSubPublicKey * @param {ZpSubgroup} * group The Zp subgroup to which all Zp group elements of the * subkey belong. * @param {ElGamalPublicKey} * publicKey The ElGamal public from which to extract the subkey. * @param {number} * fromIndex The index (inclusive) of the array of Zp group * elements of the existing key that marks the beginning of the * subkey. * @param {number} * toIndex The index (exlusive) of the array of Zp group elements * of the existing key that marks the end of the subkey. * @returns {ElGamalPublic} The ElGamal public subkey. */ this.getSubPublicKey = function(group, publicKey, fromIndex, toIndex) { var subElements = []; var elements = publicKey.elements; for (var i = fromIndex; i < toIndex; i++) { subElements.push(elements[i]); } return elGamalService_.newPublicKey(group, subElements); }; /** * Retrieves an ElGamalEncryptedElements object from an existing * ElGamalEncryptedElements object that contains a subset of the phi * elements of the existing object. * * @function getSubEncryptedElements * @param {ElGamalEncryptedElements} * encryptedElements The ElGamalEncryptedElements object from * which to extract the new object. * @param {number} * fromIndex The index (inclusive) of the phi array of the * existing ElGamalEncryptedElements object that marks the * beginning of the new phi array. * @param {number} * toIndex The index (exclusive) of the phi array of the * ElGamalEncryptedElements object that marks the end of the * new phi array. * @returns {ElGamalEncryptedElements} The new ElGamalEncryptedElements * object that contains a subset of the phis. */ this.getSubEncryptedElements = function( encryptedElements, fromIndex, toIndex) { var gamma = encryptedElements.gamma; var phis = encryptedElements.phis; var subPhis = []; for (var i = fromIndex; i < toIndex; i++) { subPhis.push(phis[i]); } return elGamalService_.newEncryptedElements( gamma, subPhis, encryptedElements.secret); }; /** * Generates the ciphertext needed to generate a plaintext exponent equality * zero-knowledge proof of knowledge. The array of base elements provided as * input must have an odd number of elements. * * The procedure is the following: *
    *
  1. Generate the plaintext to encrypt by exponentiating a subset of the * base elements with the ephermeral key
  2. *
  3. Construct the ElGamal public key from an equal number of remaining * base elements
  4. *
  5. Generate the gamma element of the ciphertext by exponentiating the * remaining base element with the ephemeral key
  6. *
  7. Generate the phi array of the ciphertext by multiplying each * plaintext element with its corresponding public key element, the latter * being exponentiated with the message key
  8. *
  9. The resulting ciphertext can be used in a zero knowledge proof of * knowledge to show that each plaintext element was generated from the same * ephemeral key and that each phi element was generated from the same * message key.
  10. *
* * @param {ZpGroupElement[]} * baseElements The array of base elements. * @param {Exponent} * ephemeralKey The ephemeral key. * @param {Exponent} * messageKey The message key. * @returns {ElGamalEncryptedElements} The ciphertext. * @throws {Error} * If the number base elements provided as input is not an odd * number. */ this.generatePlaintextExponentEqualityProofCiphertext = function( baseElements, ephemeralKey, messageKey) { var numBaseElements = baseElements.length; if ((numBaseElements % 2) === 0) { throw new Error( 'Number of base elements must be an odd number for plaintext exponent equality proof generation; Found ' + numBaseElements); } var numPlaintextElements = ((numBaseElements - 1) / 2); var plaintext = []; for (var i = 0; i < numPlaintextElements; i++) { plaintext.push(baseElements[1 + i].exponentiate(ephemeralKey)); } var publicKeyElements = []; for (var j = 0; j < numPlaintextElements; j++) { publicKeyElements.push(baseElements[1 + numPlaintextElements + j]); } var gamma = baseElements[0].exponentiate(ephemeralKey); var phis = []; for (var k = 0; k < numPlaintextElements; k++) { phis.push( plaintext[k].multiply(publicKeyElements[k].exponentiate(messageKey))); } return elGamalService_.newEncryptedElements(gamma, phis); }; }