/*
 * 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
 */
var elGamalCipherTest = {

  /**
   * ElGamal decrypts a collection of Zp group elements that was encrypted
   * using Java and checks if the original collection of Zp group elements
   * could be retrieved.
   *
   * @param {String}
   *            elGamalPrivateKeyJson the ElGamal private key, in JSON format.
   * @param {String}
   *            encryptedZpGroupElementsJson the collection of encrypted Zp
   *            group elements, in JSON format.
   * @param {String}
   *            zpGroupElementsJson the collection of Zp group elements that
   *            were encryped, in JSON format.
   * @throws {Error}
   *             if the decryption operation fails or the original collection
   *             of Zp group elements could not be retrieved.
   */
  decrypt: function(
      elGamalPrivateKeyJson, encryptedZpGroupElementsJson, zpGroupElementsJson,
      preComputedValuesJson) {

    'use strict';

    var errorData = {};
    errorData['ElGamal private key (JSON format) [Java]'] =
        elGamalPrivateKeyJson;
    errorData['Encrypted Zp group elements (JSON format) [Java]'] =
        encryptedZpGroupElementsJson;
    errorData['Zp group elements that were encrypted (JSON format) [Java]'] =
        zpGroupElementsJson;

    try {
      cryptolib('homomorphic.cipher', 'homomorphic.service', function(box) {
        var elGamalPrivateKeyJsonB64 =
            _converters.base64Encode(elGamalPrivateKeyJson);
        var elGamalPrivateKey = box.homomorphic.service.createElGamalPrivateKey(
            elGamalPrivateKeyJsonB64);

        var encryptedZpGroupElements =
            box.homomorphic.cipher.deserializeElGamalComputationValues(
                encryptedZpGroupElementsJson);

        var zpGroupElements =
            testUtils.deserializeZpGroupElements(zpGroupElementsJson);

        var decryptedZpGroupElements =
            box.homomorphic.service.createDecrypter(elGamalPrivateKey)
                .decrypt(encryptedZpGroupElements, true);

        errorData['Decrypted Zp group elements (JSON format) [JS]'] =
            testUtils.stringifyZpGroupElements(decryptedZpGroupElements);

        var decryptionLengthFound = decryptedZpGroupElements.length;
        var decryptionLengthExpected = zpGroupElements.length;
        if (decryptionLengthFound !== decryptionLengthExpected) {
          throw 'Expected decrypted Zp group element collection length: ' +
              decryptionLengthExpected + ' ; Found: ' + decryptionLengthFound;
        }

        for (var i = 0; i < decryptedZpGroupElements.length; i++) {
          var zpGroupElementFound = decryptedZpGroupElements[i];
          var zpGroupElementExpected = zpGroupElements[i];

          if (!zpGroupElementFound.equals(zpGroupElementExpected)) {
            throw 'Expected decrypted Zp group element: ' +
                zpGroupElementExpected.stringify() +
                ' ; Found: ' + zpGroupElementFound.stringify();
          }
        }
      });
    } catch (error) {
      throw testUtils.serializeError(error, errorData);
    }
  },

  /**
   * ElGamal encrypts a collection of Zp group elements that will be decrypted
   * using Java.
   *
   * @param {String}
   *            elGamalPublicKeyJson the ElGamal public key, in JSON format.
   * @param {String}
   *            zpGroupElementsJson the collection of Zp group elements to
   *            encrypt, in JSON format.
   * @param {String}
   *            preComputedValuesJson the pre-computed ElGamal encryption
   *            values, in JSON format (if the pre-computation step was
   *            performed).
   * @returns {String} the ElGamal encrypted data, in JSON format.
   * @throws {Error}
   *             if the encryption operation fails.
   */
  encrypt: function(
      elGamalPublicKeyJson, zpGroupElementsJson, preComputedValuesJson) {

    'use strict';

    var errorData = {};
    errorData['ElGamal public key (JSON format) [Java]'] = elGamalPublicKeyJson;
    if (typeof preComputedValuesJson !== 'undefined') {
      errorData['Pre-computed ElGamal encryption values (JSON format) [JS]'] =
          preComputedValuesJson;
    }

    try {
      var encryptedZpGroupElementsJson;
      cryptolib('homomorphic.cipher', 'homomorphic.service', function(box) {
        var elGamalPublicKeyJsonB64 =
            _converters.base64Encode(elGamalPublicKeyJson);
        var elGamalPublicKey = box.homomorphic.service.createElGamalPublicKey(
            elGamalPublicKeyJsonB64);

        var zpGroupElements =
            testUtils.deserializeZpGroupElements(zpGroupElementsJson);

        var encryptedZpGroupElements;
        if (typeof preComputedValuesJson !== 'undefined') {
          var preComputationValues =
              box.homomorphic.cipher.deserializeElGamalComputationValues(
                  preComputedValuesJson);
          var preComputedEncrypterValues =
              new box.homomorphic.cipher.ElGamalEncrypterValues(
                  null, preComputationValues.getGamma(),
                  preComputationValues.getPhis());

          encryptedZpGroupElements =
              box.homomorphic.service.createEncrypter(elGamalPublicKey)
                  .encryptGroupElements(
                      zpGroupElements, preComputedEncrypterValues)
                  .getElGamalComputationValues();
        } else {
          encryptedZpGroupElements =
              box.homomorphic.service.createEncrypter(elGamalPublicKey)
                  .encryptGroupElements(zpGroupElements)
                  .getElGamalComputationValues();
        }

        encryptedZpGroupElementsJson = encryptedZpGroupElements.stringify();
      });

      return encryptedZpGroupElementsJson;
    } catch (error) {
      throw testUtils.serializeError(error, errorData);
    }
  },

  /**
   * Generates pre-computed ElGamal encryption values, that can be used to
   * ElGamal encrypt data, using Java or JavaScript.
   *
   * @param {String}
   *            elGamalPublicKeyJson the ElGamal public key, in JSON format.
   * @returns {String} the generated pre-computed values, in JSON format.
   * @throws {Error}
   *             if the pre-computed values generation operation fails.
   */
  generatePreComputedValues: function(elGamalPublicKeyJson) {

    'use strict';

    var errorData = {};
    errorData['ElGamal public key (JSON format) [Java]'] = elGamalPublicKeyJson;

    try {
      var preComputedValuesJson;
      cryptolib('homomorphic.cipher', 'homomorphic.service', function(box) {
        var elGamalPublicKeyJsonB64 =
            _converters.base64Encode(elGamalPublicKeyJson);
        var elGamalPublicKey = box.homomorphic.service.createElGamalPublicKey(
            elGamalPublicKeyJsonB64);

        var elGamalEncrypter =
            box.homomorphic.service.createEncrypter(elGamalPublicKey);

        var preComputedEncrypterValues = elGamalEncrypter.preCompute();

        preComputedValuesJson =
            preComputedEncrypterValues.getElGamalComputationValues()
                .stringify();
      });

      return preComputedValuesJson;
    } catch (error) {
      throw testUtils.serializeError(error, errorData);
    }
  }
};