/* * 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 ElGamalKeyPair = require('./key-pair'); var ElGamalPublicKey = require('./public-key'); var ElGamalPrivateKey = require('./private-key'); var ElGamalEncrypter = require('./encrypter'); var ElGamalDecrypter = require('./decrypter'); var ElGamalPrngDecrypter = require('./prng-decrypter'); var ElGamalEncryptedElements = require('./encrypted-elements'); var mathematical = require('scytl-mathematical'); var validator = require('./input-validator'); var codec = require('scytl-codec'); module.exports = ElGamalCryptographyService; /** * @class ElGamalCryptographyService * @classdesc The ElGamal cryptography service API. To instantiate this object, * use the method {@link newService}. * @hideconstructor * @param {Object} * [options] An object containing optional arguments. * @param {SecureRandomService} * [options.secureRandomService=Created internally] The secure random * service to use. * @param {MathematicalService} * [options.mathematicalService=Created internally] The mathematical * service to use. */ function ElGamalCryptographyService(options) { options = options || {}; var secureRandomService; if (options.secureRandomService) { secureRandomService = options.secureRandomService; } var mathService_; if (options.mathematicalService) { mathService_ = options.mathematicalService; } else if (secureRandomService) { mathService_ = mathematical.newService({secureRandomService: secureRandomService}); } else { mathService_ = mathematical.newService(); } /** * Creates a new ElGamalPublicKey object, which encapsulates an ElGamal * public key. * * @function newPublicKey * @memberof ElGamalCryptographyService * @param {ZpSubgroup|string} * groupOrJson The Zp subgroup to which the elements of the * public key belong OR a JSON string representation of * an ElGamalPublicKey object, compatible with its * toJson method. For the latter case, any * additional input arguments will be ignored. * @param {ZpGroupElement[]} * elements The Zp group elements that comprise the public key. * @returns {ElGamalPublicKey} The ElGamalPublicKey object. * @throws {Error} * If the input data validation fails. */ this.newPublicKey = function(groupOrJson, elements) { if (typeof groupOrJson !== 'string') { validator.checkIsObjectWithProperties( groupOrJson, 'Zp subgroup for new ElGamalPublicKey object'); validator.checkZpGroupElements( elements, 'Zp group elements for new ElGamalPublicKey object', groupOrJson); return new ElGamalPublicKey(groupOrJson, elements); } else { return jsonToPublicKey(groupOrJson); } }; /** * Creates a new ElGamalPrivateKey object, which encapsulates an ElGamal * private key. * * @function newPrivateKey * @memberof ElGamalCryptographyService * @param {ZpSubgroup|string} * groupOrJson The Zp subgroup to which the exponents of the * private key are associated OR a JSON string * representation of an ElGamalPrivateKey object, compatible with * its toJson method. For the latter case, any * additional input arguments will be ignored. * @param {Exponent[]} * exponents The exponents that comprise the private key. * @returns {ElGamalPrivateKey} The new ElGamalPrivateKey object. * @throws {Error} * If the input data validation fails. */ this.newPrivateKey = function(groupOrJson, exponents) { if (typeof groupOrJson !== 'string') { validator.checkIsObjectWithProperties( groupOrJson, 'Zp subgroup for new ElGamalPrivateKey object'); validator.checkExponents( exponents, 'Exponents for new ElGamalPrivateKey object', groupOrJson.q); return new ElGamalPrivateKey(groupOrJson, exponents); } else { return jsonToPrivateKey(groupOrJson); } }; /** * Creates a new ElGamalKeyPair object, which encapsulates an ElGamal key * pair. * * @function newKeyPair * @memberof ElGamalCryptographyService * @param {ElGamalPublicKey} * publicKey The ElGamal public key comprising the key pair. * @param {ElGamalPrivateKey} * privateKey The ElGamal private key comprising the key pair. * @returns {ElGamalKeyPair} The new ElGamalKeyPair object. * @throws {Error} * If the input data validation fails. */ this.newKeyPair = function(publicKey, privateKey) { checkElGamalPublicKey( publicKey, 'ElGamal public key for new ElGamalKeyPair object'); checkElGamalPrivateKey( privateKey, 'ElGamal private key for new ElGamalKeyPair object'); return new ElGamalKeyPair(publicKey, privateKey); }; /** * Creates a new ElGamalEncryptedElements object, which encapsulates the * encryption or encryption pre-computation of some Zp group elements. * * @function newEncryptedElements * @memberof ElGamalCryptographyService * @param {ZpGroupElement|string} * gammaOrJson The gamma Zp group element comprising the * encryption or pre-computation OR a JSON string * representation of an ElGamalEncryptedElements object, * compatible with its toJson method. For the * latter case, any additional input arguments will be ignored. * @param {ZpGroupElement[]} * phis The phi Zp group elements comprising the encryption or * pre-computation. * @param {Exponent} * [secret] The secret exponent comprising the encryption or * pre-computation. CAUTION: Provide only when the secret * is really needed later (e.g. for zero-knowledge proof * generation). * @returns {ElGamalEncryptedElements} The new ElGamalEncryptedElements * object. * @throws {Error} * If the input data validation fails. */ this.newEncryptedElements = function(gammaOrJson, phis, secret) { if (typeof gammaOrJson !== 'string') { validator.checkZpGroupElement( gammaOrJson, 'Gamma Zp group element for new ElGamalEncryptedElements object'); validator.checkZpGroupElements( phis, 'Phi Zp group elements for new ElGamalEncryptedElements object'); if (typeof secret !== 'undefined') { validator.checkExponent(secret, 'ElGamal encryption secret exponent'); } return new ElGamalEncryptedElements(gammaOrJson, phis, secret); } else { return jsonToEncryptedElements(gammaOrJson); } }; /** * Creates a new ElGamalEncrypter object for ElGamal encrypting data. It * must be initialized with an ElGamal public key. * * @function newEncrypter * @memberof ElGamalCryptographyService * @returns {ElGamalEncrypter} The new ElGamalEncrypter object. */ this.newEncrypter = function() { return new ElGamalEncrypter(mathService_); }; /** * Creates a new ElGamalDecrypter object for ElGamal decrypting data. It * must be initialized with an ElGamal private key. * * @function newDecrypter * @memberof ElGamalCryptographyService * @returns {ElGamalDecrypter} The new ElGamalDecrypter object. */ this.newDecrypter = function() { return new ElGamalDecrypter(mathService_); }; /** * Creates a new ElGamalPrngDecrypter object for ElGamal decrypting data, * via the PRNG seed used for encrypting the data. It must be initialized * with an ElGamal public key and a secure random generator, the latter * having been initialized with the seed. * * @function newPrngDecrypter * @memberof ElGamalCryptographyService * @returns {ElGamalPrngDecrypter} The new ElGamalPrngDecrypter object. */ this.newPrngDecrypter = function() { return new ElGamalPrngDecrypter(mathService_); }; function checkElGamalPublicKey(publicKey, label) { validator.checkElGamalPublicKey(publicKey, label); validator.checkIsInstanceOf( publicKey, ElGamalPublicKey, 'ElGamalPublicKey', label); } function checkElGamalPrivateKey(privateKey, label) { validator.checkElGamalPrivateKey(privateKey, label); validator.checkIsInstanceOf( privateKey, ElGamalPrivateKey, 'ElGamalPrivateKey', label); } function jsonToPublicKey(json) { validator.checkIsJsonString( json, 'JSON string to deserialize to ElGamalPublicKey object'); var parsed = JSON.parse(json).publicKey; var p = codec.bytesToBigInteger( codec.base64Decode(parsed.zpSubgroup.p.toString())); var q = codec.bytesToBigInteger( codec.base64Decode(parsed.zpSubgroup.q.toString())); var g = codec.bytesToBigInteger( codec.base64Decode(parsed.zpSubgroup.g.toString())); var group = mathService_.newZpSubgroup(p, q, g); var elements = []; for (var i = 0; i < parsed.elements.length; i++) { var value = codec.bytesToBigInteger(codec.base64Decode(parsed.elements[i])); var element = mathService_.newZpGroupElement(p, q, value); elements.push(element); } return new ElGamalPublicKey(group, elements); } function jsonToPrivateKey(json) { validator.checkIsJsonString( json, 'JSON string representation of ElGamalPrivateKey object'); var parsed = JSON.parse(json).privateKey; var g = codec.bytesToBigInteger( codec.base64Decode(parsed.zpSubgroup.g.toString())); var p = codec.bytesToBigInteger( codec.base64Decode(parsed.zpSubgroup.p.toString())); var q = codec.bytesToBigInteger( codec.base64Decode(parsed.zpSubgroup.q.toString())); var group = mathService_.newZpSubgroup(p, q, g); var exponents = []; for (var i = 0; i < parsed.exponents.length; i++) { var value = codec.bytesToBigInteger(codec.base64Decode(parsed.exponents[i])); var exponent = mathService_.newExponent(q, value); exponents.push(exponent); } return new ElGamalPrivateKey(group, exponents); } function jsonToEncryptedElements(json) { validator.checkIsJsonString( json, 'JSON string representation of ElGamalEncryptedElements object'); var parsed = JSON.parse(json).ciphertext; var p = codec.bytesToBigInteger(codec.base64Decode(parsed.p)); var q = codec.bytesToBigInteger(codec.base64Decode(parsed.q)); var gammaFromJson = mathService_.newZpGroupElement( p, q, codec.bytesToBigInteger(codec.base64Decode(parsed.gamma))); var phisFromJson = []; for (var i = 0; i < parsed.phis.length; i++) { phisFromJson.push(mathService_.newZpGroupElement( p, q, codec.bytesToBigInteger(codec.base64Decode(parsed.phis[i])))); } return new ElGamalEncryptedElements(gammaFromJson, phisFromJson); } }