/* * 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 */ // The global namespace pollution and other glaring violations of anything // resembling best practices is due to the intended short-livedness of this // implementation. var MAX_EXPONENT_BYTES = 2; var PROGRESS_PERCENT_MIN_CHECK_INTERVAL = 10; var GENERATOR = '21303916314604998551484796420422871007468543247831789236879215369694842734099405650535148287249791144057375987697205687518340016316179892851284240278138045703633698036256611527988640313051528625117090689542380890153876064768602704006774164341226969138952495075758675629665109761968589385514561688161321002676900768077022957299407868881468752268501397255241070908793893954188678747644443826465068701078202311876709764302561277744548084228639372458598925393551439452959527949768006498598646518365505226629313181567048632516237514971091380204424481498506499338185144271546651990709723565928420753129878206710329253950578'; var P = '25701322016688441576103032148674110774925156427397637132202577741938936247112125492211659378990027134829873026348759004510642939209382852797923423233606398455768339503774439196473285206499419017249519687083918862292745452369770073017370862703937184066962152692474974647384317528679859283948201449898123774491462507974828236623365121967170401847974506359775053259024902177376974036791676038887180065668218851769610708183278395407393103098286493672450097566567641277262751905925272593655979627326843104589717100845609213331825322091528671293329804211235906640910489075537455976735341285644494144220105240375300711488393'; var Q = '81259987775362040571620137377019110900790585487969528507141757273696249070213'; var ANOTHER_GENERATOR = '9484337033848536557358945372517536146444081505783459082983213315915020922388822299017798461933010391712259501201285084235930755858547311934865298396110947362913733900057881561201738951725697128822802249693116611294569213895789823297341865759919045291915705783128173481781221176839434866976235031358969583527093743179257827355453820268832913547364509208164670680381449515691300440305333919098105147193087681183020489724214850151272955879869448467832934822639783688344322183540621808345771717172404141548242866493539424279296365911355186761897533654055060787119955689115570505208857288933136456026541073605147828202749'; var ANOTHER_P = '22115396014581083143553195792384915983375949993753365299824374182778648887213105675373952839695834312125954513956465041609718392131788741228259266178434294275575978809365653903991346366383637500069010795276889266879661859362340655085050484891149389971190945080643692669920059928787982790808824970597361825060408436004650739931286605415552480363242364319544423959620330010413847743583301091182870021747196927889763205955078275353307312408480155002017730305672359814713050632922694161739281353676187308501682728826507718002850698488786266115311952718697872338756199429262811547458969260835283688979875294699655014592833'; var ANOTHER_Q = '79783631514788897688995869254536231266441581267248557197866166084121363399063'; var _converters; var _keyFactory; var _elGamalCipherFactory; var _g; var _p; var _q; var _zpSubgroup; var _anotherZpSubgroup; var _progressPercentSum; var _progressPercentLatest; var isInitialized = false; function initializeTestVars() { if (!isInitialized) { cryptolib( 'commons.mathematical', 'commons.utils', 'homomorphic.keypair', 'homomorphic.cipher', function(box) { _converters = new box.commons.utils.Converters(); _keyFactory = new box.homomorphic.keypair.factory.KeyFactory(); _elGamalCipherFactory = new box.homomorphic.cipher.factory.ElGamalCipherFactory(); _g = new box.forge.jsbn.BigInteger(GENERATOR); _p = new box.forge.jsbn.BigInteger(P); _q = new box.forge.jsbn.BigInteger(Q); _zpSubgroup = new box.commons.mathematical.ZpSubgroup(_g, _p, _q); var g = new box.forge.jsbn.BigInteger(ANOTHER_GENERATOR); var p = new box.forge.jsbn.BigInteger(ANOTHER_P); var q = new box.forge.jsbn.BigInteger(ANOTHER_Q); _anotherZpSubgroup = new box.commons.mathematical.ZpSubgroup(g, p, q); initializeProgressMeter(); }); isInitialized = true; } } function generateRandomInteger(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } function generateRandomString(length) { var charset = 'abcdefghijklmnopqrstuvwxyz0123456789'; var string = ''; for (var i = 0; i < length; i++) string += charset.charAt(Math.floor(Math.random() * charset.length)); return string; } function generateRandomExponent(zpSubgroup) { var exponent; cryptolib('commons.mathematical', function(box) { do { var randomInt = new box.forge.jsbn.BigInteger( box.forge.random.getBytes(MAX_EXPONENT_BYTES)); exponent = new box.commons.mathematical.Exponent(zpSubgroup.getQ(), randomInt); } while (exponent.getValue().equals(box.forge.jsbn.BigInteger.ZERO)); }); return exponent; } function generateRandomGroupElement(zpSubgroup) { var element = zpSubgroup.getGenerator().exponentiate( generateRandomExponent(zpSubgroup)); cryptolib('commons.exceptions', function(box) { if (!zpSubgroup.isGroupMember(element)) { throw new box.commons.exceptions.CryptoLibException( 'Randomly generated element is not a member of the required mathematical group'); } }); return element; } function generateExponentiatedElements(baseElements, exponent) { var exponentiatedElements = []; for (var i = 0; i < baseElements.length; i++) { var element = baseElements[i].exponentiate(exponent); exponentiatedElements.push(element); } return exponentiatedElements; } function generatePlaintext(numElements) { var plaintext = []; var element = generateRandomGroupElement(_zpSubgroup); plaintext.push(element); for (var i = 1; i < numElements; i++) { element = generateRandomGroupElement(_zpSubgroup); plaintext.push(element); } return plaintext; } function generateSimplePlaintext(numElements) { var plaintext = []; cryptolib('commons.exceptions', function(box) { if ((numElements % 2) !== 0) { throw new box.commons.exceptions.CryptoLibException( 'Plaintext must have even number of elements for simple plaintext equality proof generation.'); } // Note that all elements of a simple plaintext must have the same value. var element = generateRandomGroupElement(_zpSubgroup); for (var i = 0; i < numElements; i++) { plaintext.push(element); } }); return plaintext; } function generateNonSimplePlaintext(numElements) { var plaintext = []; var element = generateRandomGroupElement(_zpSubgroup); plaintext.push(element); for (var i = 1; i < numElements; i++) { do { element = generateRandomGroupElement(_zpSubgroup); } while ( element.getElementValue().equals(plaintext[i - 1].getElementValue())); plaintext.push(element); } return plaintext; } function generateElGamalKeyPair(numElements) { var elements = []; var exponents = []; var exponent = generateRandomExponent(_zpSubgroup); var element = _zpSubgroup.getGenerator().exponentiate(exponent); exponents.push(exponent); elements.push(element); for (var i = 1; i < numElements; i++) { exponent = generateRandomExponent(_zpSubgroup); element = _zpSubgroup.getGenerator().exponentiate(exponent); exponents.push(exponent); elements.push(element); } var elGamalPublicKey = _keyFactory.createPublicKey(elements, _zpSubgroup); var elGamalPrivateKey = _keyFactory.createPrivateKey(exponents, _zpSubgroup); return _keyFactory.createKeyPair(elGamalPublicKey, elGamalPrivateKey); } function generateCiphertext(publicKey, plaintext) { var encrypter = _elGamalCipherFactory.createEncrypter(publicKey); return encrypter.encryptGroupElements(plaintext); } function generateExponentCiphertext( plaintext, primaryWitness, secondaryWitness) { var ciphertext; cryptolib('commons.exceptions', 'homomorphic.cipher', function(box) { var plaintextLength = plaintext.length; if ((plaintextLength % 2) === 0) { throw new box.commons.exceptions.CryptoLibException( 'Plaintext must have odd number of elements for plaintext exponent equality proof generation.'); } var numCiphertextElements = ((plaintextLength - 1) / 2) + 1; var exponents = []; exponents.push(primaryWitness); exponents.push(secondaryWitness); var gamma = plaintext[0].exponentiate(exponents[0]); var phis = []; for (var i = 0; i < (numCiphertextElements - 1); i++) { var phi = plaintext[i + 1].exponentiate(exponents[0]); phi = phi.multiply( plaintext[i + numCiphertextElements].exponentiate(exponents[1])); phis.push(phi); } ciphertext = new box.homomorphic.cipher.ElGamalEncrypterValues(null, gamma, phis); }); return ciphertext; } function getSubElGamalPublicKey(publicKey, fromIndex, toIndex) { var elements = publicKey.getGroupElementsArray(); var subElements = []; for (var i = fromIndex; i < toIndex; i++) { subElements.push(elements[i]); } return _keyFactory.createPublicKey(subElements, _zpSubgroup); } function getSubCiphertext(encrypterValues, fromIndex, toIndex) { var subCiphertext; cryptolib('homomorphic.cipher', function(box) { var exponent = encrypterValues.getR(); var gamma = encrypterValues.getGamma(); var phis = encrypterValues.getPhis(); var subPhis = []; for (var i = fromIndex; i < toIndex; i++) { subPhis.push(phis[i]); } subCiphertext = new box.homomorphic.cipher.ElGamalEncrypterValues( exponent, gamma, subPhis); }); return subCiphertext; } function initializeProgressMeter() { _progressPercentSum = 0; _progressPercentLatest = 0; } function progressCallback(progressPercent) { _progressPercentSum += progressPercent; _progressPercentLatest = progressPercent; }; function getProgressPercentSum(numChecks, minCheckInterval) { var percent = 0; var lastPercent = 0; var percentChange = 0; var incrementPercentSum = false; var percentSum = 0; for (var i = 0; i < numChecks; i++) { percent = Math.floor(((i + 1) / numChecks) * 100); percent = Math.min(percent, 100); percentChange = percent - lastPercent; if (percentChange > 0) { incrementPercentSum = (percentChange >= minCheckInterval) || (percent === 100); if (incrementPercentSum) { lastPercent = percent; percentSum += percent; } } } return percentSum; }