/*
 * 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
 */
describe('Stores', function() {
  'use strict';

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

  describe('sks', function() {
    var data = {
      salt: 'CpHq+38iqF3Ls7j97qBQ2A9Diy8=',
      secrets: {},
      store:
          '[encoded data, 7800 characters]'
    };
    describe('sks content', function() {
      it('throws an error if it does not have a property called salt',
         function() {
           cryptolib('stores.sks', function(box) {
             expect(function() {
               box.stores.sks.SksReader(Object.create(data, {salt: undefined}));
             }).toThrow();
           });
         });
    });
    describe('should have a sks keystore reader that', function() {
      it('opens a sks json object', function() {
        cryptolib('stores.sks', function(box) {
          var sksReader = new box.stores.sks.SksReader(data);
          expect(sksReader.keyStore).toBeDefined();
        });
      });

      it('opens a string containing a stringyfied sks keystore', function() {
        cryptolib('stores.sks', function(box) {
          var sksReader = new box.stores.sks.SksReader(JSON.stringify(data));
          expect(sksReader.keyStore).toBeDefined();
        });
      });

      it('throws an error when trying to open a string containing an invalid or corrupted stringyfied sks keystore',
         function() {
           cryptolib('stores.sks', function(box) {
             expect(function() {
               box.stores.sks.SksReader('corrupted keystore');
             }).toThrow();
           });
         });

      it('retrieves a secret key associated with a specified alias and password',
         function() {

           var f_main = function(box) {

             var secretKeyFactory =
                 new box.symmetric.secretkey.factory.SecretKeyFactory();

             var secretKeyB64 =
                 secretKeyFactory.getCryptoSecretKeyGeneratorForEncryption()
                     .generate();

             var passwordBase64 =
                 secretKeyFactory.getCryptoSecretKeyGeneratorForEncryption()
                     .generate();

             var prepareKeyStore = function() {
               var data = {
                 salt: 'CpHq+38iqF3Ls7j97qBQ2A9Diy8=',
                 secrets: {},
                 store:
                     '[encoded data, 7800 characters]'
               };

               var f = function(box) {

                 var cipherFactory =
                     new box.symmetric.cipher.factory.SymmetricCipherFactory();
                 var pbkdfGeneratorfactory =
                     new box.primitives.derivation.factory
                         .SecretKeyGeneratorFactory();
                 var converters = new box.commons.utils.Converters();

                 var aesCipher = cipherFactory.getCryptoSymmetricCipher();

                 var cryptoPbkdfSecretKeyGenerator =
                     pbkdfGeneratorfactory.createPBKDF();
                 var saltB64 = 'CpHq+38iqF3Ls7j97qBQ2A9Diy8=';
                 var passwordB64 = cryptoPbkdfSecretKeyGenerator.generateSecret(
                     passwordBase64, saltB64);

                 var aliasAndSecretKey =
                     'alias1' + converters.base64Decode(secretKeyB64);
                 var aliasAndSecretKeyB64 =
                     converters.base64Encode(aliasAndSecretKey);

                 data.secrets.alias1 =
                     aesCipher.encrypt(passwordB64, aliasAndSecretKeyB64);
               };

               f.policies = {
                 primitives: {
                   derivation: {
                     pbkdf: {
                       provider:
                           Config.primitives.derivation.pbkdf.provider.FORGE,
                       hash: Config.primitives.derivation.pbkdf.hash.SHA256,
                       saltLengthBytes: 20,
                       keyLengthBytes: 32,
                       iterations:
                           Config.primitives.derivation.pbkdf.iterations.I_1
                     }
                   }
                 },
                 symmetric: {
                   cipher: {
                     provider: Config.symmetric.cipher.provider.FORGE,
                     algorithm: {
                       AES128_GCM: {
                         name:
                             Config.symmetric.cipher.algorithm.AES128_GCM.name,
                         keyLengthBytes: 32,
                         tagLengthBytes: Config.symmetric.cipher.algorithm
                                             .AES128_GCM.tagLengthBytes
                       }
                     },
                     initializationVectorLengthBytes:
                         Config.symmetric.cipher.initializationVectorLengthBytes
                             .IV_12,
                     secureRandom: {
                       provider:
                           Config.symmetric.cipher.secureRandom.provider.SCYTL
                     }
                   }
                 }
               };

               cryptolib(
                   'symmetric.cipher', 'commons.utils', 'primitives.derivation',
                   f);

               return data;
             };

             var data = prepareKeyStore();
             var sksReader = new box.stores.sks.SksReader(data);
             var retrievedSecretKey =
                 sksReader.getSecretKey('alias1', passwordBase64);

             expect(retrievedSecretKey).toEqual(secretKeyB64);
           };

           f_main.policies = {
             symmetric: {
               secretkey: {
                 encryption: {lengthBytes: 32},
                 mac: {
                   lengthBytes: Config.symmetric.secretkey.mac.lengthBytes.SK_32
                 },
                 secureRandom: {
                   provider:
                       Config.symmetric.secretkey.secureRandom.provider.SCYTL
                 }
               },
               cipher: {
                 provider: Config.symmetric.cipher.provider.FORGE,
                 algorithm: {
                   AES128_GCM: {
                     name: Config.symmetric.cipher.algorithm.AES128_GCM.name,
                     keyLengthBytes: 32,
                     tagLengthBytes: Config.symmetric.cipher.algorithm
                                         .AES128_GCM.tagLengthBytes
                   }
                 },
                 initializationVectorLengthBytes:
                     Config.symmetric.cipher.initializationVectorLengthBytes
                         .IV_12,
                 secureRandom: {
                   provider: Config.symmetric.cipher.secureRandom.provider.SCYTL
                 }
               }
             },
             primitives: {
               derivation: {
                 pbkdf: {
                   provider: Config.primitives.derivation.pbkdf.provider.FORGE,
                   hash: Config.primitives.derivation.pbkdf.hash.SHA256,
                   saltLengthBytes: 20,
                   keyLengthBytes:
                       Config.primitives.derivation.pbkdf.keyLengthBytes.KL_32,
                   iterations: Config.primitives.derivation.pbkdf.iterations.I_1
                 }
               }
             }
           };

           cryptolib(
               'stores.sks', 'commons.utils', 'symmetric.secretkey', f_main);
         });

      it('throws an error if you try to retrieve a secret key providing an alias not defined in the keyStore',
         function() {
           cryptolib('stores.sks', function(box) {
             var sksReader = new box.stores.sks.SksReader(data);

             var thrownError = false;
             try {
               sksReader.getSecretKey('alias2');
             } catch (error) {
               thrownError = true;
             }

             expect(thrownError).toBeTruthy();
           });
         });

      it('retrieves a secret key and a private key from a real .sks key store',
         function() {

           var f_main = function(box) {
             var data = {
               'salt': 'xwZO9BDmu+rREWpt3lGWsWL8Pp9lHYf+5upIllTmWKw=',
               'secrets': {
                 'symmetric':
                     '4ok9VK1L+DNRYACKC2CpyEFaIr8HDAA8L4UFCO/luSjO/5O8XFPeYMx03PUEHZ3kVy/+3vM='
               },
               'store':
                   '[encoded data, 9276 characters]'
             };

             var sksReader = new box.stores.sks.SksReader(data);

             var passwordBase64 = 'UUtKTzZLT1ZCQkY0VzNaWVpPN0M2V1IzRzY=';

             var retrievedSecretKeyB64 =
                 sksReader.getSecretKey('symmetric', passwordBase64);

             expect(retrievedSecretKeyB64).toBeDefined();

             var converters = new box.commons.utils.Converters();

             var secretKey = converters.base64Decode(retrievedSecretKeyB64);

             expect(secretKey.length === 16).toBeTruthy();

             var privateKeyChain = sksReader.getPrivateKeyChain(passwordBase64);

             expect(privateKeyChain).toBeDefined();

             for (var key in privateKeyChain.map) {
               if (typeof key !== 'undefined') {
                 expect(privateKeyChain.map[key]).toBeDefined();
               }
             }
           };

           f_main.policies = {
             symmetric: {
               secretkey: {
                 encryption: {lengthBytes: 16},
                 mac: {
                   lengthBytes: Config.symmetric.secretkey.mac.lengthBytes.SK_32
                 },
                 secureRandom: {
                   provider:
                       Config.symmetric.secretkey.secureRandom.provider.SCYTL
                 }
               },
               cipher: {
                 provider: Config.symmetric.cipher.provider.FORGE,
                 algorithm: {
                   AES128_GCM: {
                     name: Config.symmetric.cipher.algorithm.AES128_GCM.name,
                     keyLengthBytes: 16,
                     tagLengthBytes: Config.symmetric.cipher.algorithm
                                         .AES128_GCM.tagLengthBytes
                   }
                 },
                 initializationVectorLengthBytes:
                     Config.symmetric.cipher.initializationVectorLengthBytes
                         .IV_12,
                 secureRandom: {
                   provider: Config.symmetric.cipher.secureRandom.provider.SCYTL
                 }
               }
             },
             primitives: {
               derivation: {
                 pbkdf: {
                   provider: Config.primitives.derivation.pbkdf.provider.FORGE,
                   hash: Config.primitives.derivation.pbkdf.hash.SHA256,
                   saltLengthBytes: 32,
                   keyLengthBytes:
                       Config.primitives.derivation.pbkdf.keyLengthBytes.KL_16,
                   iterations:
                       Config.primitives.derivation.pbkdf.iterations.I_32000
                 }
               }
             }
           };

           cryptolib('stores.sks', 'commons.utils', f_main);
         });

      it('retrieves an ElGamal private key from a real .sks key store JSON', function() {

        var f_main = function(box) {
          var data = {
            'salt': '7Q951QtBynbVfljodmtQmg351nq/kmxcHfCwdrTUdng=',
            'egPrivKeys': {
              'elgamalprivatekeyalias1':
                  'HA5aI6+JCB1/AIeb70bcMetz+1AuY9zTh4GJAS2Xr9A1EN6N5qj9pLCwXneozTyuOi8mp0sHg1bZfWKuehBH+8kjYDlKzexbqmXiDhf2jw7mX991x0KVpeQ7oJJMGIPAv+Ff3lGHZxZYEdAy72Nttt5HZWkX7jPGI9ZeqT8aQhB3mNuGMhFnfMFMdEFGr9AVa8ZeXfLVpuVkGoHVj8mkN9PHUy+NLQc3dyh1ggfHNrUlYD21SOEp+4GTNxVNjsMPiOhi7LTHabXfliq3KdVltpo2IAZ0uvlmIj2cxZC+qFXv1BNiypPYkPRYSCfthWjhkz8iUSJUek5ve5sewVAMFZc5cRXEesvP5AH7tRwmv+63Ht7ktv8W5SKCm+ycVmqx3Tzo4NEEsxDqwBilTLhbcokANljO3iyR3HgVIrvwEJGMm79AfH8RYe5rhCUbLZ/KF+XCVm3TZ2ux0zWgynZi7d/1rzqae9nM5vBfdK6YMmT+AuEoXxKDzvV/9cqlRNg+EWF+vpfIit203fSiSv8fF6WZ3TSSUQ/oR6I56i5PWO/MBT3iYz++iwSO1faKNG762p7OY7gnG0V/EICTVtXn2pg+p78beAISpNL/FNxntVKIdg2dAXwaRcSWb9V5/zrN8FTQgIKEMzFI/lWZUT4N0R+S08gdYfRIkxc3Olp2vXUV/rt5b7oc6jhHJo4Z37m3FSm7LFsrmVvzh5PGA0/gFfWbuZyPngjqoPBNJFtbFI4dqG+H8hOzOg9/KAx41qNPuUGWjuVatr0nE2OdWLxaOe8eQ4exykL4xsS/s2bkrmynXJTCVKm9mNudmMUfdWE7Y3PxWtx3TnQpHGqIb0VbQRJabTzwHyyYBTqAmxsLO20VoCagPDJscPJNcBvGgYyKcjzjjPo+aaAHSfHiqUdOluN5+sVNfy6ifUkIh5VBeoH6+s3Xhj0xPLLIpFIz8RA3y5lB7l19mFjw/T+pyq8dCWlx3xD8XgY9K/kWxQF8ERvZ2Qu/zHySV8ybXB3TKXJsmZNeNyFFCSZs6uvw+XhupaG+LuOO4+FWTD3PpCMkeGXGJa4zCsnkMu/Hgi4ZTVvOxO27BwZz08/x9y76dpQcE1nt0IB2KMNuwD82m9cO77meHBp/LgsMQmA9G/+5i/mW6oq9ZlxlsE4FoA7ElJR2X7V2DXMwDSQPSuOScUjZCfsd4hPHW7+Q/INzBmIbBqvlaE7yjJ6Rt23UbfKgjnNL8891KE9qgnlxAZ3M77YuQpMLKWTmYpQH3fcrBFv6QRV1aUaMhDL1Fjnzn6J/sbM4l0JysJrMigRmHy6I5PWJDQTeuzEDtFAyd9E7sOv5/rdIBHvtQlUBy+4PTPPoGv+idkIKKZ04yglwGTWxtkKsAvt1CyQExj/q7Wghf1xWLoW3/hpoYqRtSOSIgQUpFEsjW2SY1kR/5EdudS66gfFbsABrNheNHSddXUIfB1QlqYBdIarUq1Np8rlZ0XywLxrVlqUmnLI9oXQbkvkq1NhD9XlbEYu+ROZFXZADytu9K4KInkqAZXz9HB/QBncTRmPRS6onM+kbjKLImtvlEJ6EJt2I46nIUVM2d73MTPLNWaUNkuFq9u+oZ/Dn1ozgEoeqCJtHtMtl33FUu1Nsl6LtJ6bbKtNR6XlCgedbWtUX0Qli8RDw6sb/zrRZ9lBqNw/8Nfg5zn6CBdcqwRgVwNc48VJ+g6X35quwKFlUyLEFi4DvUN46n1FbE09PACVJu2uYZ8AI3bF1jpPZbMVcPcQEHI0WhUCg9+HyoQrOl8X7h2uwnxxaClGujvT1INXLWwIosqg/73WvC+kl78lOC4cczc6lzuKZoWLAxpOZ8gpRxWF+BT2mo63Ulr9aIhoeh9rTxecWyk2NVQ+tPm6WfMW7Uq21H5sLDFRKE1zkQJsMZiJDfqyY0osrCJsY/Y9wq/HrlEf/GTe9zrMEC1ZZheDqKO7zwdIs8iubKv6n+pSJSifjXjAsv29HDpF/vycVy7HzlouloQEJFZqpXxTawVcOpf6/pwxuO3CbTtpAJFdsI2hf84/bV5ZhW+vAvIlGkINMr+oshNA4FKvDr1nC6bXAU9swfZoGyJUuvVm6vLlAL49JZGWpkfcrOyaSFP15p4kPRfzv63hV57QPImx55QPfpbPUXQeW512TcZTsNX8UYNVNny2FSZXckQQaArkrLwqVmjKhNwrzEVc2WuAcbthcm8dsSpjsDSaJDmS2xVK7flmQTTou5xHJppWyUeeY/bo/vBE6oG6v5QFzwDcnyPXXy7cZnUvEzk+IVNZCqMQ4ZNjMW4mMiR+TJKCQ4ro5QEqCWFkSGtOrerZTkthacwqS0UTVorFxrZEPZwKGsMSmMEA44KhcxN2jgiyc/Aed3oyEKWbmtMcd8eaqiKEFCaU+v31/WL1vBi5zxEOCsJUPjbgcESlVzwSttE9RRVwoljMoZDoMDPEC3x9aOWz03bGn2Ky7XiLgPzAi2FN6EIWoPnqI1KylTdN51gSqv8P79CzhSQzROFpB4AunA6K7qPCM4UYvuPReKmkNJzON5FnMBJTUWT6PNISXdKdbFIgdRkF0d7rwF1a3H13fXG4NginqYtzNoVpPfBHlXAWU8dKOyIb0tvTPeqAfUZ0iXTXsTuVmqSuhaPANWPYOuFZwB84TvUhq+Azj4S1kJN1K0Fo3DwLaRYTcAkbZOEuNR33e8kRGsmfH7aU3JsKZJJBffY88CbY5fclNG7HUn59kI3L8Mxuvntb7KGed3VNqNxKaCwT7XJJcIo4XBl6xdzuwzlaSsdsd30Ci6uFip2ootnemCNJwtgoVLXFJe0pJ+JVOU1LMcFJdWTf6fuwDndzI+RWFwcoc30FxmEBJ2fF/vBvlSqgtRpISAeq/y7auPFn7+GKJZspuIDJqkXdOKxCm2ChmiqZ0ZPZRX6vU+zHxCN536eauE7IY1khuAOBZsxPweJg0RKm3+rB3fc4amlneTV/Pwc0CCLEFY1tVouaCe9GSzy1tzNCN8c5j5YVUWAjsgvXcmnzSAsQYvn1cjt+BPr3B+nnbx1oEJgi1dmF8NXm7qCg0KGTPqc5+kKpJh/egtsbxBNVeb12MmFPXzrockM6+ZlyOSdHxdFYUxlFaBnEs8MHTOXgoCpU93U/3uu7l+Kqm+i4ErpGliyYqguvMizxAHk8jckmzoMkcztvFFv18qlgG22Nz1BqwCobAc6DT7QQIlAQsQMY0d1QMlcxH35aTN5kS0/G2EDZ3ZG5AQEwQmczf4kNVWwk62XhTRcKcqdRRX/7h6OOMDc1nP5/GmcderDbaRFV28eFrzi4Mjjcjzpz889ttmHXPN7D8AvbTtHqRm4ujAFAvYbhU5hguY7x7BP+OOB01jvOM/F1U7Zzgg12FQoaMvcacJECaMR1Alz22htPaK0VIxCmxZd+q/T1xAJYPNwver4QbKJX6Pg==',
              'elgamalprivatekeyalias2':
                  'vezAhuUI9Rqb9SNp9QXGpdiiKjJCGjK3MWmQbSC+NV+4HYEKKuCP6N9RuKAprssqmGYDWUc3ecYD6JsRlW2PNnbAv0oK/aGGogh3gqrVDLqBqSet1hmnN3NImAmXscpioAAnfPd1RR4V2lv8+Lc3aUPKTvNwl1P3jATM3TViGzb0hxMhsCuUwFOX'
            },
            'store':
                'MFUCAQMwEQYJKoZIhvcNAQcBoAQEAjAAMD0wITAJBgUrDgMCGgUABBR+zoG7NsLvE8zqUFX7HXeP/yK07gQUQaC4BDlbcjaPJxp4KTcJh9wSzMMCAgQA'
          };
          var sksReader = new box.stores.sks.SksReader(data);

          var converters = new box.commons.utils.Converters();
          var passwordBase64 =
              converters.base64Encode('01234567890abcdefghijk');

          var retrievedElGamalPrivateKey = sksReader.getElGamalPrivateKey(
              'elgamalprivatekeyalias1', passwordBase64);

          expect(retrievedElGamalPrivateKey).toBeDefined();

          var group = retrievedElGamalPrivateKey.getGroup();

          var elGamalPrivKeyP =
              '25878792566670842099842137716422866466252991028815773139028451679515364679624923581358662655689289205766441980239548823737806954397019411202244121935752456749381769565031670387914863935577896116425654849306598185507995737892509839616944496073707445338806101425467388977937489020456783676102620561970644684015868766028080049372849872115052208214439472603355483095640041515460851475971118272125133224007949688443680429668091313474118875081620746919907567682398209044343652147328622866834600839878114285018818463110227111614032671442085465843940709084719667865761125514800243342061732684028802646193202210299179139410607';
          var elGamalPrivKeyQ =
              '12939396283335421049921068858211433233126495514407886569514225839757682339812461790679331327844644602883220990119774411868903477198509705601122060967876228374690884782515835193957431967788948058212827424653299092753997868946254919808472248036853722669403050712733694488968744510228391838051310280985322342007934383014040024686424936057526104107219736301677741547820020757730425737985559136062566612003974844221840214834045656737059437540810373459953783841199104522171826073664311433417300419939057142509409231555113555807016335721042732921970354542359833932880562757400121671030866342014401323096601105149589569705303';
          var elGamalPrivKeyG =
              '23337993065784550228812110720552652305178266477392633588884900695706615523553977368516877521940228584865573144621632575456086035440118913707895716109366641541746808409917179478292952139273396531060021729985473121368590574110220870149822495151519706210399569901298027813383104891697930149341258267962490850297875794622068418425473578455187344232698462829084010585324877420343904740081787639502967515631687068869545665294697583750184911025514712871193837246483893950501015755683415509019863976071649325968623617568219864744389709563087949389080252971419711636380986100047871404548371112472694814597772988558887480308242';
          var elGamalPrivKeyExponent =
              '4859259447320592668819568962669961246561051865212874540681243424367801394774393725734829250015941495215558771019728908939132002055237814853650010734496771955581048260699707147620967124841995608460226547395857304134089405473568889471419858106512603895297560717994246768950866491384823569118637582497519403351819609184883390558852906911976387433283350856334618005481106148226480670760580586739976814586223883428847209532777965666220529265035157816712732743895240103023779784827727286610206045924297284258748757461471125260095895029712193814187300268672295894546694634423997510695194818714918502470470193799175083856878';

          expect(group.getP().toString())
              .toEqual(
                  converters.base64ToBigInteger(elGamalPrivKeyP).toString());
          expect(group.getQ().toString())
              .toEqual(
                  converters.base64ToBigInteger(elGamalPrivKeyQ).toString());
          expect(group.getGenerator().getElementValue().toString())
              .toEqual(
                  converters.base64ToBigInteger(elGamalPrivKeyG).toString());

          var exponents = retrievedElGamalPrivateKey.getExponentsArray();
          expect(exponents.length).toBe(1);
          expect(exponents[0].getValue().toString())
              .toEqual(converters.base64ToBigInteger(elGamalPrivKeyExponent)
                           .toString());
        };

        f_main.policies = {
          symmetric: {
            secretkey: {
              encryption: {lengthBytes: 16},
              mac: {
                lengthBytes: Config.symmetric.secretkey.mac.lengthBytes.SK_32
              },
              secureRandom: {
                provider: Config.symmetric.secretkey.secureRandom.provider.SCYTL
              }
            },
            cipher: {
              provider: Config.symmetric.cipher.provider.FORGE,
              algorithm: {
                AES128_GCM: {
                  name: Config.symmetric.cipher.algorithm.AES128_GCM.name,
                  keyLengthBytes: 16,
                  tagLengthBytes: Config.symmetric.cipher.algorithm.AES128_GCM
                                      .tagLengthBytes
                }
              },
              initializationVectorLengthBytes:
                  Config.symmetric.cipher.initializationVectorLengthBytes.IV_12,
              secureRandom: {
                provider: Config.symmetric.cipher.secureRandom.provider.SCYTL
              }
            }
          },
          primitives: {
            derivation: {
              pbkdf: {
                provider: Config.primitives.derivation.pbkdf.provider.FORGE,
                hash: Config.primitives.derivation.pbkdf.hash.SHA256,
                saltLengthBytes: 32,
                keyLengthBytes:
                    Config.primitives.derivation.pbkdf.keyLengthBytes.KL_16,
                iterations:
                    Config.primitives.derivation.pbkdf.iterations.I_32000
              }
            }
          }
        };

        cryptolib('stores.sks', 'commons.utils', f_main);
      });

      it('opens a keystore and gets the full certificate chain', function() {

        var f_main = function(box) {
          var data = testKeystore;

          // open the keystore
          var sksReader = new box.stores.sks.SksReader(data);

          var converters = new box.commons.utils.Converters();
          var passwordBase64 =
              converters.base64Encode('01234567890abcdefghijk');

          // get all certificate chains
          var certificateChain = sksReader.getCertificateChain(passwordBase64);

          expect(certificateChain.length).toEqual(7);
        };

        f_main.policies = {
          symmetric: {
            secretkey: {
              encryption: {lengthBytes: 32},
              mac: {
                lengthBytes: Config.symmetric.secretkey.mac.lengthBytes.SK_32
              },
              secureRandom: {
                provider: Config.symmetric.secretkey.secureRandom.provider.SCYTL
              }
            },
            cipher: {
              provider: Config.symmetric.cipher.provider.FORGE,
              algorithm: {
                AES128_GCM: {
                  name: Config.symmetric.cipher.algorithm.AES128_GCM.name,
                  keyLengthBytes: 16,
                  tagLengthBytes: Config.symmetric.cipher.algorithm.AES128_GCM
                                      .tagLengthBytes
                }
              },
              initializationVectorLengthBytes:
                  Config.symmetric.cipher.initializationVectorLengthBytes.IV_12,
              secureRandom: {
                provider: Config.symmetric.cipher.secureRandom.provider.SCYTL
              }
            }
          },
          primitives: {
            derivation: {
              pbkdf: {
                provider: Config.primitives.derivation.pbkdf.provider.FORGE,
                hash: Config.primitives.derivation.pbkdf.hash.SHA256,
                saltLengthBytes: 32,
                keyLengthBytes:
                    Config.primitives.derivation.pbkdf.keyLengthBytes.KL_16,
                iterations: Config.primitives.derivation.pbkdf.iterations.I_1
              }
            }
          }
        };

        cryptolib('stores.sks', 'commons.utils', f_main);
      });

      it('opens a keystore and gets a certificate chain, by private key alias',
         function() {

           var f_main = function(box) {
             var data = testKeystore;

             // open the keystore
             var sksReader = new box.stores.sks.SksReader(data);

             var converters = new box.commons.utils.Converters();
             var passwordBase64 =
                 converters.base64Encode('01234567890abcdefghijk');

             // get all certificate chains
             var user1CertificateChain =
                 sksReader.getCertificateChainByAlias(passwordBase64, 'user1');
             var user2CertificateChain =
                 sksReader.getCertificateChainByAlias(passwordBase64, 'user2');

             expect(user1CertificateChain.length).toEqual(3);
             expect(user2CertificateChain.length).toEqual(4);
           };

           f_main.policies = {
             symmetric: {
               secretkey: {
                 encryption: {lengthBytes: 32},
                 mac: {
                   lengthBytes: Config.symmetric.secretkey.mac.lengthBytes.SK_32
                 },
                 secureRandom: {
                   provider:
                       Config.symmetric.secretkey.secureRandom.provider.SCYTL
                 }
               },
               cipher: {
                 provider: Config.symmetric.cipher.provider.FORGE,
                 algorithm: {
                   AES128_GCM: {
                     name: Config.symmetric.cipher.algorithm.AES128_GCM.name,
                     keyLengthBytes: 16,
                     tagLengthBytes: Config.symmetric.cipher.algorithm
                                         .AES128_GCM.tagLengthBytes
                   }
                 },
                 initializationVectorLengthBytes:
                     Config.symmetric.cipher.initializationVectorLengthBytes
                         .IV_12,
                 secureRandom: {
                   provider: Config.symmetric.cipher.secureRandom.provider.SCYTL
                 }
               }
             },
             primitives: {
               derivation: {
                 pbkdf: {
                   provider: Config.primitives.derivation.pbkdf.provider.FORGE,
                   hash: Config.primitives.derivation.pbkdf.hash.SHA256,
                   saltLengthBytes: 20,
                   keyLengthBytes:
                       Config.primitives.derivation.pbkdf.keyLengthBytes.KL_16,
                   iterations: Config.primitives.derivation.pbkdf.iterations.I_1
                 }
               }
             }
           };
           cryptolib('stores.sks', 'commons.utils', f_main);
         });

      it('opens a keystore and gets a certificate, by subject common name',
         function() {

           var f_main = function(box) {
             var data = testKeystore;

             var sksReader = new box.stores.sks.SksReader(data);

             var converters = new box.commons.utils.Converters();
             var passwordBase64 =
                 converters.base64Encode('01234567890abcdefghijk');

             // retrieve certificate from key store, with subject common name
             // 'SecDev'.
             var certificatePem =
                 sksReader.getCertificateBySubject(passwordBase64, 'SecDev');

             // verify contents of certificate (which is self-signed in this
             // example).
             var certificate =
                 new box.certificates.service.load(certificatePem);
             expect(certificate.getSubjectCN()).toBe('SecDev');
             expect(certificate.getSubjectOrgUnit()).toBe('QA');
             expect(certificate.getSubjectOrg()).toBe('Scytl');
             expect(certificate.getSubjectCountry()).toBe('ES');
             expect(certificate.verify(certificatePem)).toBe(true);
           };

           f_main.policies = {
             symmetric: {
               secretkey: {
                 encryption: {lengthBytes: 32},
                 mac: {
                   lengthBytes: Config.symmetric.secretkey.mac.lengthBytes.SK_32
                 },
                 secureRandom: {
                   provider:
                       Config.symmetric.secretkey.secureRandom.provider.SCYTL
                 }
               },
               cipher: {
                 provider: Config.symmetric.cipher.provider.FORGE,
                 algorithm: {
                   AES128_GCM: {
                     name: Config.symmetric.cipher.algorithm.AES128_GCM.name,
                     keyLengthBytes: 16,
                     tagLengthBytes: Config.symmetric.cipher.algorithm
                                         .AES128_GCM.tagLengthBytes
                   }
                 },
                 initializationVectorLengthBytes:
                     Config.symmetric.cipher.initializationVectorLengthBytes
                         .IV_12,
                 secureRandom: {
                   provider: Config.symmetric.cipher.secureRandom.provider.SCYTL
                 }
               }
             },
             primitives: {
               derivation: {
                 pbkdf: {
                   provider: Config.primitives.derivation.pbkdf.provider.FORGE,
                   hash: Config.primitives.derivation.pbkdf.hash.SHA256,
                   saltLengthBytes: 20,
                   keyLengthBytes:
                       Config.primitives.derivation.pbkdf.keyLengthBytes.KL_16,
                   iterations: Config.primitives.derivation.pbkdf.iterations.I_1
                 }
               }
             }
           };

           cryptolib(
               'stores.sks', 'certificates.service', 'commons.utils', f_main);
         });

      it('should derive the key only once', function() {

        var f_main = function(box) {
          var data = testKeystore;

          var sksReader = new box.stores.sks.SksReader(data);

          var converters = new box.commons.utils.Converters();
          var passwordBase64 =
              converters.base64Encode('01234567890abcdefghijk');

          var spy = spyOn(sksReader, '_deriveKey').and.callThrough();
          // Retrieve a certificate a few times.
          for (var i = 0; i < 5; i++) {
            sksReader.getCertificateBySubject(passwordBase64, 'SecDev');
          }
          expect(spy).toHaveBeenCalledTimes(1);
        };

        f_main.policies = {
          symmetric: {
            secretkey: {
              encryption: {lengthBytes: 32},
              mac: {
                lengthBytes: Config.symmetric.secretkey.mac.lengthBytes.SK_32
              },
              secureRandom: {
                provider: Config.symmetric.secretkey.secureRandom.provider.SCYTL
              }
            },
            cipher: {
              provider: Config.symmetric.cipher.provider.FORGE,
              algorithm: {
                AES128_GCM: {
                  name: Config.symmetric.cipher.algorithm.AES128_GCM.name,
                  keyLengthBytes: 16,
                  tagLengthBytes: Config.symmetric.cipher.algorithm.AES128_GCM
                                      .tagLengthBytes
                }
              },
              initializationVectorLengthBytes:
                  Config.symmetric.cipher.initializationVectorLengthBytes.IV_12,
              secureRandom: {
                provider: Config.symmetric.cipher.secureRandom.provider.SCYTL
              }
            }
          },
          primitives: {
            derivation: {
              pbkdf: {
                provider: Config.primitives.derivation.pbkdf.provider.FORGE,
                hash: Config.primitives.derivation.pbkdf.hash.SHA256,
                saltLengthBytes: 20,
                keyLengthBytes:
                    Config.primitives.derivation.pbkdf.keyLengthBytes.KL_16,
                iterations: Config.primitives.derivation.pbkdf.iterations.I_1
              }
            }
          }
        };

        cryptolib('stores.sks', 'commons.utils', f_main);
      });
    });
  });

  describe('bugfix 102362', function() {
    var sksBug102362 = testKeystore;

    it('must retrieve a private key by alias', function() {
      cryptolibPolicies = {
        symmetric: {
          secretkey: {
            encryption: {lengthBytes: 32},
            mac:
                {lengthBytes: Config.symmetric.secretkey.mac.lengthBytes.SK_32},
            secureRandom: {
              provider: Config.symmetric.secretkey.secureRandom.provider.SCYTL
            }
          },
          cipher: {
            provider: Config.symmetric.cipher.provider.FORGE,
            algorithm: {
              AES128_GCM: {
                name: Config.symmetric.cipher.algorithm.AES128_GCM.name,
                keyLengthBytes: 16,
                tagLengthBytes:
                    Config.symmetric.cipher.algorithm.AES128_GCM.tagLengthBytes
              }
            },
            initializationVectorLengthBytes:
                Config.symmetric.cipher.initializationVectorLengthBytes.IV_12,
            secureRandom: {
              provider: Config.symmetric.cipher.secureRandom.provider.SCYTL
            }
          }
        },
        primitives: {
          derivation: {
            pbkdf: {
              provider: Config.primitives.derivation.pbkdf.provider.FORGE,
              hash: Config.primitives.derivation.pbkdf.hash.SHA256,
              saltLengthBytes: 20,
              keyLengthBytes:
                  Config.primitives.derivation.pbkdf.keyLengthBytes.KL_16,
              iterations: 1
            }
          }
        }
      };

      cryptolib('stores.sks', 'commons.utils', function(box) {
        var converters = new box.commons.utils.Converters();

        var sksReader = new box.stores.sks.SksReader(sksBug102362);
        var passwordBase64 = converters.base64Encode('01234567890abcdefghijk');


        var privateKey = sksReader.getPrivateKeys(passwordBase64, 'user1');

        expect(privateKey.user1).toBeDefined();
      });
    });

    it('must retrieve only the requested key/keys', function() {
      cryptolibPolicies = {
        symmetric: {
          secretkey: {
            encryption: {lengthBytes: 32},
            mac:
                {lengthBytes: Config.symmetric.secretkey.mac.lengthBytes.SK_32},
            secureRandom: {
              provider: Config.symmetric.secretkey.secureRandom.provider.SCYTL
            }
          },
          cipher: {
            provider: Config.symmetric.cipher.provider.FORGE,
            algorithm: {
              AES128_GCM: {
                name: Config.symmetric.cipher.algorithm.AES128_GCM.name,
                keyLengthBytes: 16,
                tagLengthBytes:
                    Config.symmetric.cipher.algorithm.AES128_GCM.tagLengthBytes
              }
            },
            initializationVectorLengthBytes:
                Config.symmetric.cipher.initializationVectorLengthBytes.IV_12,
            secureRandom: {
              provider: Config.symmetric.cipher.secureRandom.provider.SCYTL
            }
          }
        },
        primitives: {
          derivation: {
            pbkdf: {
              provider: Config.primitives.derivation.pbkdf.provider.FORGE,
              hash: Config.primitives.derivation.pbkdf.hash.SHA256,
              saltLengthBytes: 20,
              keyLengthBytes:
                  Config.primitives.derivation.pbkdf.keyLengthBytes.KL_16,
              iterations: 1
            }
          }
        }
      };

      cryptolib('stores.sks', 'commons.utils', function(box) {
        var converters = new box.commons.utils.Converters();

        var sksReader = new box.stores.sks.SksReader(sksBug102362);
        var passwordBase64 = converters.base64Encode('01234567890abcdefghijk');


        var privateKey = sksReader.getPrivateKeys(passwordBase64, 'user2');

        expect(privateKey.user1).not.toBeDefined();
      });
    });

    it('if not alias is specified, it must retrieve all the secret key in the keystore',
       function() {
         cryptolibPolicies = {
           symmetric: {
             secretkey: {
               encryption: {lengthBytes: 32},
               mac: {
                 lengthBytes: Config.symmetric.secretkey.mac.lengthBytes.SK_32
               },
               secureRandom: {
                 provider:
                     Config.symmetric.secretkey.secureRandom.provider.SCYTL
               }
             },
             cipher: {
               provider: Config.symmetric.cipher.provider.FORGE,
               algorithm: {
                 AES128_GCM: {
                   name: Config.symmetric.cipher.algorithm.AES128_GCM.name,
                   keyLengthBytes: 16,
                   tagLengthBytes: Config.symmetric.cipher.algorithm.AES128_GCM
                                       .tagLengthBytes
                 }
               },
               initializationVectorLengthBytes:
                   Config.symmetric.cipher.initializationVectorLengthBytes
                       .IV_12,
               secureRandom: {
                 provider: Config.symmetric.cipher.secureRandom.provider.SCYTL
               }
             }
           },
           primitives: {
             derivation: {
               pbkdf: {
                 provider: Config.primitives.derivation.pbkdf.provider.FORGE,
                 hash: Config.primitives.derivation.pbkdf.hash.SHA256,
                 saltLengthBytes: 20,
                 keyLengthBytes:
                     Config.primitives.derivation.pbkdf.keyLengthBytes.KL_16,
                 iterations: 1
               }
             }
           }
         };

         cryptolib('stores.sks', 'commons.utils', function(box) {
           var converters = new box.commons.utils.Converters();

           var sksReader = new box.stores.sks.SksReader(sksBug102362);
           var passwordBase64 =
               converters.base64Encode('01234567890abcdefghijk');


           var privateKey = sksReader.getPrivateKeys(passwordBase64);

           expect(privateKey.user1).toBeDefined();
           expect(privateKey.user2).toBeDefined();
         });
       });
  });

  describe('bugfix #102457', function() {
    it('sks should manage invalid symmetric aliases', function() {
      cryptolibPolicies = {
        symmetric: {
          secretkey: {
            encryption: {lengthBytes: 32},
            mac:
                {lengthBytes: Config.symmetric.secretkey.mac.lengthBytes.SK_32},
            secureRandom: {
              provider: Config.symmetric.secretkey.secureRandom.provider.SCYTL
            }
          },
          cipher: {
            provider: Config.symmetric.cipher.provider.FORGE,
            algorithm: {
              AES128_GCM: {
                name: Config.symmetric.cipher.algorithm.AES128_GCM.name,
                keyLengthBytes: 16,
                tagLengthBytes:
                    Config.symmetric.cipher.algorithm.AES128_GCM.tagLengthBytes
              }
            },
            initializationVectorLengthBytes:
                Config.symmetric.cipher.initializationVectorLengthBytes.IV_12,
            secureRandom: {
              provider: Config.symmetric.cipher.secureRandom.provider.SCYTL
            }
          }
        },
        primitives: {
          derivation: {
            pbkdf: {
              provider: Config.primitives.derivation.pbkdf.provider.FORGE,
              hash: Config.primitives.derivation.pbkdf.hash.SHA256,
              saltLengthBytes: 20,
              keyLengthBytes:
                  Config.primitives.derivation.pbkdf.keyLengthBytes.KL_16,
              iterations: Config.primitives.derivation.pbkdf.iterations.I_1
            }
          }
        }
      };

      cryptolib('stores.sks', 'commons.utils', function(box) {
        var data = testKeystore;
        var sksReader = new box.stores.sks.SksReader(data);

        var converters = new box.commons.utils.Converters();
        var passwordBase64 = converters.base64Encode('01234567890abcdefghijk');

        expect(function() {
          sksReader.getSecretKey('werypvqwenprqv', passwordBase64);
        }).toThrow();
      });
    });
  });
});
