# ElGamal Cryptography Module 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. ## How to view, build and test the source code of this module 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: ```java 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: ```javascript karma test --or-- mvn test ``` To only run a chosen test suite, add an `f` in front of the corresponding `describe` statement. For example: ```javascript 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: ```javascript 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: ```javascript mvn clean install ``` ## How to generate the JSDoc for this module 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: ```javascript node_modules/.bin/jsdoc lib ``` This will generate a file called `out`. Double click on the file `out/index.html` to view the JSDoc. ## How to `npm` install this module To `npm` install this module in standalone mode, do the following: ```javascript 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: 1. Add the following lines to the `dependencies` section of your module's `package.json` file. ```javascript "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", ``` 2. Make sure that the `.npmrc` file of your module contains the following line. ```javascript registry=https://nexus.scytl.net/content/groups/public-npm/ ``` 3. Install all dependencies. ```javascript npm install ``` ## How to instantiate an ElGamal cryptography service The following example shows the default way to create a new instance of an ElGamal cryptography service. ```javascript 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. ```javascript var elGamal = require('scytl-elgamal'); var secureRandom = require('scytl-securerandom'); var mySecureRandomService = secureRandom.newService({prngSeed: mySeed}); var elGamalService = elGamal.newService({secureRandomService: mySecureRandomService}); ``` ## How to instantiate ElGamal key objects 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. ```javascript 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); ``` ## How to encrypt 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. ```javascript 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. ```javascript 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. ```javascript 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. ## How to encrypt, using a pre-computation step 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. ```javascript var encrypter = elGamalService.newEncrypter().init(publicKey); var preComputation = encrypter.preCompute(); ... var encryptedElements = encrypter.encrypt(elements, {preComputation: preComputation}); ``` ## How to encrypt and save the secret exponent 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. ```javascript 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. ## How to encrypt and save the secret exponent, using a pre-computation step. 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. ```javascript 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*. ## How to encrypt, using a short secret exponent 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. ```javascript 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. ```javascript var encrypter = elGamalService.newEncrypter().init(publicKey); var preComputation = encrypter.preCompute({useShortExponent: true}); ... var encryptedElements = encrypter.encrypt(elements, {preComputation: preComputation}); ``` ## How to decrypt 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. ```javascript var decrypter = elGamalService.newDecrypter().init(privateKey); var elements = decrypter.decrypt(encryptedElements); var elements = decrypter.decrypt(encryptedElements, {confirmMembership: true}); ``` ## How to encrypt and decrypt, using a randomly generated PRNG seed. 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. ```javascript 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. ```javascript 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. ```javascript 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}); ``` ## How to instantiate an ElGamal encrypted elements object. 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. ```javascript var encryptedElements = encrypter.encrypt(elements); var newEncryptedElements = elGamalService.newEncryptedElements(encryptedElements.gamma, encryptedElements.phis); ``` ## How to serialize and deserialize ElGamal cryptography objects The following examples illustrate how to serialize and deserialize `ElGamalPublicKey`, `ElGamalPrivateKey` and `ElGamalEncryptedElements` objects to and from their corresponding JSON representations. ```javascript 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); ``` ## Note regarding input data validation and Zp subgroup membership checks 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.