/*
 * 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');
var elGamal = require('scytl-elgamal');
var certificate = require('scytl-certificate');
var symmetric = require('scytl-symmetric');
var pbkdf = require('scytl-pbkdf');
var codec = require('scytl-codec');

describe('The key store module that should be able to ...', function() {
  var PASSWORD = '01234567890abcdefghijk';
  var PRIVATE_KEY_1_ALIAS = 'user1';
  var PRIVATE_KEY_2_ALIAS = 'user2';
  var SECRET_KEY_1_ALIAS = 'symmetricalias1';
  var SECRET_KEY_2_ALIAS = 'symmetricalias2';
  var ELGAMAL_PRIVATE_KEY_1_ALIAS = 'elgamalprivatekeyalias1';
  var ELGAMAL_PRIVATE_KEY_2_ALIAS = 'elgamalprivatekeyalias2';
  var SUBJECT_COMMON_NAME = 'SecDev';

  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 ELGAMAL_PRIVATE_KEY_P_B64 =
      '25878792566670842099842137716422866466252991028815773139028451679515364679624923581358662655689289205766441980239548823737806954397019411202244121935752456749381769565031670387914863935577896116425654849306598185507995737892509839616944496073707445338806101425467388977937489020456783676102620561970644684015868766028080049372849872115052208214439472603355483095640041515460851475971118272125133224007949688443680429668091313474118875081620746919907567682398209044343652147328622866834600839878114285018818463110227111614032671442085465843940709084719667865761125514800243342061732684028802646193202210299179139410607';
  var ELGAMAL_PRIVATE_KEY_Q_B64 =
      '12939396283335421049921068858211433233126495514407886569514225839757682339812461790679331327844644602883220990119774411868903477198509705601122060967876228374690884782515835193957431967788948058212827424653299092753997868946254919808472248036853722669403050712733694488968744510228391838051310280985322342007934383014040024686424936057526104107219736301677741547820020757730425737985559136062566612003974844221840214834045656737059437540810373459953783841199104522171826073664311433417300419939057142509409231555113555807016335721042732921970354542359833932880562757400121671030866342014401323096601105149589569705303';
  var ELGAMAL_PRIVATE_KEY_G_B64 =
      '23337993065784550228812110720552652305178266477392633588884900695706615523553977368516877521940228584865573144621632575456086035440118913707895716109366641541746808409917179478292952139273396531060021729985473121368590574110220870149822495151519706210399569901298027813383104891697930149341258267962490850297875794622068418425473578455187344232698462829084010585324877420343904740081787639502967515631687068869545665294697583750184911025514712871193837246483893950501015755683415509019863976071649325968623617568219864744389709563087949389080252971419711636380986100047871404548371112472694814597772988558887480308242';
  var ELGAMAL_PRIVATE_KEY_EXPONENT_B64 =
      '4859259447320592668819568962669961246561051865212874540681243424367801394774393725734829250015941495215558771019728908939132002055237814853650010734496771955581048260699707147620967124841995608460226547395857304134089405473568889471419858106512603895297560717994246768950866491384823569118637582497519403351819609184883390558852906911976387433283350856334618005481106148226480670760580586739976814586223883428847209532777965666220529265035157816712732743895240103023779784827727286610206045924297284258748757461471125260095895029712193814187300268672295894546694634423997510695194818714918502470470193799175083856878';

  var policy_;
  var keyStoreService_;
  var scytlKeyStore_;
  var certificateService_;
  var policyForElGamal_;
  var scytlKeyStoreWithElGamalKeys_;
  var elGamalPrivateKeyP_;
  var elGamalPrivateKeyQ_;
  var elGamalPrivateKeyG_;
  var elGamalPrivateKeyExponent_;
  var scytlKeyStoreJson_;

  beforeAll(function() {
    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);

    certificateService_ = certificate.newService();

    policyForElGamal_ = cryptoPolicy.newInstance();
    policyForElGamal_.symmetric.secretKey.encryption.lengthBytes = 32;
    policyForElGamal_.primitives.keyDerivation.pbkdf.keyLengthBytes = 16;
    policyForElGamal_.primitives.keyDerivation.pbkdf.minSaltLengthBytes = 32;
    policyForElGamal_.primitives.keyDerivation.pbkdf.hashAlgorithm = 'SHA256';
    policyForElGamal_.primitives.keyDerivation.pbkdf.numIterations = 32000;

    var keyStoreService = keyStore.newService({policy: policyForElGamal_});
    scytlKeyStoreWithElGamalKeys_ = keyStoreService.newScytlKeyStore(
        SCYTL_KEY_STORE_WITH_ELGAMAL_KEYS, PASSWORD);

    elGamalPrivateKeyP_ =
        codec.bytesToBigInteger(codec.base64Decode(ELGAMAL_PRIVATE_KEY_P_B64));
    elGamalPrivateKeyQ_ =
        codec.bytesToBigInteger(codec.base64Decode(ELGAMAL_PRIVATE_KEY_Q_B64));
    elGamalPrivateKeyG_ =
        codec.bytesToBigInteger(codec.base64Decode(ELGAMAL_PRIVATE_KEY_G_B64));
    elGamalPrivateKeyExponent_ = codec.bytesToBigInteger(
        codec.base64Decode(ELGAMAL_PRIVATE_KEY_EXPONENT_B64));

    scytlKeyStoreJson_ = JSON.stringify(SCYTL_KEY_STORE);
  });

  describe('create a key store service that should be able to ..', function() {
    describe('load a Scytl key store that should be able to', function() {
      it('retrieve one or more private keys', function() {
        var privateKey1Pem =
            scytlKeyStore_.getPrivateKey(PRIVATE_KEY_1_ALIAS, PASSWORD);
        expect(privateKey1Pem).toBeDefined();

        var privateKey2Pem =
            scytlKeyStore_.getPrivateKey(PRIVATE_KEY_2_ALIAS, PASSWORD);
        expect(privateKey2Pem).toBeDefined();
      });

      it('retrieve a private key, using a specified PBKDF service object',
         function() {
           var keyStoreService = keyStore.newService({
             policy: policy_,
             pbkdfService: pbkdf.newService({policy: policy_})
           });
           var scytlKeyStore =
               keyStoreService.newScytlKeyStore(SCYTL_KEY_STORE, PASSWORD);

           var privateKey1Pem =
               scytlKeyStore.getPrivateKey(PRIVATE_KEY_1_ALIAS, PASSWORD);
           expect(privateKey1Pem).toBeDefined();
         });

      it('retrieve one or more certificates', function() {
        var certificate1Pem =
            scytlKeyStore_.getCertificate(PRIVATE_KEY_1_ALIAS);
        expect(certificate1Pem).toBeDefined();

        var certificate2Pem =
            scytlKeyStore_.getCertificate(PRIVATE_KEY_2_ALIAS);
        expect(certificate2Pem).toBeDefined();
      });

      it('retrieve a certificate by subject', function() {
        var certificatePem =
            scytlKeyStore_.getCertificateBySubject(SUBJECT_COMMON_NAME);
        expect(certificatePem).toBeDefined();

        var certificate =
            certificateService_.newX509Certificate(certificatePem);
        expect(certificate.subjectCommonName).toBe('SecDev');
        expect(certificate.subjectOrganizationalUnit).toBe('QA');
        expect(certificate.subjectOrganization).toBe('Scytl');
        expect(certificate.subjectCountry).toBe('ES');
        expect(certificate.verify(certificatePem)).toBe(true);
      });

      it('retrieve one or more certificate chains', function() {
        var certificateChain1 =
            scytlKeyStore_.getCertificateChain(PRIVATE_KEY_1_ALIAS);
        expect(certificateChain1).toBeDefined();
        expect(certificateChain1.length).toBe(3);

        var certificateChain2 =
            scytlKeyStore_.getCertificateChain(PRIVATE_KEY_2_ALIAS);
        expect(certificateChain2).toBeDefined();
        expect(certificateChain2.length).toBe(4);

        var rootCertificate =
            certificateService_.newX509Certificate(certificateChain1[2]);
        var intermediateCertificate =
            certificateService_.newX509Certificate(certificateChain1[1]);
        var leafCertificate =
            certificateService_.newX509Certificate(certificateChain1[0]);

        expect(rootCertificate.subjectCommonName).toBe('SecDev');
        expect(intermediateCertificate.subjectCommonName).toBe('SecQA');
        expect(leafCertificate.subjectCommonName).toBe('SecEndEntity');

        expect(rootCertificate.verify(certificateChain1[2])).toBe(true);
        expect(rootCertificate.verify(certificateChain1[1])).toBe(true);
        expect(intermediateCertificate.verify(certificateChain1[0])).toBe(true);
      });

      it('retrieve one or more secret keys', function() {
        var secretKey1 =
            scytlKeyStore_.getSecretKey(SECRET_KEY_1_ALIAS, PASSWORD);
        expect(secretKey1).toBeDefined();
        expect(secretKey1.length).toBe(16);

        var secretKey2 =
            scytlKeyStore_.getSecretKey(SECRET_KEY_2_ALIAS, PASSWORD);
        expect(secretKey2).toBeDefined();
        expect(secretKey2.length).toBe(16);
      });

      it('retrieve a secret key, using a specified symmetric cryptography service object',
         function() {
           var keyStoreService = keyStore.newService({
             policy: policy_,
             symmetricCryptographyService:
                 symmetric.newService({policy: policy_})
           });
           var scytlKeyStore =
               keyStoreService.newScytlKeyStore(SCYTL_KEY_STORE, PASSWORD);

           var secretKey1 =
               scytlKeyStore.getSecretKey(SECRET_KEY_1_ALIAS, PASSWORD);
           expect(secretKey1).toBeDefined();
           expect(secretKey1.length).toBe(16);
         });

      it('retrieve one or more ElGamal private keys', function() {
        var elGamalPrivateKey1 =
            scytlKeyStoreWithElGamalKeys_.getElGamalPrivateKey(
                ELGAMAL_PRIVATE_KEY_1_ALIAS, PASSWORD);
        expect(elGamalPrivateKey1).toBeDefined();

        var elGamalPrivateKey2 =
            scytlKeyStoreWithElGamalKeys_.getElGamalPrivateKey(
                ELGAMAL_PRIVATE_KEY_2_ALIAS, PASSWORD);
        expect(elGamalPrivateKey2).toBeDefined();

        var group = elGamalPrivateKey1.group;
        expect(group.p.toString()).toEqual(elGamalPrivateKeyP_.toString());
        expect(group.q.toString()).toEqual(elGamalPrivateKeyQ_.toString());
        expect(group.generator.value.toString())
            .toEqual(elGamalPrivateKeyG_.toString());
        var exponents = elGamalPrivateKey1.exponents;
        expect(exponents.length).toBe(1);
        expect(exponents[0].value.toString())
            .toEqual(elGamalPrivateKeyExponent_.toString());
      });

      it('retrieve an ElGamal private key, using a specified ElGamal service object',
         function() {
           var keyStoreService = keyStore.newService({
             policy: policyForElGamal_,
             elGamalService: elGamal.newService()
           });
           var scytlKeyStoreWithElGamalKeys = keyStoreService.newScytlKeyStore(
               SCYTL_KEY_STORE_WITH_ELGAMAL_KEYS, PASSWORD);

           var elGamalPrivateKey1 =
               scytlKeyStoreWithElGamalKeys.getElGamalPrivateKey(
                   ELGAMAL_PRIVATE_KEY_1_ALIAS, PASSWORD);
           expect(elGamalPrivateKey1).toBeDefined();
         });
    });

    it('load a Scytl key store, in JSON format', function() {
      var scytlKeyStore = keyStoreService_.newScytlKeyStore(
          JSON.stringify(SCYTL_KEY_STORE), PASSWORD);
      expect(scytlKeyStore).toBeDefined();

      // Check private keys.
      var privateKey1Pem =
          scytlKeyStore_.getPrivateKey(PRIVATE_KEY_1_ALIAS, PASSWORD);
      expect(privateKey1Pem).toBeDefined();
      var privateKey2Pem =
          scytlKeyStore_.getPrivateKey(PRIVATE_KEY_2_ALIAS, PASSWORD);
      expect(privateKey2Pem).toBeDefined();

      // Check certificates.
      var certificate1Pem = scytlKeyStore_.getCertificate(PRIVATE_KEY_1_ALIAS);
      expect(certificate1Pem).toBeDefined();
      var certificate2Pem = scytlKeyStore_.getCertificate(PRIVATE_KEY_2_ALIAS);
      expect(certificate2Pem).toBeDefined();

      // Check secret keys.
      var secretKey1 =
          scytlKeyStore_.getSecretKey(SECRET_KEY_1_ALIAS, PASSWORD);
      expect(secretKey1).toBeDefined();
      expect(secretKey1.length).toBe(16);
      var secretKey2 =
          scytlKeyStore_.getSecretKey(SECRET_KEY_2_ALIAS, PASSWORD);
      expect(secretKey2).toBeDefined();
      expect(secretKey2.length).toBe(16);

      var keyStoreService = keyStore.newService({policy: policyForElGamal_});
      var scytlKeyStoreWithElGamalKeys = keyStoreService.newScytlKeyStore(
          JSON.stringify(SCYTL_KEY_STORE_WITH_ELGAMAL_KEYS), PASSWORD);
      expect(scytlKeyStoreWithElGamalKeys).toBeDefined();

      // Check ElGamal keys.
      var elGamalPrivateKey1 =
          scytlKeyStoreWithElGamalKeys.getElGamalPrivateKey(
              ELGAMAL_PRIVATE_KEY_1_ALIAS, PASSWORD);
      expect(elGamalPrivateKey1).toBeDefined();
      var elGamalPrivateKey2 =
          scytlKeyStoreWithElGamalKeys.getElGamalPrivateKey(
              ELGAMAL_PRIVATE_KEY_2_ALIAS, PASSWORD);
      expect(elGamalPrivateKey2).toBeDefined();
    });
  });
});
