123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- /*
- * 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 mathematical = require('../lib/index');
- var CommonTestData = require('./data/common-data');
- var secureRandom = require('scytl-securerandom');
- var forge = require('node-forge');
- var BigInteger = forge.jsbn.BigInteger;
- describe('The mathematical module should be able to ...', function() {
- var TEST_SEED = new Uint8Array([18, 58, 188]);
- var mathService_;
- var mathArrayCompressor_;
- var mathRandomGenerator_;
- var mathGroupHandler_;
- var g_;
- var p_;
- var q_;
- var group_;
- var anotherGroup_;
- var qrGroup_;
- var elementValue_;
- var element_;
- var exponentValue_;
- var exponent_;
- var element1Value_;
- var element2Value_;
- var element3Value_;
- var elements_;
- var exponents_;
- var qrGroupLarge_;
- var exponent1Large_;
- var exponent2Large_;
- var multiGroupElementValueArray_;
- var multiGroupElementValues_;
- var multiGroupElements_;
- var minCertaintyLevel_;
- beforeAll(function() {
- mathService_ = mathematical.newService();
- mathArrayCompressor_ = mathService_.newArrayCompressor();
- mathRandomGenerator_ = mathService_.newRandomGenerator();
- mathGroupHandler_ = mathService_.newGroupHandler();
- var testData = new CommonTestData();
- p_ = testData.getP();
- q_ = testData.getQ();
- g_ = testData.getG();
- group_ = testData.getGroup();
- anotherGroup_ = testData.getAnotherGroup();
- qrGroup_ = testData.getQuadraticResidueGroup();
- elementValue_ = testData.getElementValue();
- element_ = testData.getElement();
- exponentValue_ = testData.getExponentValue();
- exponent_ = testData.getExponent();
- element1Value_ = testData.getElement1Value();
- element2Value_ = testData.getElement2Value();
- element3Value_ = testData.getElement3Value();
- elements_ = testData.getElements();
- exponents_ = testData.getExponents();
- qrGroupLarge_ = testData.getLargeQuadraticResidueGroup();
- exponent1Large_ = testData.getExponent1Large();
- exponent2Large_ = testData.getExponent2Large();
- multiGroupElementValueArray_ = testData.getMultiGroupElementValueArray();
- multiGroupElementValues_ = testData.multiGroupElementValues();
- multiGroupElements_ = testData.getMultiGroupElements();
- minCertaintyLevel_ = testData.getMinimumCertaintyLevel();
- });
- describe('create a mathematical service that should be able to ..', function() {
- it('be created from a secure random service object provided as input',
- function() {
- var mathService = mathematical.newService(
- {secureRandomService: secureRandom.newService()});
- var mathRandomGenerator = mathService.newRandomGenerator();
- var randomExponent = mathRandomGenerator.nextExponent(qrGroup_);
- expect(randomExponent).toBeDefined();
- });
- describe(
- 'create a new ZpSubgroup object that should be able to ..', function() {
- it('be defined', function() {
- expect(group_).toBeDefined();
- expect(group_.identity).toBeDefined();
- });
- it('check group membership', function() {
- var element =
- mathService_.newZpGroupElement(p_, q_, BigInteger.ONE);
- expect(group_.isGroupMember(element)).toBeTruthy();
- // Check membership of element with wrong q.
- var q2 = new BigInteger('19');
- var p2 = new BigInteger('37');
- var group2 = mathService_.newZpSubgroup(p2, q2, g_);
- var element2 = mathService_.newZpGroupElement(
- group2.p, group2.q, BigInteger.ONE);
- expect(!group_.isGroupMember(element2)).toBeTruthy();
- // Check membership of non-member element.
- element =
- mathService_.newZpGroupElement(p_, q_, new BigInteger('5'));
- expect(!group_.isGroupMember(element)).toBeTruthy();
- });
- it('be compared with another ZpSubgroup object', function() {
- var group2 = mathService_.newZpSubgroup(p_, q_, g_);
- var q2 = new BigInteger('19');
- var p2 = new BigInteger('37');
- var group3 = mathService_.newZpSubgroup(p2, q2, g_);
- expect(group_.equals(group2)).toBeTruthy();
- expect(!group_.equals(group3)).toBeTruthy();
- });
- it('be serialized and deserialized', function() {
- var groupJson = group_.toJson();
- var groupFromJson = mathService_.newZpSubgroup(groupJson);
- expect(groupFromJson).toBeDefined();
- expect(groupFromJson.equals(group_)).toBeTruthy();
- });
- });
- describe(
- 'create a new ZpGroupElement object that should be able to ..',
- function() {
- it('be defined', function() {
- expect(element_).toBeDefined();
- expect(element_.value.equals(elementValue_)).toBeTruthy();
- });
- it('be multiplied with another ZpGroupElement object', function() {
- var value = new BigInteger('2');
- var element2 = mathService_.newZpGroupElement(p_, q_, value);
- var groupResult = element_.multiply(element2);
- expect(groupResult).toBeDefined();
- expect(groupResult.value.equals(new BigInteger('6'))).toBeTruthy();
- });
- it('be exponentiated with an Exponent object', function() {
- var value = new BigInteger('2');
- var exponent = mathService_.newExponent(q_, value);
- var groupResult = element_.exponentiate(exponent);
- expect(groupResult).toBeDefined();
- expect(groupResult.value.equals(new BigInteger('9'))).toBeTruthy();
- });
- it('be inverted', function() {
- var groupResult = element_.invert();
- expect(groupResult).toBeDefined();
- expect(groupResult.value.equals(new BigInteger('8'))).toBeTruthy();
- });
- it('be serialized and deserialized', function() {
- var elementJson = element_.toJson();
- var elementFromJson = mathService_.newZpGroupElement(elementJson);
- expect(elementFromJson).toBeDefined();
- expect(elementFromJson.equals(element_)).toBeTruthy();
- });
- });
- describe(
- 'create a new Exponent object that should be able to ..', function() {
- it('be defined', function() {
- expect(exponent_).toBeDefined();
- expect(exponent_.value.equals(exponentValue_)).toBeTruthy();
- });
- it('be added to another Exponent object', function() {
- var value = new BigInteger('3');
- var exponent2 = mathService_.newExponent(q_, value);
- var exponentResult = exponent_.add(exponent2);
- expect(exponentResult).toBeDefined();
- expect(exponentResult.value.equals(new BigInteger('5')))
- .toBeTruthy();
- });
- it('subtract another Exponent object from itself', function() {
- var value = new BigInteger('5');
- var exponent2 = mathService_.newExponent(q_, value);
- var exponentResult = exponent2.subtract(exponent_);
- expect(exponentResult).toBeDefined();
- expect(exponentResult.value.equals(new BigInteger('3')))
- .toBeTruthy();
- });
- it('be multiplied with another Exponent object', function() {
- var value = new BigInteger('3');
- var exponent2 = mathService_.newExponent(q_, value);
- var exponentResult = exponent_.multiply(exponent2);
- expect(exponentResult).toBeDefined();
- expect(exponentResult.value.toString())
- .toEqual('' + exponent_.value * exponent2.value % q_);
- });
- it('be multiplied with Exponent objects encapsulating large values',
- function() {
- var exponentResult = exponent1Large_.multiply(exponent2Large_);
- expect(exponentResult).toBeDefined();
- });
- it('be negated', function() {
- var exponentResult = exponent_.negate();
- expect(exponentResult).toBeDefined();
- expect(exponentResult.value.equals(new BigInteger('9')))
- .toBeTruthy();
- });
- it('be serialized and deserialized', function() {
- var exponentJson = exponent_.toJson();
- var exponentFromJson = mathService_.newExponent(exponentJson);
- expect(exponentFromJson).toBeDefined();
- expect(exponentFromJson.equals(exponent_)).toBeTruthy();
- });
- });
- it('create a new quadratic residue Zp subgroup', function() {
- var group = mathService_.newQuadraticResidueGroup(p_, g_);
- expect(group).toBeDefined();
- });
- describe(
- 'create a new MathematicalArrayCompressor object that should be able to ..',
- function() {
- it('compress Zp group elements', function() {
- var compressedElement =
- mathArrayCompressor_.compressZpGroupElements(elements_);
- expect(compressedElement).toBeDefined();
- expect(compressedElement.value.equals(new BigInteger('8')))
- .toBeTruthy();
- compressedElement = mathArrayCompressor_.compressZpGroupElements(
- multiGroupElements_, group_);
- expect(compressedElement).toBeDefined();
- expect(compressedElement.value.equals(new BigInteger('6')))
- .toBeTruthy();
- });
- it('compress a desired number of trailing Zp group elements',
- function() {
- var requiredLength = 4;
- var compressedArray =
- mathArrayCompressor_.compressTrailingZpGroupElements(
- multiGroupElements_, requiredLength);
- expect(compressedArray.length).toBe(requiredLength);
- var expectedCompressedArray = [];
- var expectedCompressedArrayValues = [3, 4, 5, 7];
- for (var i = 0; i < expectedCompressedArrayValues.length; i++) {
- var value =
- new BigInteger('' + expectedCompressedArrayValues[i]);
- var element = mathService_.newZpGroupElement(p_, q_, value);
- expectedCompressedArray.push(element);
- }
- var equals = true;
- for (var j = 0; j < compressedArray.length; j++) {
- if (!compressedArray[j].equals(expectedCompressedArray[j])) {
- equals = false;
- }
- }
- expect(equals).toBeTruthy();
- });
- it('compress exponents', function() {
- var compressedExponent =
- mathArrayCompressor_.compressExponents(exponents_);
- expect(compressedExponent).toBeDefined();
- expect(compressedExponent.value.equals(new BigInteger('7')))
- .toBeTruthy();
- });
- it('compress a desired number of trailing exponents', function() {
- var requiredLength = 2;
- var compressedArray =
- mathArrayCompressor_.compressTrailingExponents(
- exponents_, requiredLength);
- expect(compressedArray.length).toBe(requiredLength);
- var value;
- var exponent;
- var expectedCompressedArrayValues = [2, 5];
- var expectedCompressedArray = [];
- for (var i = 0; i < expectedCompressedArrayValues.length; i++) {
- value = new BigInteger('' + expectedCompressedArrayValues[i]);
- exponent = mathService_.newExponent(q_, value);
- expectedCompressedArray.push(exponent);
- }
- var equals = true;
- for (var j = 0; j < compressedArray.length; j++) {
- if (!compressedArray[j].equals(expectedCompressedArray[j])) {
- equals = false;
- }
- }
- expect(equals).toBeTruthy();
- });
- });
- describe(
- 'create a new MathematicalRandomGenerator object that should be able to ..',
- function() {
- it('generate a random Exponent', function() {
- var randomExponent = mathRandomGenerator_.nextExponent(qrGroup_);
- expect(randomExponent).toBeDefined();
- });
- it('generate a short random Exponent', function() {
- var randomExponent = mathRandomGenerator_.nextExponent(
- qrGroupLarge_, {useShortExponent: true});
- expect(randomExponent).toBeDefined();
- expect(function() {
- mathRandomGenerator_.nextExponent(
- qrGroup_, {useShortExponent: true});
- }).toThrow();
- });
- it('generate a random Exponent, using chosen source of randomness',
- function() {
- var secureRandomService =
- secureRandom.newService({prngSeed: TEST_SEED});
- var randomGenerator = secureRandomService.newRandomGenerator();
- var randomExponent = mathRandomGenerator_.nextExponent(
- qrGroup_, {secureRandomGenerator: randomGenerator});
- expect(randomExponent.value.toString()).toBe('9');
- });
- it('generate a random Zp group element', function() {
- for (var i = 0; i < 100; i++) {
- var element = mathRandomGenerator_.nextZpGroupElement(qrGroup_);
- expect(element).toBeDefined();
- expect(qrGroup_.isGroupMember(element)).toBeTruthy();
- }
- });
- it('generate a random Zp group element, using a short exponent',
- function() {
- var element = mathRandomGenerator_.nextZpGroupElement(
- qrGroupLarge_, {useShortExponent: true});
- expect(element).toBeDefined();
- expect(qrGroupLarge_.isGroupMember(element)).toBeTruthy();
- });
- it('generate a random Zp group element, using a chosen source of randomness',
- function() {
- var secureRandomService =
- secureRandom.newService({prngSeed: TEST_SEED});
- var randomGenerator = secureRandomService.newRandomGenerator();
- var randomElement = mathRandomGenerator_.nextZpGroupElement(
- qrGroup_, {secureRandomGenerator: randomGenerator});
- expect(randomElement.value.toString()).toBe('6');
- });
- it('generate a random quadratic residue Zp subgroup', function() {
- var bitLengthOfP = 16;
- var certainty = minCertaintyLevel_;
- var group = mathRandomGenerator_.nextQuadraticResidueGroup(
- bitLengthOfP, certainty);
- expect(group).toBeDefined();
- });
- it('generate a random quadratic residue Zp subgroup, using a chosen source of randomness',
- function() {
- var bitLengthOfP = 16;
- var certainty = minCertaintyLevel_;
- var secureRandomService =
- secureRandom.newService({prngSeed: TEST_SEED});
- var randomGenerator = secureRandomService.newRandomGenerator();
- var group = mathRandomGenerator_.nextQuadraticResidueGroup(
- bitLengthOfP, certainty,
- {secureRandomGenerator: randomGenerator});
- expect(group.p.toString()).toBe('41843');
- expect(group.q.toString()).toBe('20921');
- expect(group.generator.value.toString()).toBe('3');
- });
- });
- describe(
- 'create a new MathematicalGroupHandler object that should be able to ..',
- function() {
- it('exponentiate an array of Zp group elements', function() {
- var exponentiatedElements = mathGroupHandler_.exponentiateElements(
- group_, elements_, exponent_);
- expect(exponentiatedElements).toBeDefined();
- expect(exponentiatedElements.length).toEqual(elements_.length);
- expect(exponentiatedElements[0].value.toString())
- .toEqual('' + (Math.pow(element1Value_, exponentValue_) % p_));
- expect(exponentiatedElements[1].value.toString())
- .toEqual('' + (Math.pow(element2Value_, exponentValue_) % p_));
- expect(exponentiatedElements[2].value.toString())
- .toEqual('' + (Math.pow(element3Value_, exponentValue_) % p_));
- expect(exponentiatedElements[0].p).toEqual(p_);
- expect(exponentiatedElements[1].p).toEqual(p_);
- expect(exponentiatedElements[2].p).toEqual(p_);
- expect(exponentiatedElements[0].q).toEqual(q_);
- expect(exponentiatedElements[1].q).toEqual(q_);
- expect(exponentiatedElements[2].q).toEqual(q_);
- });
- it('honor the validation flag when exponentiating an array of Zp group elements',
- function() {
- // Confirm that no error is thrown when exponentiating some
- // elements that do not belong to group but validation flag is
- // not set.
- mathGroupHandler_.exponentiateElements(
- group_, multiGroupElements_, exponent_);
- // Confirm that no error is thrown when exponentiating some
- // elements that do not belong to group but validation flag is
- // set to false.
- mathGroupHandler_.exponentiateElements(
- group_, multiGroupElements_, exponent_, false);
- // Confirm that error is thrown when exponentiating some elements
- // that do not belong to group and validation flag is set to
- // true.
- expect(function() {
- mathGroupHandler_.exponentiateElements(
- group_, multiGroupElements_, exponent_, true);
- }).toThrow();
- });
- it('divide two Zp group element arrays', function() {
- var value;
- var element;
- var elements = [];
- value = new BigInteger('1');
- element = mathService_.newZpGroupElement(p_, q_, value);
- elements.push(element);
- value = new BigInteger('2');
- element = mathService_.newZpGroupElement(p_, q_, value);
- elements.push(element);
- value = new BigInteger('16');
- element = mathService_.newZpGroupElement(p_, q_, value);
- elements.push(element);
- var dividedElements =
- mathGroupHandler_.divideElements(elements_, elements, group_);
- expect(dividedElements).toBeDefined();
- });
- it('check the Zp subgroup membership of an array of Zp group elements, for which all elements are members',
- function() {
- mathGroupHandler_.checkGroupMembership(group_, elements_);
- });
- it('check the Zp subgroup membership of an array of Zp group elements, for which not all elements are members',
- function() {
- expect(function() {
- mathGroupHandler_.checkGroupMembership(
- anotherGroup_, elements_);
- }).toThrow();
- });
- it('extract a specified number of Zp subgroup members from an array of Zp group element values',
- function() {
- var numMembersRequired = multiGroupElementValueArray_.length - 2;
- var extractedElements = mathGroupHandler_.extractGroupMembers(
- group_, multiGroupElementValues_, numMembersRequired);
- expect(extractedElements).toBeDefined();
- expect(extractedElements.length).toBe(numMembersRequired);
- });
- });
- });
- });
|