Swisspost 460d0e0780 sVote source code publication | 5 years ago | |
---|---|---|
.. | ||
lib | 5 years ago | |
spec | 5 years ago | |
.jshintrc | 5 years ago | |
.npmrc | 5 years ago | |
LICENSE | 5 years ago | |
README.md | 5 years ago | |
karma.conf.js | 5 years ago | |
package-lock.json | 5 years ago | |
package.json | 5 years ago | |
pom.xml | 5 years ago |
This module defines the ElGamal cryptography API for Scytl's JavaScript cryptographic library. The starting point for using this module is the instantiation of an ElGamal cryptography service.
Start by git cloning the project comm/scytl-cryptolib
from Stash
.
The source code of this module will be found in the directory scytl-cryptolib/cryptolib-js-elgamal/lib
.
To build this module, change to the directory scytl-cryptolib/cryptolib-js-elgamal
and do the following:
npm install
--or--
mvn clean install -DskipTests
The unit tests of this module will be found in the directory scytl-cryptolib/cryptolib-js-elgamal/spec
. To run the tests, change to the directory scytl-cryptolib/cryptolib-js-elgamal
and do the following:
karma test
--or--
mvn test
To only run a chosen test suite, add an f
in front of the corresponding describe
statement. For example:
fdescribe('create an ElGamal cryptography service that should be able to ..', function()
...
To only run a chosen test, add an f
in front of the corresponding it
statement. For example:
fit('create a new ElGamal key pair', function() {
...
Note: To build and test the entire scytl-cryptolib
project, change to the directory scytl-cryptolib
and do the following:
mvn clean install
To generate the JSDoc for this module, change to the directory scytl-cryptolib/cryptolib-js-elgamal
, build the module (if not already done) and do the following:
node_modules/.bin/jsdoc lib
This will generate a file called out
. Double click on the file out/index.html
to view the JSDoc.
npm
install this moduleTo npm
install this module in standalone mode, do the following:
npm install --registry https://nexus.scytl.net/content/groups/public-npm/ scytl-elgamal
To npm
install this module as a dependency of your own module, do the following:
Add the following lines to the dependencies
section of your module's package.json
file.
"scytl-bitwise": "^2.1.0",
"scytl-codec": "^2.1.0",
"scytl-securerandom": "^2.1.0",
"scytl-mathematical": "^2.1.0",
"scytl-elgamal": "^2.1.0",
Make sure that the .npmrc
file of your module contains the following line.
registry=https://nexus.scytl.net/content/groups/public-npm/
Install all dependencies.
npm install
The following example shows the default way to create a new instance of an ElGamal cryptography service.
var elGamal = require('scytl-elgamal');
var elGamalService = elGamal.newService();
The following example shows how to supply one's own secure random service, initialized with a chosen seed, to the ElGamal cryptography service.
var elGamal = require('scytl-elgamal');
var secureRandom = require('scytl-securerandom');
var mySecureRandomService = secureRandom.newService({prngSeed: mySeed});
var elGamalService = elGamal.newService({secureRandomService: mySecureRandomService});
The following examples illustrate how to create new ElGamalKeyPublicKey
, ElGamalPrivateKey
and ElGamalKeyPair
objects, from a Zp subgroup, a set of public key elements and a set of private key exponents.
var elGamal = require('scytl-elgamal');
var mathematical = require('scytl-mathematical');
var forge = require('node-forge');
var BigInteger = BigInteger;
var elGamalService = elGamal.newService();
var mathService = mathematical.newService();
var p = new BigInteger('23');
var q = new BigInteger('11');
var g = new BigInteger('2');
var group = mathService.newZpSubgroup(p, q, g);
var keyElementValues = [new BigInteger('2'), new BigInteger('4')];
var keyExponentValues = [new BigInteger('1'), new BigInteger('2')];
var keyElements = [];
var keyExponents = [];
for (int i = 0; i < keyElementValues.length; i++) {
keyElements.push(mathService.newZpGubroupElement(p, q, keyElementValues[i]));
keyExponents.push(mathService.newExponent(q, keyExponentValues[i]));
}
var publicKey = elGamalService.newPublicKey(group, keyElements);
var privateKey = elGamalService.newPrivateKey(group, keyExponents);
var keyPair = elGamalService.newKeyPair(publicKey, privateKey);
The following example shows how to encrypt an array of Zp group elements, with a given ElGamal public key. The output of the encryption will be an ElGamalEncryptedElements
object, which encapsulates the gamma
Zp group element and the array of phi
Zp group elements that comprise the encryption.
var encrypter = elGamalService.newEncrypter().init(publicKey);
var p = new BigInteger('23');
var q = new BigInteger('11');
var elements = [new ZpGroupElement(p, q, '3'), new ZpGroupElement(p, q, '6'), ...];
var encryptedElements = encrypter.encrypt(elements);
The following example shows how to encrypt an array of Zp group elements that are received in JSON
serialized form.
var elementJsons = [element1Json, element2Json, ...];
var elements = [];
for (var i = 0; i < elementJsons.length; i++) {
var elements.push(new ZpGroupElement(elementJsons[i]));
}
var encryptedElements = encrypter.encrypt(elements);
The following example shows how to encrypt an array of Zp group elements that are received as an array of their values, in string format.
var elementValueStrings = ['3', '6', ...];
var encryptedElements = encrypter.encrypt(elementValueStrings);
Note In all of the examples shown above, it is required that the values of the Zp group elements being encrypted belong to the same Zp subgroup as that of the ElGamal public key used to initialize the encrypter.
The following example shows how to encrypt by performing a pre-computation step. The pre-computation step does not require knowledge of the Zp group elements to be encrypted. Therefore, it can be handled by a web worker during a voting session. Since the operations performed during the pre-computation step are computationally dominant in the encryption process, passing this step to a worker can significantly improve performance. The output of the pre-computation step will also be an ElGamalEncryptedElements
object, but with no information pertaining to the Zp group elements to be encrypted. It is equivalent to ElGamal encrypting an array of identity
Zp group elements.
var encrypter = elGamalService.newEncrypter().init(publicKey);
var preComputation = encrypter.preCompute();
...
var encryptedElements = encrypter.encrypt(elements, {preComputation: preComputation});
The following example shows how to encrypt an array of Zp group elements, retrieve the secret exponent that was generated by and used during the encryption process and create a new ciphertext object from the public components of the encryption. An example of usage is the case where one needs to generate a zero knowledge proof of knowledge of the secret exponent generated by the encryption. The secret exponent can be retrieved as a property of the ElGamalEncryptedElements
object returned by the encrypt
method. Normally, this property is undefined
, in order to protect the ElGamal encrypted elements from being decrypted by any other means than the private key.
var encrypter = elGamalService.newEncrypter().init(publicKey);
var encryptedElementsAndSecret = encrypter.encrypt(elements, {saveSecret: true});
var secret = encryptedElementsAndSecret.secret;
var encryptedElements = elGamalService.newEncryptedElements(encryptedElementsAndSecret.gamma, encryptedElementsAndSecret.phis);
Note: If the secret exponent is not needed, it is highly recommended to use the method encrypt
without the saveSecret
flag set to true, so that private information is not unnecessarily exposed.
The following example shows how to encrypt an array of Zp group elements and retrieve the secret exponent when the pre-computation step needs to be performed. Note: The secret exponent is always generated by the pre-computation step, whether internally or externally, with regards to the encryption process.
var encrypter = elGamalService.newEncrypter().init(publicKey);
...
var preComputationAndSecret = encrypter.preCompute({saveSecret: true});
var secret = preComputationAndSecret.secret;
var preComputation = elGamalService.newEncryptedElements(preComputationAndSecret.gamma, preComputationAndSecret.phis)
...
var encryptedElements = encrypter.encrypt(elements, {preComputation: preComputation});
Note: As mentioned in the previous example, if the secret exponent is not needed, it is highly recommended to not set the saveSecret
flag to true.
The following example shows how to encrypt an array of Zp group elements, using a so-called short secret exponent
. Note: This method is only applicable for quadratic residue
Zp subgroups, which are defined such that the modulus p
and order q
have the relationship: p = 2q + 1
. If a pre-computation is provided as an option the short exponent option will be ignored since the secret exponent will already been generated by the pre-computation process.
var encrypter = elGamalService.newEncrypter().init(publicKey);
var encryptedElements = encrypter.encrypt(elements, {useShortExponent: true});
The following example shows how to encrypt an array of Zp group elements, using a short secret exponent
and performing a pre-computation step.
var encrypter = elGamalService.newEncrypter().init(publicKey);
var preComputation = encrypter.preCompute({useShortExponent: true});
...
var encryptedElements = encrypter.encrypt(elements, {preComputation: preComputation});
The following examples illustrate how to decrypt an encrypted array of Zp group elements, with a given ElGamal private key. If the confirmMembership
parameter is provided and set to true, then a Zp subgroup membership check will performed on the encryption elements provided as input before starting the decryption process. Note: By default, this membership check is not performed because it is a computationally costly operation.
var decrypter = elGamalService.newDecrypter().init(privateKey);
var elements = decrypter.decrypt(encryptedElements);
var elements = decrypter.decrypt(encryptedElements, {confirmMembership: true});
The following example shows how to encrypt an array of Zp group elements, by using a source of randomness created from a randomly generated PRNG seed and then decrypt the encrypted elements by using the public key and the same randomly generated seed used to encrypt. Note This shows a way in which ElGamal encrypted elements can be decrypted without the use of a private key.
var seed = secureRandomService.nextSeed();
var randomGenerator = new SecureRandomService({prngSeed: seed}).newRandomGenerator();
var encrypter = elGamalService.newEncrypter().init(publicKey, {secureRandomGenerator: randomGenerator});
var encryptedElements = encrypter.encrypt(elements);
...
var randomGenerator = new SecureRandomService({prngSeed: seed}).newRandomGenerator();
var prngDecrypter = elGamalService.newPrngDecrypter().init(publicKey, randomGenerator);
var decryptedElements = prngDecrypter.decrypt(encryptedElements);
The following example shows how to individually encrypt each member of an array of Zp group elements, using a randomly generated PRNG seed and then individually decrypt each encrypted element using the same randomly generated seed. Note In this example, the ElGamal public key is still allowed to have more than one element because it will be internally compressed to a single element before encrypting or decrypting.
var seed = secureRandomService.nextSeed();
var encrypterRandomGenerator = new SecureRandomService({prngSeed: seed}).newRandomGenerator();
var encrypter = elGamalService.newEncrypter().init(publicKey, {secureRandomGenerator: encrypterRandomGenerator});
var encryptedElementArray[];
for (var i = 0; i < elements.length; i++) {
encryptedElementArray.push(encrypter.encrypt([elements[i]]));
}
...
var decrypterRandomGenerator = new SecureRandomService({prngSeed: seed}).newRandomGenerator();
var prngDecrypter = elGamalService.newPrngDecrypter().init(publicKey, decrypterRandomGenerator);
var decryptedElementArray = [];
for (var j = 0; j < encryptedElementArray.length; i++) {
var decryptedElementArray.push(prngDecrypter.decrypt(encryptedElementArray[j])[0]);
}
The following example shows how to encrypt and decrypt an array of Zp group elements, using a randomly generated PRNG seed and a short
secret exponent. Note In this case, the useShortExponent
flag must also be set to true for the decryption because the short
secret exponent will need to be regenerated during this process.
var seed = secureRandomService.nextSeed();
var randomGenerator = new SecureRandomService({prngSeed: seed}).newRandomGenerator();
var encrypter = elGamalService.newEncrypter().init(publicKey, {secureRandomGenerator: randomGenerator});
var encryptedElements = encrypter.encrypt(elements, {useShortExponent: true});
...
var randomGenerator = new SecureRandomService({prngSeed: seed}).newRandomGenerator();
var prngDecrypter = elGamalService.newPrngDecrypter().init(publicKey, randomGenerator);
var decryptedElements = prngDecrypter.decrypt(encryptedElements, {useShortExponent: true});
In some cases, it might be necessary to construct an ElGamal encrypted elements object manually rather than obtaining it from an encryption or pre-computation. The following example shows how to create a new ElGamalEncryptedElements
object, using the components of an encryption operation.
var encryptedElements = encrypter.encrypt(elements);
var newEncryptedElements = elGamalService.newEncryptedElements(encryptedElements.gamma, encryptedElements.phis);
The following examples illustrate how to serialize and deserialize ElGamalPublicKey
, ElGamalPrivateKey
and ElGamalEncryptedElements
objects to and from their corresponding JSON representations.
var publicKeyJson = publicKey.toJson(publicKey);
var publicKey = elGamalService.newPublicKey(publicKeyJson);
var privateKeyJson = privateKey.toJson(privateKey);
var privateKey = elGamalService.newPrivateKey(privateKeyJson);
var encryptedElementsJson = encryptedElements.toJson(encryptedElements);
var encryptedElements = elGamalService.newEncryptedElements(encryptedElementsJson);
In general, the input data validation performed by the ElGamal service does not include checking that the values of a Zp group element array or the element values of an ElGamal encryption provided as input are members of the Zp subgroup provided as input (either directly or via an ElGamal key). The reason for this, is that the group membership check involves the modPow
mathematical operation, which is computationally costly and is linearly proportional to the number of values to check. An exception to this rule is the ElGamal decryption operation, in which case the group membership check is provided as an option. The mathematical service provides for the creation of a MathematicalGroupHandler
object which defines a method called checkGroupMembership
that can, for example, be used to check an array of Zp group elements against the Zp subgroup of an ElGamal public key before providing this data as input to an encryption method of the ElGamal service.