service.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /*
  2. * Copyright 2018 Scytl Secure Electronic Voting SA
  3. *
  4. * All rights reserved
  5. *
  6. * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
  7. */
  8. /* jshint node:true */
  9. 'use strict';
  10. var ElGamalKeyPair = require('./key-pair');
  11. var ElGamalPublicKey = require('./public-key');
  12. var ElGamalPrivateKey = require('./private-key');
  13. var ElGamalEncrypter = require('./encrypter');
  14. var ElGamalDecrypter = require('./decrypter');
  15. var ElGamalPrngDecrypter = require('./prng-decrypter');
  16. var ElGamalEncryptedElements = require('./encrypted-elements');
  17. var mathematical = require('scytl-mathematical');
  18. var validator = require('./input-validator');
  19. var codec = require('scytl-codec');
  20. module.exports = ElGamalCryptographyService;
  21. /**
  22. * @class ElGamalCryptographyService
  23. * @classdesc The ElGamal cryptography service API. To instantiate this object,
  24. * use the method {@link newService}.
  25. * @hideconstructor
  26. * @param {Object}
  27. * [options] An object containing optional arguments.
  28. * @param {SecureRandomService}
  29. * [options.secureRandomService=Created internally] The secure random
  30. * service to use.
  31. * @param {MathematicalService}
  32. * [options.mathematicalService=Created internally] The mathematical
  33. * service to use.
  34. */
  35. function ElGamalCryptographyService(options) {
  36. options = options || {};
  37. var secureRandomService;
  38. if (options.secureRandomService) {
  39. secureRandomService = options.secureRandomService;
  40. }
  41. var mathService_;
  42. if (options.mathematicalService) {
  43. mathService_ = options.mathematicalService;
  44. } else if (secureRandomService) {
  45. mathService_ =
  46. mathematical.newService({secureRandomService: secureRandomService});
  47. } else {
  48. mathService_ = mathematical.newService();
  49. }
  50. /**
  51. * Creates a new ElGamalPublicKey object, which encapsulates an ElGamal
  52. * public key.
  53. *
  54. * @function newPublicKey
  55. * @memberof ElGamalCryptographyService
  56. * @param {ZpSubgroup|string}
  57. * groupOrJson The Zp subgroup to which the elements of the
  58. * public key belong <b>OR</b> a JSON string representation of
  59. * an ElGamalPublicKey object, compatible with its
  60. * <code>toJson</code> method. For the latter case, any
  61. * additional input arguments will be ignored.
  62. * @param {ZpGroupElement[]}
  63. * elements The Zp group elements that comprise the public key.
  64. * @returns {ElGamalPublicKey} The ElGamalPublicKey object.
  65. * @throws {Error}
  66. * If the input data validation fails.
  67. */
  68. this.newPublicKey = function(groupOrJson, elements) {
  69. if (typeof groupOrJson !== 'string') {
  70. validator.checkIsObjectWithProperties(
  71. groupOrJson, 'Zp subgroup for new ElGamalPublicKey object');
  72. validator.checkZpGroupElements(
  73. elements, 'Zp group elements for new ElGamalPublicKey object',
  74. groupOrJson);
  75. return new ElGamalPublicKey(groupOrJson, elements);
  76. } else {
  77. return jsonToPublicKey(groupOrJson);
  78. }
  79. };
  80. /**
  81. * Creates a new ElGamalPrivateKey object, which encapsulates an ElGamal
  82. * private key.
  83. *
  84. * @function newPrivateKey
  85. * @memberof ElGamalCryptographyService
  86. * @param {ZpSubgroup|string}
  87. * groupOrJson The Zp subgroup to which the exponents of the
  88. * private key are associated <b>OR</b> a JSON string
  89. * representation of an ElGamalPrivateKey object, compatible with
  90. * its <code>toJson</code> method. For the latter case, any
  91. * additional input arguments will be ignored.
  92. * @param {Exponent[]}
  93. * exponents The exponents that comprise the private key.
  94. * @returns {ElGamalPrivateKey} The new ElGamalPrivateKey object.
  95. * @throws {Error}
  96. * If the input data validation fails.
  97. */
  98. this.newPrivateKey = function(groupOrJson, exponents) {
  99. if (typeof groupOrJson !== 'string') {
  100. validator.checkIsObjectWithProperties(
  101. groupOrJson, 'Zp subgroup for new ElGamalPrivateKey object');
  102. validator.checkExponents(
  103. exponents, 'Exponents for new ElGamalPrivateKey object',
  104. groupOrJson.q);
  105. return new ElGamalPrivateKey(groupOrJson, exponents);
  106. } else {
  107. return jsonToPrivateKey(groupOrJson);
  108. }
  109. };
  110. /**
  111. * Creates a new ElGamalKeyPair object, which encapsulates an ElGamal key
  112. * pair.
  113. *
  114. * @function newKeyPair
  115. * @memberof ElGamalCryptographyService
  116. * @param {ElGamalPublicKey}
  117. * publicKey The ElGamal public key comprising the key pair.
  118. * @param {ElGamalPrivateKey}
  119. * privateKey The ElGamal private key comprising the key pair.
  120. * @returns {ElGamalKeyPair} The new ElGamalKeyPair object.
  121. * @throws {Error}
  122. * If the input data validation fails.
  123. */
  124. this.newKeyPair = function(publicKey, privateKey) {
  125. checkElGamalPublicKey(
  126. publicKey, 'ElGamal public key for new ElGamalKeyPair object');
  127. checkElGamalPrivateKey(
  128. privateKey, 'ElGamal private key for new ElGamalKeyPair object');
  129. return new ElGamalKeyPair(publicKey, privateKey);
  130. };
  131. /**
  132. * Creates a new ElGamalEncryptedElements object, which encapsulates the
  133. * encryption or encryption pre-computation of some Zp group elements.
  134. *
  135. * @function newEncryptedElements
  136. * @memberof ElGamalCryptographyService
  137. * @param {ZpGroupElement|string}
  138. * gammaOrJson The gamma Zp group element comprising the
  139. * encryption or pre-computation <b>OR</b> a JSON string
  140. * representation of an ElGamalEncryptedElements object,
  141. * compatible with its <code>toJson</code> method. For the
  142. * latter case, any additional input arguments will be ignored.
  143. * @param {ZpGroupElement[]}
  144. * phis The phi Zp group elements comprising the encryption or
  145. * pre-computation.
  146. * @param {Exponent}
  147. * [secret] The secret exponent comprising the encryption or
  148. * pre-computation. <b>CAUTION:</b> Provide only when the secret
  149. * is really needed later (e.g. for zero-knowledge proof
  150. * generation).
  151. * @returns {ElGamalEncryptedElements} The new ElGamalEncryptedElements
  152. * object.
  153. * @throws {Error}
  154. * If the input data validation fails.
  155. */
  156. this.newEncryptedElements = function(gammaOrJson, phis, secret) {
  157. if (typeof gammaOrJson !== 'string') {
  158. validator.checkZpGroupElement(
  159. gammaOrJson,
  160. 'Gamma Zp group element for new ElGamalEncryptedElements object');
  161. validator.checkZpGroupElements(
  162. phis,
  163. 'Phi Zp group elements for new ElGamalEncryptedElements object');
  164. if (typeof secret !== 'undefined') {
  165. validator.checkExponent(secret, 'ElGamal encryption secret exponent');
  166. }
  167. return new ElGamalEncryptedElements(gammaOrJson, phis, secret);
  168. } else {
  169. return jsonToEncryptedElements(gammaOrJson);
  170. }
  171. };
  172. /**
  173. * Creates a new ElGamalEncrypter object for ElGamal encrypting data. It
  174. * must be initialized with an ElGamal public key.
  175. *
  176. * @function newEncrypter
  177. * @memberof ElGamalCryptographyService
  178. * @returns {ElGamalEncrypter} The new ElGamalEncrypter object.
  179. */
  180. this.newEncrypter = function() {
  181. return new ElGamalEncrypter(mathService_);
  182. };
  183. /**
  184. * Creates a new ElGamalDecrypter object for ElGamal decrypting data. It
  185. * must be initialized with an ElGamal private key.
  186. *
  187. * @function newDecrypter
  188. * @memberof ElGamalCryptographyService
  189. * @returns {ElGamalDecrypter} The new ElGamalDecrypter object.
  190. */
  191. this.newDecrypter = function() {
  192. return new ElGamalDecrypter(mathService_);
  193. };
  194. /**
  195. * Creates a new ElGamalPrngDecrypter object for ElGamal decrypting data,
  196. * via the PRNG seed used for encrypting the data. It must be initialized
  197. * with an ElGamal public key and a secure random generator, the latter
  198. * having been initialized with the seed.
  199. *
  200. * @function newPrngDecrypter
  201. * @memberof ElGamalCryptographyService
  202. * @returns {ElGamalPrngDecrypter} The new ElGamalPrngDecrypter object.
  203. */
  204. this.newPrngDecrypter = function() {
  205. return new ElGamalPrngDecrypter(mathService_);
  206. };
  207. function checkElGamalPublicKey(publicKey, label) {
  208. validator.checkElGamalPublicKey(publicKey, label);
  209. validator.checkIsInstanceOf(
  210. publicKey, ElGamalPublicKey, 'ElGamalPublicKey', label);
  211. }
  212. function checkElGamalPrivateKey(privateKey, label) {
  213. validator.checkElGamalPrivateKey(privateKey, label);
  214. validator.checkIsInstanceOf(
  215. privateKey, ElGamalPrivateKey, 'ElGamalPrivateKey', label);
  216. }
  217. function jsonToPublicKey(json) {
  218. validator.checkIsJsonString(
  219. json, 'JSON string to deserialize to ElGamalPublicKey object');
  220. var parsed = JSON.parse(json).publicKey;
  221. var p = codec.bytesToBigInteger(
  222. codec.base64Decode(parsed.zpSubgroup.p.toString()));
  223. var q = codec.bytesToBigInteger(
  224. codec.base64Decode(parsed.zpSubgroup.q.toString()));
  225. var g = codec.bytesToBigInteger(
  226. codec.base64Decode(parsed.zpSubgroup.g.toString()));
  227. var group = mathService_.newZpSubgroup(p, q, g);
  228. var elements = [];
  229. for (var i = 0; i < parsed.elements.length; i++) {
  230. var value =
  231. codec.bytesToBigInteger(codec.base64Decode(parsed.elements[i]));
  232. var element = mathService_.newZpGroupElement(p, q, value);
  233. elements.push(element);
  234. }
  235. return new ElGamalPublicKey(group, elements);
  236. }
  237. function jsonToPrivateKey(json) {
  238. validator.checkIsJsonString(
  239. json, 'JSON string representation of ElGamalPrivateKey object');
  240. var parsed = JSON.parse(json).privateKey;
  241. var g = codec.bytesToBigInteger(
  242. codec.base64Decode(parsed.zpSubgroup.g.toString()));
  243. var p = codec.bytesToBigInteger(
  244. codec.base64Decode(parsed.zpSubgroup.p.toString()));
  245. var q = codec.bytesToBigInteger(
  246. codec.base64Decode(parsed.zpSubgroup.q.toString()));
  247. var group = mathService_.newZpSubgroup(p, q, g);
  248. var exponents = [];
  249. for (var i = 0; i < parsed.exponents.length; i++) {
  250. var value =
  251. codec.bytesToBigInteger(codec.base64Decode(parsed.exponents[i]));
  252. var exponent = mathService_.newExponent(q, value);
  253. exponents.push(exponent);
  254. }
  255. return new ElGamalPrivateKey(group, exponents);
  256. }
  257. function jsonToEncryptedElements(json) {
  258. validator.checkIsJsonString(
  259. json, 'JSON string representation of ElGamalEncryptedElements object');
  260. var parsed = JSON.parse(json).ciphertext;
  261. var p = codec.bytesToBigInteger(codec.base64Decode(parsed.p));
  262. var q = codec.bytesToBigInteger(codec.base64Decode(parsed.q));
  263. var gammaFromJson = mathService_.newZpGroupElement(
  264. p, q, codec.bytesToBigInteger(codec.base64Decode(parsed.gamma)));
  265. var phisFromJson = [];
  266. for (var i = 0; i < parsed.phis.length; i++) {
  267. phisFromJson.push(mathService_.newZpGroupElement(
  268. p, q, codec.bytesToBigInteger(codec.base64Decode(parsed.phis[i]))));
  269. }
  270. return new ElGamalEncryptedElements(gammaFromJson, phisFromJson);
  271. }
  272. }