123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571 |
- /*
- * 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 CommonTestData = require('./data/common-data');
- var elGamal = require('../lib/index');
- var mathematical = require('scytl-mathematical');
- var secureRandom = require('scytl-securerandom');
- describe('The ElGamal cryptography module should be able to ...', function() {
- var elGamalService_;
- var secureRandomService_;
- var mathService_;
- var mathRandomGenerator_;
- var group_;
- var publicKey_;
- var privateKey_;
- var publicKeyElements_;
- var privateKeyExponents_;
- var keyPair_;
- var elements_;
- var elementValueStrings_;
- var largePublicKey_;
- var largePrivateKey_;
- var elementsFromLargeGroup_;
- var elementsFromLargeNonQrGroup_;
- var identityElements_;
- var shortExponentBitLength_;
- var singleElements_;
- var elGamalEncrypter_;
- var elGamalDecrypter_;
- var encryptedElements_;
- var elGamalEncrypterLargeGroup_;
- var elGamalDecrypterLargeGroup_;
- var prngSeed_;
- var encrypterRandomGenerator_;
- var decrypterRandomGenerator_;
- beforeAll(function() {
- elGamalService_ = elGamal.newService();
- secureRandomService_ = secureRandom.newService();
- mathService_ =
- mathematical.newService({secureRandomService: secureRandomService_});
- mathRandomGenerator_ = mathService_.newRandomGenerator();
- var testData = new CommonTestData();
- group_ = testData.getGroup();
- publicKey_ = testData.getPublicKey();
- privateKey_ = testData.getPrivateKey();
- publicKeyElements_ = testData.getPublicKeyElements();
- privateKeyExponents_ = testData.getPrivateKeyExponents();
- keyPair_ = testData.getKeyPair();
- elements_ = testData.getZpGroupElements();
- elementValueStrings_ = testData.getZpGroupElementValueStrings();
- largePublicKey_ = testData.getLargePublicKey();
- largePrivateKey_ = testData.getLargePrivateKey();
- elementsFromLargeGroup_ = testData.getElementsFromLargeZpSubgroup();
- elementsFromLargeNonQrGroup_ =
- testData.getElementsFromLargeNonQrZpSubgroup();
- identityElements_ = testData.getIdentityElements();
- singleElements_ = testData.getSingleElements();
- shortExponentBitLength_ = testData.getShortExponentBitLength();
- elGamalEncrypter_ = elGamalService_.newEncrypter().init(publicKey_);
- elGamalDecrypter_ = elGamalService_.newDecrypter().init(privateKey_);
- encryptedElements_ = elGamalEncrypter_.encrypt(elements_);
- elGamalEncrypterLargeGroup_ =
- elGamalService_.newEncrypter().init(largePublicKey_);
- elGamalDecrypterLargeGroup_ =
- elGamalService_.newDecrypter().init(largePrivateKey_);
- prngSeed_ = secureRandomService_.nextSeed();
- });
- beforeEach(function() {
- encrypterRandomGenerator_ =
- secureRandom.newService({prngSeed: prngSeed_}).newRandomGenerator();
- decrypterRandomGenerator_ =
- secureRandom.newService({prngSeed: prngSeed_}).newRandomGenerator();
- });
- describe('create an ElGamal cryptography service that should be able to ..', function() {
- it('create a new ElGamal key pair', function() {
- validatePublicKey(keyPair_.publicKey, group_);
- validatePrivateKey(keyPair_.privateKey, group_);
- });
- it('create a new ElGamalEncryptedElements object', function() {
- var encryptedElements = elGamalEncrypter_.encrypt(elements_);
- var gamma = encryptedElements.gamma;
- var phis = encryptedElements.phis;
- var newEncryptedElements =
- elGamalService_.newEncryptedElements(gamma, phis);
- var decryptedElements = elGamalDecrypter_.decrypt(newEncryptedElements);
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('create a new ElGamalEncryptedElements object, with a saved secret',
- function() {
- var encryptedElementsAndSecret =
- elGamalEncrypter_.encrypt(elements_, {saveSecret: true});
- var gamma = encryptedElementsAndSecret.gamma;
- var phis = encryptedElementsAndSecret.phis;
- var secret = encryptedElementsAndSecret.secret;
- var newEncryptedElementsAndSecret =
- elGamalService_.newEncryptedElements(gamma, phis, secret);
- expect((newEncryptedElementsAndSecret.secret.equals(secret)))
- .toBeTruthy();
- var encryptedElements = elGamalService_.newEncryptedElements(
- newEncryptedElementsAndSecret.gamma,
- newEncryptedElementsAndSecret.phis);
- var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('serialize and deserialize an ElGamal public key', function() {
- var publicKeyJson = publicKey_.toJson();
- var publicKeyFromJson = elGamalService_.newPublicKey(publicKeyJson);
- validatePublicKey(publicKeyFromJson, group_);
- });
- it('serialize and deserialize an ElGamal private key', function() {
- var privateKeyJson = privateKey_.toJson();
- var privateKeyFromJson = elGamalService_.newPrivateKey(privateKeyJson);
- validatePrivateKey(privateKeyFromJson, group_);
- });
- it('serialize and deserialize ElGamal encrypted elements', function() {
- var encryptedElements = elGamalEncrypter_.encrypt(elements_);
- var encryptedElementsJson = encryptedElements.toJson();
- var encryptedElementsFromJson =
- elGamalService_.newEncryptedElements(encryptedElementsJson);
- var decryptedElements =
- elGamalDecrypter_.decrypt(encryptedElementsFromJson);
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('serialize and deserialize an encryption pre-computation', function() {
- var preComputation = elGamalEncrypter_.preCompute();
- var preComputationJson = preComputation.toJson();
- var preComputationFromJson =
- elGamalService_.newEncryptedElements(preComputationJson);
- var encryptedElements = elGamalEncrypter_.encrypt(
- elements_, {preComputation: preComputationFromJson});
- var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, elements_);
- });
- describe('create an encrypter/decrypter pair that should be able to ..', function() {
- it('encrypt and decrypt some group elements', function() {
- var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements_);
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('encrypt and decrypt some group elements, using a specified secure random service object',
- function() {
- var elGamalService =
- elGamal.newService({secureRandomService: secureRandomService_});
- var elGamalEncrypter =
- elGamalService.newEncrypter().init(publicKey_);
- var elGamalDecrypter =
- elGamalService.newDecrypter().init(privateKey_);
- var encryptedElements = elGamalEncrypter.encrypt(elements_);
- var decryptedElements = elGamalDecrypter.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('encrypt and decrypt some group elements, using a specified mathematical service object',
- function() {
- var elGamalService =
- elGamal.newService({mathematicalService: mathService_});
- var elGamalEncrypter =
- elGamalService.newEncrypter().init(publicKey_);
- var elGamalDecrypter =
- elGamalService.newDecrypter().init(privateKey_);
- var encryptedElements = elGamalEncrypter.encrypt(elements_);
- var decryptedElements = elGamalDecrypter.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('encrypt and decrypt some group element value strings', function() {
- var encryptedElements = elGamalEncrypter_.encrypt(elementValueStrings_);
- var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('decrypt some group elements, with the check group membership flag set to true',
- function() {
- var decryptedElements = elGamalDecrypter_.decrypt(
- encryptedElements_, {checkMembership: true});
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('decrypt some group elements, with check group membership flag set to false',
- function() {
- var decryptedElements = elGamalDecrypter_.decrypt(
- encryptedElements_, {checkMembership: false});
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('throw an error when decrypting some group elements, the check group membership flag is set to true and the check fails',
- function() {
- expect(function() {
- elGamalDecrypterLargeGroup_.decrypt(
- encryptedElements_, {checkMembership: true});
- }).toThrow();
- });
- it('encrypt and decrypt some group elements, using an encryption pre-computation',
- function() {
- var preComputation = elGamalEncrypter_.preCompute();
- var encryptedElements = elGamalEncrypter_.encrypt(
- elements_, {preComputation: preComputation});
- var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('encrypt and decrypt some group elements and retrieve the secret exponent',
- function() {
- var encryptedElementsAndSecret =
- elGamalEncrypter_.encrypt(elements_, {saveSecret: true});
- checkSecretExponent(
- publicKey_, encryptedElementsAndSecret, elements_);
- var encryptedElements = elGamalService_.newEncryptedElements(
- encryptedElementsAndSecret.gamma,
- encryptedElementsAndSecret.phis);
- var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('encrypt and decrypt some group elements, using an encryption pre-computation, and retrieve the secret exponent after pre-computing',
- function() {
- var preComputationAndSecret =
- elGamalEncrypter_.preCompute({saveSecret: true});
- checkSecretExponent(
- publicKey_, preComputationAndSecret, identityElements_);
- var preComputation = elGamalService_.newEncryptedElements(
- preComputationAndSecret.gamma, preComputationAndSecret.phis);
- var encryptedElements = elGamalEncrypter_.encrypt(
- elements_, {preComputation: preComputation});
- var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('encrypt and decrypt some group elements, using encrypted elements generated with a short exponent',
- function() {
- var encryptedElementsAndSecret = elGamalEncrypterLargeGroup_.encrypt(
- elementsFromLargeGroup_,
- {useShortExponent: true, saveSecret: true});
- expect(encryptedElementsAndSecret.secret.value.bitLength())
- .not.toBeGreaterThan(shortExponentBitLength_);
- var encryptedElements = elGamalService_.newEncryptedElements(
- encryptedElementsAndSecret.gamma,
- encryptedElementsAndSecret.phis);
- var decryptedElements =
- elGamalDecrypterLargeGroup_.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, elementsFromLargeGroup_);
- });
- it('encrypt and decrypt some group elements, using an encryption pre-computation generated with a short exponent',
- function() {
- var preComputationAndSecret = elGamalEncrypterLargeGroup_.preCompute(
- {useShortExponent: true, saveSecret: true});
- expect(preComputationAndSecret.secret.value.bitLength())
- .not.toBeGreaterThan(shortExponentBitLength_);
- var preComputation = elGamalService_.newEncryptedElements(
- preComputationAndSecret.gamma, preComputationAndSecret.phis);
- var encryptedElements = elGamalEncrypterLargeGroup_.encrypt(
- elementsFromLargeGroup_, {preComputation: preComputation});
- var decryptedElements =
- elGamalDecrypterLargeGroup_.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, elementsFromLargeGroup_);
- });
- it('encrypt and decrypt some group elements, when the public key and private key undergo compression',
- function() {
- var encryptedElements = elGamalEncrypter_.encrypt([elements_[0]]);
- var decryptedElements = elGamalDecrypter_.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, [elements_[0]]);
- });
- it('throw an error when encrypting before the encrypter has been initialized with a public key',
- function() {
- var encrypter = elGamalService_.newEncrypter();
- expect(function() {
- encrypter.encrypt(elements_);
- }).toThrow();
- });
- it('throw an error when decrypting before the decrypter has been initialized with a private key',
- function() {
- var decrypter = elGamalService_.newDecrypter();
- expect(function() {
- decrypter.decrypt(encryptedElements_);
- }).toThrow();
- });
- it('throw an error when decrypting before the PRNG decrypter has been initialized with a public key',
- function() {
- var prngDecrypter = elGamalService_.newPrngDecrypter();
- expect(function() {
- prngDecrypter.decrypt(encryptedElements_);
- }).toThrow();
- });
- it('throw an error when attempting to decrypt non-group members and group membership is being checked',
- function() {
- expect(function() {
- var encryptedElements =
- elGamalEncrypterLargeGroup_.encrypt(elementsFromLargeGroup_);
- elGamalDecrypter_.decrypt(
- encryptedElements, {checkMembership: true});
- }).toThrow();
- });
- it('throw an error when attempting to ElGamal encrypt, using a short exponent, for a group that is not of type quadratic residue',
- function() {
- expect(function() {
- elGamalEncrypterLargeGroup_.encrypt(
- elementsFromLargeNonQrGroup_, {useShortExponent: true});
- }).toThrow();
- });
- it('throw an error when attempting to ElGamal encrypt, using a short exponent, for a group whose order is smaller than this exponent',
- function() {
- expect(function() {
- elGamalEncrypter_.encrypt(elements_, {useShortExponent: true});
- }).toThrow();
- });
- });
- describe(
- 'create an encrypter/PRNG decrypter pair that should be able to ..',
- function() {
- it('encrypt and decrypt some group elements', function() {
- var encrypterFromSeed = elGamalService_.newEncrypter().init(
- publicKey_, {secureRandomGenerator: encrypterRandomGenerator_});
- var encryptedElements = encrypterFromSeed.encrypt(elements_);
- var prngDecrypter = elGamalService_.newPrngDecrypter().init(
- publicKey_, decrypterRandomGenerator_);
- var decryptedElements = prngDecrypter.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('encrypt and decrypt some group elements, using an encryption pre-computation',
- function() {
- var encrypterFromSeed = elGamalService_.newEncrypter().init(
- publicKey_,
- {secureRandomGenerator: encrypterRandomGenerator_});
- var preComputation = encrypterFromSeed.preCompute();
- var encryptedElements = encrypterFromSeed.encrypt(
- elements_, {preComputation: preComputation});
- var prngDecrypter = elGamalService_.newPrngDecrypter().init(
- publicKey_, decrypterRandomGenerator_);
- var decryptedElements = prngDecrypter.decrypt(encryptedElements);
- checkDecryptedElements(decryptedElements, elements_);
- });
- it('encrypt some group elements and retrieve the secret exponent that will be used for decryption',
- function() {
- var encrypterFromSeed = elGamalService_.newEncrypter().init(
- publicKey_,
- {secureRandomGenerator: encrypterRandomGenerator_});
- var encryptedElementsAndSecret =
- encrypterFromSeed.encrypt(elements_, {saveSecret: true});
- var secret = encryptedElementsAndSecret.secret;
- var secretFromDecrypter = mathRandomGenerator_.nextExponent(
- group_, {secureRandomGenerator: decrypterRandomGenerator_});
- expect(secretFromDecrypter.equals(secret)).toBeTruthy();
- });
- it('encrypt some group elements, using pre-computation, and retrieve the secret exponent that will be used for decryption',
- function() {
- var encrypterFromSeed = elGamalService_.newEncrypter().init(
- publicKey_,
- {secureRandomGenerator: encrypterRandomGenerator_});
- var preComputationAndSecret =
- encrypterFromSeed.preCompute({saveSecret: true});
- var secret = preComputationAndSecret.secret;
- var secretFromDecrypter = mathRandomGenerator_.nextExponent(
- group_, {secureRandomGenerator: decrypterRandomGenerator_});
- expect(secretFromDecrypter.equals(secret)).toBeTruthy();
- });
- it('encrypt and decrypt some group elements, using encrypted elements generated with a short exponent',
- function() {
- var encrypterFromSeed = elGamalService_.newEncrypter().init(
- largePublicKey_,
- {secureRandomGenerator: encrypterRandomGenerator_});
- var encryptedElementsAndSecret = encrypterFromSeed.encrypt(
- elementsFromLargeGroup_,
- {useShortExponent: true, saveSecret: true});
- expect(encryptedElementsAndSecret.secret.value.bitLength())
- .not.toBeGreaterThan(shortExponentBitLength_);
- var encryptedElements = elGamalService_.newEncryptedElements(
- encryptedElementsAndSecret.gamma,
- encryptedElementsAndSecret.phis);
- var prngDecrypter = elGamalService_.newPrngDecrypter().init(
- largePublicKey_, decrypterRandomGenerator_);
- var decryptedElements = prngDecrypter.decrypt(
- encryptedElements, {useShortExponent: true});
- checkDecryptedElements(
- decryptedElements, elementsFromLargeGroup_);
- });
- it('encrypt and decrypt some group elements, using an encryption pre-computation generated with a short exponent',
- function() {
- var encrypterFromSeed = elGamalService_.newEncrypter().init(
- largePublicKey_,
- {secureRandomGenerator: encrypterRandomGenerator_});
- var preComputationAndSecret = encrypterFromSeed.preCompute(
- {useShortExponent: true, saveSecret: true});
- expect(preComputationAndSecret.secret.value.bitLength())
- .not.toBeGreaterThan(shortExponentBitLength_);
- var preComputation = elGamalService_.newEncryptedElements(
- preComputationAndSecret.gamma, preComputationAndSecret.phis);
- var encryptedElements = encrypterFromSeed.encrypt(
- elementsFromLargeGroup_, {preComputation: preComputation});
- var prngDecrypter = elGamalService_.newPrngDecrypter().init(
- largePublicKey_, decrypterRandomGenerator_);
- var decryptedElements = prngDecrypter.decrypt(
- encryptedElements, {useShortExponent: true});
- checkDecryptedElements(
- decryptedElements, elementsFromLargeGroup_);
- });
- it('encrypt and decrypt an array of single group elements',
- function() {
- var encrypterFromSeed = elGamalService_.newEncrypter().init(
- largePublicKey_,
- {secureRandomGenerator: encrypterRandomGenerator_});
- var encryptedElementArray = [];
- for (var i = 0; i < singleElements_.length; i++) {
- encryptedElementArray.push(
- encrypterFromSeed.encrypt([singleElements_[i]]));
- }
- var prngDecrypter = elGamalService_.newPrngDecrypter().init(
- largePublicKey_, decrypterRandomGenerator_);
- var decryptedElementArray = [];
- for (var j = 0; j < encryptedElementArray.length; j++) {
- decryptedElementArray.push(
- prngDecrypter.decrypt(encryptedElementArray[j])[0]);
- }
- checkDecryptedElements(decryptedElementArray, singleElements_);
- });
- });
- });
- function validatePublicKey(publicKey, group) {
- var elements = publicKey.elements;
- var numElements = elements.length;
- expect(numElements).toBe(publicKeyElements_.length);
- for (var i = 0; i < numElements; i++) {
- expect(elements[i].value.toString())
- .toEqual(publicKeyElements_[i].value.toString());
- }
- expect(publicKey.group.equals(group)).toBeTruthy();
- }
- function validatePrivateKey(privateKey, group) {
- var exponents = privateKey.exponents;
- var numExponents = exponents.length;
- expect(numExponents).toBe(privateKeyExponents_.length);
- for (var i = 0; i < numExponents; i++) {
- expect(exponents[i].value.toString())
- .toEqual(privateKeyExponents_[i].value.toString());
- }
- expect(privateKey.group.equals(group)).toBeTruthy();
- }
- function checkDecryptedElements(decryptedElements, elements) {
- expect(decryptedElements.length).toBe(elements.length);
- for (var i = 0; i < decryptedElements.length; i++) {
- expect(decryptedElements[i].equals(elements[i])).toBeTruthy();
- }
- }
- function checkSecretExponent(
- publicKey, encryptedElementsAndSecret, elements) {
- var publicKeyElements = publicKey.elements;
- var gamma = encryptedElementsAndSecret.gamma;
- var phis = encryptedElementsAndSecret.phis;
- var secret = encryptedElementsAndSecret.secret;
- expect((group_.generator.exponentiate(secret)).equals(gamma)).toBeTruthy();
- for (var i = 0; i < phis.length; i++) {
- expect((publicKeyElements[i].exponentiate(secret).multiply(elements[i]))
- .equals(phis[i]))
- .toBeTruthy();
- }
- }
- });
|