/*
 * 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 keyStore = require('../lib/index');
var cryptoPolicy = require('scytl-cryptopolicy');

describe('The key store module that should be able to ...', function() {
  var PASSWORD = '01234567890abcdefghijk';
  var PRIVATE_KEY_ALIAS = 'user1';
  var SECRET_KEY_ALIAS = 'symmetricalias1';
  var ELGAMAL_PRIVATE_KEY_ALIAS = 'elgamalprivatekeyalias1';

  var SCYTL_KEY_STORE = {
    'salt': 'AWnL9mtD1UoAQ58l+8GH9aHPlrDMgeMF8m+0wHyCPAI=',
    'secrets': {
      'symmetricalias1':
          '7mgsaxqwL1oBASLQ40bcP5btCcLF1kgo+382PsIeqkyw3JeQIYsEIdRuUxdmP/9lUqQj+2sxxlCk6XU=',
      'symmetricalias2':
          'hF+vhbcCy0odpXrpPAohUKrnX3LP1sLv9t7QCaBgdStg8rvx55opMrV+l3+ywOwx6u3jKfhTbmL4tnM='
    },
    'store':
        '[encoded data, 13944 characters]'
  };

  var SCYTL_KEY_STORE_WITH_ELGAMAL_KEYS = {
    'salt': '7Q951QtBynbVfljodmtQmg351nq/kmxcHfCwdrTUdng=',
    'egPrivKeys': {
      'elgamalprivatekeyalias1':
          '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',
      'elgamalprivatekeyalias2':
          'vezAhuUI9Rqb9SNp9QXGpdiiKjJCGjK3MWmQbSC+NV+4HYEKKuCP6N9RuKAprssqmGYDWUc3ecYD6JsRlW2PNnbAv0oK/aGGogh3gqrVDLqBqSet1hmnN3NImAmXscpioAAnfPd1RR4V2lv8+Lc3aUPKTvNwl1P3jATM3TViGzb0hxMhsCuUwFOX'
    },
    'store':
        'MFUCAQMwEQYJKoZIhvcNAQcBoAQEAjAAMD0wITAJBgUrDgMCGgUABBR+zoG7NsLvE8zqUFX7HXeP/yK07gQUQaC4BDlbcjaPJxp4KTcJh9wSzMMCAgQA'
  };

  var keyStoreService_;
  var scytlKeyStore_;
  var scytlKeyStoreWithElGamalKeys_;
  var nonObject_;
  var emptyObject_;
  var nonString_;
  var nonJsonString_;
  var wrongPassword_;
  var wrongAlias_;
  var wrongSubjectCn_;
  var keyStoreWithNoSalt_ = clone(SCYTL_KEY_STORE);
  delete keyStoreWithNoSalt_.salt;
  var keyStoreWithUndefinedSalt_ = clone(SCYTL_KEY_STORE);
  keyStoreWithUndefinedSalt_.salt = undefined;
  var keyStoreWithNoStore_ = clone(SCYTL_KEY_STORE);
  delete keyStoreWithNoStore_.store;
  var keyStoreWithUndefinedStore_ = clone(SCYTL_KEY_STORE);
  keyStoreWithUndefinedStore_.store = undefined;

  beforeAll(function() {
    var policy = cryptoPolicy.newInstance();
    policy.symmetric.secretKey.encryption.lengthBytes = 32;
    policy.primitives.keyDerivation.pbkdf.keyLengthBytes = 16;
    policy.primitives.keyDerivation.pbkdf.minSaltLengthBytes = 20;
    policy.primitives.keyDerivation.pbkdf.hashAlgorithm = 'SHA256';
    policy.primitives.keyDerivation.pbkdf.numIterations = 1;
    keyStoreService_ = keyStore.newService({policy: policy});
    scytlKeyStore_ =
        keyStoreService_.newScytlKeyStore(SCYTL_KEY_STORE, PASSWORD);

    policy = cryptoPolicy.newInstance();
    policy.symmetric.secretKey.encryption.lengthBytes = 32;
    policy.primitives.keyDerivation.pbkdf.keyLengthBytes = 16;
    policy.primitives.keyDerivation.pbkdf.minSaltLengthBytes = 32;
    policy.primitives.keyDerivation.pbkdf.hashAlgorithm = 'SHA256';
    policy.primitives.keyDerivation.pbkdf.numIterations = 32000;
    var keyStoreService = keyStore.newService({policy: policy});
    scytlKeyStoreWithElGamalKeys_ = keyStoreService.newScytlKeyStore(
        SCYTL_KEY_STORE_WITH_ELGAMAL_KEYS, PASSWORD);

    nonObject_ = 999;
    emptyObject_ = {};
    nonString_ = 999;
    nonJsonString_ = 'Not a JSON string';
    wrongPassword_ = 'Wrong password';
    wrongAlias_ = 'Wrong alias';
    wrongSubjectCn_ = 'Wrong subject common name';
  });

  describe('create a key store service that should be able to ..', function() {
    it('throw an error when loading a Scytl key store, using an invalid key store data object or an invalid JSON serialization',
       function() {
         expect(function() {
           keyStoreService_.newScytlKeyStore(undefined, PASSWORD);
         }).toThrow();

         expect(function() {
           keyStoreService_.newScytlKeyStore(null, PASSWORD);
         }).toThrow();

         expect(function() {
           keyStoreService_.newScytlKeyStore({}, PASSWORD);
         }).toThrow();

         expect(function() {
           keyStoreService_.newScytlKeyStore(keyStoreWithNoSalt_, PASSWORD);
         }).toThrow();

         expect(function() {
           keyStoreService_.newScytlKeyStore(
               keyStoreWithUndefinedSalt_, PASSWORD);
         }).toThrow();

         expect(function() {
           keyStoreService_.newScytlKeyStore(keyStoreWithNoStore_, PASSWORD);
         }).toThrow();

         expect(function() {
           keyStoreService_.newScytlKeyStore(
               keyStoreWithUndefinedStore_, PASSWORD);
         }).toThrow();

         expect(function() {
           keyStoreService_.newScytlKeyStore(nonJsonString_, PASSWORD);
         }).toThrow();
       });

    it('throw an error when loading a Scytl key store, using an invalid password',
       function() {
         expect(function() {
           keyStoreService_.newScytlKeyStore(SCYTL_KEY_STORE);
         }).toThrow();

         expect(function() {
           keyStoreService_.newScytlKeyStore(SCYTL_KEY_STORE, undefined);
         }).toThrow();

         expect(function() {
           keyStoreService_.newScytlKeyStore(SCYTL_KEY_STORE, null);
         }).toThrow();

         expect(function() {
           keyStoreService_.newScytlKeyStore(SCYTL_KEY_STORE, nonString_);
         }).toThrow();

         expect(function() {
           keyStoreService_.newScytlKeyStore(SCYTL_KEY_STORE, wrongPassword_);
         }).toThrow();
       });

    describe('load a Scytl key store that should be able to', function() {
      it('throw an error when retrieving a private key, using an invalid alias',
         function() {
           expect(function() {
             scytlKeyStore_.getPrivateKey(undefined, PASSWORD);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getPrivateKey(null, PASSWORD);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getPrivateKey('', PASSWORD);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getPrivateKey(nonString_, PASSWORD);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getPrivateKey(wrongAlias_, PASSWORD);
           }).toThrow();
         });

      it('throw an error when retrieving a private key, using an invalid password',
         function() {
           expect(function() {
             scytlKeyStore_.getPrivateKey(PRIVATE_KEY_ALIAS);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getPrivateKey(PRIVATE_KEY_ALIAS, undefined);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getPrivateKey(PRIVATE_KEY_ALIAS, null);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getPrivateKey(PRIVATE_KEY_ALIAS, nonString_);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getPrivateKey(PRIVATE_KEY_ALIAS, wrongPassword_);
           }).toThrow();
         });

      it('throw an error when retrieving a certificate, using invalid input data',
         function() {
           expect(function() {
             scytlKeyStore_.getCertificate();
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificate(undefined);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificate(null);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificate('');
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificate(nonString_);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificate(wrongAlias_);
           }).toThrow();
         });

      it('throw an error when retrieving a certificate by subject, using invalid input data',
         function() {
           expect(function() {
             scytlKeyStore_.getCertificateBySubject();
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificateBySubject(undefined);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificateBySubject(null);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificateBySubject('');
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificateBySubject(nonString_);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificateBySubject(wrongSubjectCn_);
           }).toThrow();
         });

      it('throw an error when retrieving a certificate chain, using invalid input data',
         function() {
           expect(function() {
             scytlKeyStore_.getCertificateChain();
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificateChain(undefined);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificateChain(null);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificateChain('');
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificateChain(nonString_);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getCertificateChain(wrongAlias_);
           }).toThrow();
         });

      it('throw an error when retrieving a secret key, using an invalid alias',
         function() {
           expect(function() {
             scytlKeyStore_.getSecretKey(undefined, PASSWORD);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getSecretKey(null, PASSWORD);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getSecretKey('', PASSWORD);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getSecretKey(nonString_, PASSWORD);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getSecretKey(wrongAlias_, PASSWORD);
           }).toThrow();
         });

      it('throw an error when retrieving a secret key, using an invalid password',
         function() {
           expect(function() {
             scytlKeyStore_.getSecretKey(SECRET_KEY_ALIAS);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getSecretKey(SECRET_KEY_ALIAS, undefined);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getSecretKey(SECRET_KEY_ALIAS, null);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getSecretKey(SECRET_KEY_ALIAS, nonString_);
           }).toThrow();

           expect(function() {
             scytlKeyStore_.getSecretKey(SECRET_KEY_ALIAS, wrongPassword_);
           }).toThrow();
         });

      it('throw an error when retrieving an ElGamal private key, using an invalid alias',
         function() {
           expect(function() {
             scytlKeyStoreWithElGamalKeys_.getElGamalPrivateKey(
                 undefined, PASSWORD);
           }).toThrow();

           expect(function() {
             scytlKeyStoreWithElGamalKeys_.getElGamalPrivateKey(null, PASSWORD);
           }).toThrow();

           expect(function() {
             scytlKeyStoreWithElGamalKeys_.getElGamalPrivateKey('', PASSWORD);
           }).toThrow();

           expect(function() {
             scytlKeyStoreWithElGamalKeys_.getElGamalPrivateKey(
                 nonString_, PASSWORD);
           }).toThrow();

           expect(function() {
             scytlKeyStoreWithElGamalKeys_.getElGamalPrivateKey(
                 wrongAlias_, PASSWORD);
           }).toThrow();
         });

      it('throw an error when retrieving an ElGamal private key, using an invalid password',
         function() {
           expect(function() {
             scytlKeyStoreWithElGamalKeys_.getElGamalPrivateKey(
                 ELGAMAL_PRIVATE_KEY_ALIAS);
           }).toThrow();

           expect(function() {
             scytlKeyStoreWithElGamalKeys_.getElGamalPrivateKey(
                 ELGAMAL_PRIVATE_KEY_ALIAS, undefined);
           }).toThrow();

           expect(function() {
             scytlKeyStoreWithElGamalKeys_.getElGamalPrivateKey(
                 ELGAMAL_PRIVATE_KEY_ALIAS, null);
           }).toThrow();

           expect(function() {
             scytlKeyStoreWithElGamalKeys_.getElGamalPrivateKey(
                 ELGAMAL_PRIVATE_KEY_ALIAS, nonString_);
           }).toThrow();

           expect(function() {
             scytlKeyStoreWithElGamalKeys_.getElGamalPrivateKey(
                 ELGAMAL_PRIVATE_KEY_ALIAS, wrongPassword_);
           }).toThrow();
         });
    });
  });

  function clone(object) {
    return JSON.parse(JSON.stringify(object));
  }
});
