/*
 * 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 CommonTestData = require('./data/common-data');
var ValidationTestData = require('./data/validation-data');
var elGamal = require('../lib/index');
var secureRandom = require('scytl-securerandom');

describe('The ElGamal cryptography module should be able to ...', function() {
  var elGamalService_;
  var secureRandomGenerator_;

  var group_;
  var publicKey_;
  var privateKey_;
  var publicKeyElements_;
  var privateKeyExponents_;
  var elements_;
  var elementValueStrings_;

  var nonObject_;
  var emptyObject_;
  var nonSecureRandomService_;
  var nonSecureRandomGenerator_;
  var nonMathematicalService_;
  var nonBoolean_;
  var nonString_;
  var nonJsonString_;
  var nonArray_;
  var nonStringArray_;
  var nonMathObject_;
  var nonMathObjectArray_;
  var nonElGamalObject_;
  var elementsFromAnotherGroup_;
  var publicKeyFromAnotherGroup_;

  var elGamalEncrypter_;
  var elGamalDecrypter_;
  var elGamalPrngDecrypter_;
  var encryptedElements_;
  var gamma_;
  var phis_;
  var secret_;
  var encryptedElementsFromAnotherGroup_;

  beforeAll(function() {
    elGamalService_ = elGamal.newService();
    secureRandomGenerator_ = secureRandom.newService().newRandomGenerator();

    var commonTestData = new CommonTestData();
    group_ = commonTestData.getGroup();
    publicKey_ = commonTestData.getPublicKey();
    privateKey_ = commonTestData.getPrivateKey();
    publicKeyElements_ = commonTestData.getPublicKeyElements();
    privateKeyExponents_ = commonTestData.getPrivateKeyExponents();
    elements_ = commonTestData.getZpGroupElements();
    elementValueStrings_ = commonTestData.getZpGroupElementValueStrings();

    var validationTestData = new ValidationTestData();
    nonObject_ = validationTestData.getNonObject();
    emptyObject_ = validationTestData.getEmptyObject();
    nonSecureRandomService_ = validationTestData.getNonSecureRandomService();
    nonSecureRandomGenerator_ =
        validationTestData.getNonSecureRandomGenerator();
    nonMathematicalService_ = validationTestData.getNonMathematicalService();
    nonBoolean_ = validationTestData.getNonBoolean();
    nonString_ = validationTestData.getNonString();
    nonJsonString_ = validationTestData.getNonJsonString();
    nonArray_ = validationTestData.getNonArray();
    nonStringArray_ = validationTestData.getNonStringArray();
    nonMathObject_ = validationTestData.getNonMathObject();
    nonMathObjectArray_ = validationTestData.getNonMathObjectArray();
    nonElGamalObject_ = validationTestData.getNonElGamalObject();
    elementsFromAnotherGroup_ =
        validationTestData.getElementsFromAnotherGroup();
    publicKeyFromAnotherGroup_ =
        validationTestData.getPublicKeyFromAnotherGroup();
  });

  beforeEach(function() {
    elGamalEncrypter_ = elGamalService_.newEncrypter().init(publicKey_);
    elGamalDecrypter_ = elGamalService_.newDecrypter().init(privateKey_);
    elGamalPrngDecrypter_ = elGamalService_.newPrngDecrypter().init(
        publicKey_, secureRandomGenerator_);

    var encryptedElementsAndSecret =
        elGamalEncrypter_.encrypt(elements_, {saveSecret: true});
    gamma_ = encryptedElementsAndSecret.gamma;
    phis_ = encryptedElementsAndSecret.phis;
    secret_ = encryptedElementsAndSecret.secret;
    encryptedElements_ = elGamalService_.newEncryptedElements(
        encryptedElementsAndSecret.gamma, encryptedElementsAndSecret.phis);

    encryptedElementsFromAnotherGroup_ =
        elGamalService_.newEncrypter()
            .init(publicKeyFromAnotherGroup_)
            .encrypt(elementsFromAnotherGroup_);
  });

  describe('create an ElGamal cryptography service that should be able to ..', function() {
    it('throw an error when being created, using an invalid secure random service object',
       function() {
         expect(function() {
           Object.create(elGamal.newService({secureRandomService: null}));
         }).toThrow();

         expect(function() {
           Object.create(elGamal.newService({secureRandomService: nonObject_}));
         }).toThrow();

         expect(function() {
           Object.create(
               elGamal.newService({secureRandomService: emptyObject_}));
         }).toThrow();
       });

    it('throw an error when being created, using an invalid mathematical service object',
       function() {
         expect(function() {
           Object.create(elGamal.newService({mathematicalService: null}));
         }).toThrow();

         expect(function() {
           Object.create(elGamal.newService({mathematicalService: nonObject_}));
         }).toThrow();

         expect(function() {
           Object.create(
               elGamal.newService({mathematicalService: emptyObject_}));
         }).toThrow();
       });

    it('throw an error when creating a new ElGamalPublicKey object, using invalid input data',
       function() {
         expect(function() {
           elGamalService_.newPublicKey(group_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPublicKey(undefined, publicKeyElements_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPublicKey(null, publicKeyElements_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPublicKey(nonMathObject_, publicKeyElements_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPublicKey(group_, undefined);
         }).toThrow();

         expect(function() {
           elGamalService_.newPublicKey(group_, null);
         }).toThrow();

         expect(function() {
           elGamalService_.newPublicKey(group_, nonArray_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPublicKey(group_, nonMathObjectArray_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPublicKey(nonJsonString_);
         }).toThrow();
       });

    it('throw an error when creating a new ElGamalPrivateKey object, using invalid input data',
       function() {
         expect(function() {
           elGamalService_.newPrivateKey(undefined, privateKeyExponents_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPrivateKey(null, privateKeyExponents_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPrivateKey(nonObject_, privateKeyExponents_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPrivateKey(emptyObject_, privateKeyExponents_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPrivateKey(group_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPrivateKey(group_, undefined);
         }).toThrow();

         expect(function() {
           elGamalService_.newPrivateKey(group_, null);
         }).toThrow();

         expect(function() {
           elGamalService_.newPrivateKey(group_, nonArray_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPrivateKey(group_, nonMathObjectArray_);
         }).toThrow();

         expect(function() {
           elGamalService_.newPrivateKey(nonJsonString_);
         }).toThrow();
       });

    it('throw an error when creating a new ElGamalKeyPair object, using invalid input data',
       function() {
         expect(function() {
           elGamalService_.newKeyPair(publicKey_);
         }).toThrow();

         expect(function() {
           elGamalService_.newKeyPair(undefined, privateKey_);
         }).toThrow();

         expect(function() {
           elGamalService_.newKeyPair(null, privateKey_);
         }).toThrow();

         expect(function() {
           elGamalService_.newKeyPair(nonElGamalObject_, privateKey_);
         }).toThrow();

         expect(function() {
           elGamalService_.newKeyPair(publicKey_, undefined);
         }).toThrow();

         expect(function() {
           elGamalService_.newKeyPair(publicKey_, null);
         }).toThrow();

         expect(function() {
           elGamalService_.newKeyPair(publicKey_, nonElGamalObject_);
         }).toThrow();
       });

    it('throw an error when creating a new ElGamalEncryptedElements object, using invalid input data',
       function() {
         expect(function() {
           elGamalService_.newEncryptedElements(undefined, phis_);
         }).toThrow();

         expect(function() {
           elGamalService_.newEncryptedElements(null, phis_);
         }).toThrow();

         expect(function() {
           elGamalService_.newEncryptedElements(nonObject_, phis_);
         }).toThrow();

         expect(function() {
           elGamalService_.newEncryptedElements(emptyObject_, phis_);
         }).toThrow();

         expect(function() {
           elGamalService_.newEncryptedElements(gamma_);
         }).toThrow();

         expect(function() {
           elGamalService_.newEncryptedElements(gamma_, undefined);
         }).toThrow();

         expect(function() {
           elGamalService_.newEncryptedElements(gamma_, null);
         }).toThrow();

         expect(function() {
           elGamalService_.newEncryptedElements(gamma_, nonArray_);
         }).toThrow();

         expect(function() {
           elGamalService_.newEncryptedElements(gamma_, nonMathObjectArray_);
         }).toThrow();

         expect(function() {
           elGamalService_.newEncryptedElements(gamma_, phis_, null);
         }).toThrow();

         expect(function() {
           elGamalService_.newEncryptedElements(gamma_, phis_, nonObject_);
         }).toThrow();

         expect(function() {
           elGamalService_.newEncryptedElements(gamma_, phis_, emptyObject_);
         }).toThrow();

         expect(function() {
           elGamalService_.newEncryptedElements(nonJsonString_);
         }).toThrow();
       });

    describe('Create an encrypter/decrypter pair that should ..', function() {
      it('throw an error when being initialized, using invalid input data',
         function() {
           expect(function() {
             elGamalEncrypter_.init();
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.init(undefined);
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.init(null);
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.init(nonElGamalObject_);
           }).toThrow();

           expect(function() {
             elGamalDecrypter_.init();
           }).toThrow();

           expect(function() {
             elGamalDecrypter_.init(undefined);
           }).toThrow();

           expect(function() {
             elGamalDecrypter_.init(null);
           }).toThrow();

           expect(function() {
             elGamalDecrypter_.init(nonElGamalObject_);
           }).toThrow();
         });

      it('throw an error when encrypting an array of Zp group elements, using invalid input data',
         function() {
           expect(function() {
             elGamalEncrypter_.encrypt();
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(undefined);
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(null);
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(nonArray_);
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(nonMathObjectArray_);
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(
                 elements_, {useShortExponent: nonBoolean_});
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(elements_, {preComputation: null});
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(elements_, {preComputation: nonObject_});
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(
                 elements_, {preComputation: emptyObject_});
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(
                 elements_, {preComputation: nonElGamalObject_});
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(elementsFromAnotherGroup_);
           }).toThrow();
         });

      it('throw an error when encrypting an array of Zp group element strings, using invalid input data',
         function() {
           expect(function() {
             elGamalEncrypter_.encrypt();
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(undefined);
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(null);
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(nonArray_);
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(nonStringArray_);
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(elements_, {preComputation: null});
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(elements_, {preComputation: nonObject_});
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(
                 elements_, {preComputation: emptyObject_});
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(
                 elements_, {preComputation: nonElGamalObject_});
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(
                 elements_, {useShortExponent: nonBoolean_});
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.encrypt(elements_, {saveSecret: nonBoolean_});
           }).toThrow();
         });

      it('throw an error when pre-computing an ElGamal encryption, using invalid input data',
         function() {
           expect(function() {
             elGamalEncrypter_.preCompute({useShortExponent: nonBoolean_});
           }).toThrow();

           expect(function() {
             elGamalEncrypter_.preCompute({saveSecret: nonBoolean_});
           }).toThrow();
         });

      it('throw an error when decrypting an ElGamalEncryptedElements object, using invalid input data',
         function() {
           expect(function() {
             elGamalDecrypter_.decrypt();
           }).toThrow();

           expect(function() {
             elGamalDecrypter_.decrypt(undefined);
           }).toThrow();

           expect(function() {
             elGamalDecrypter_.decrypt(null);
           }).toThrow();

           expect(function() {
             elGamalDecrypter_.decrypt(nonElGamalObject_);
           }).toThrow();

           expect(function() {
             elGamalDecrypter_.decrypt(
                 encryptedElements_, {confirmMembership: null});
           }).toThrow();

           expect(function() {
             elGamalDecrypter_.decrypt(
                 encryptedElements_, {confirmMembership: nonBoolean_});
           }).toThrow();

           expect(function() {
             elGamalDecrypter_.decrypt(encryptedElementsFromAnotherGroup_);
           }).toThrow();
         });
    });

    describe('Create a PRNG decrypter that should ..', function() {
      it('throw an error when being initialized, using invalid input data',
         function() {
           expect(function() {
             elGamalPrngDecrypter_.init();
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.init(undefined);
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.init(null);
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.init(nonElGamalObject_);
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.init(publicKey_);
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.init(publicKey_, undefined);
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.init(publicKey_, null);
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.init(publicKey_, nonObject_);
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.init(publicKey_, emptyObject_);
           }).toThrow();
         });

      it('throw an error when decrypting an ElGamalEncryptedElements object, using invalid input data',
         function() {
           expect(function() {
             elGamalPrngDecrypter_.decrypt();
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.decrypt(undefined);
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.decrypt(null);
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.decrypt(nonElGamalObject_);
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.decrypt(
                 encryptedElements_, {confirmMembership: null});
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.decrypt(
                 encryptedElements_, {confirmMembership: nonBoolean_});
           }).toThrow();

           expect(function() {
             elGamalPrngDecrypter_.decrypt(encryptedElementsFromAnotherGroup_);
           }).toThrow();
         });
    });
  });
});
