/* * 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); }); }); }); });