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