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

  it('should be defined', function() {
    cryptolib('asymmetric.cipher', function(box) {

      var cipherFactory =
          new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

      var rsaCipher = cipherFactory.getCryptoAsymmetricCipher();

      expect(rsaCipher).toBeDefined();
    });
  });

  describe('should be able to ..', function() {
    var plaintext = 'Ox2fUJq1gAbX';
    var privateKeyPem =
        '-----BEGIN RSA PRIVATE KEY-----MIIEowIBAAKCAQEAglypZU45bnf2opnRI+Y51VqouBpvDT33xIB/OtbwKzwVpi+JrjBFtfk33tE9t/dSRs79CK94HRhCWcOiLa2qWPrjeZ9SBiEyScrhIvRZVBF41zBgwQNuRvJCsKmAqlZaFNJDZxEP4repmlBn1CfVFmfrXmOKqwP5F7l9ZtucveRzsfmF1yVPFkW8TMuB3YqMiyymyqHlS8ujCsu5I8tpgPbwuxdMOY94fNhSXrYkY8IuX1g1zdq/Z1jluOaR/UqK4UpnbuJaH/F0VgDNiWh6cTD0DFGEk0b70i5wU4Q3L/S6XZQRvSuADoCbhwBKuFL5pW5n865oLVb5S3wuVdWaGwIDAQABAoIBAC/tn34Wf3kE9BGeGc1oFLVDaqqdVVz5/oEpeR2J7q0GnzMFYUpAhzC7WvY52cYsUPyll1Q9Jx0TUTmteo/uvKWQQFfz4nVMeS+2PoXabolBDzuWlsv/1eiRo0FOYHa/3siu8YcQN9X0DpAkpbfTmT1uoZOHZ3EuucMmOFu7vGn38Grw8bSxpR0uvTtnb8ygC+aB51y38RMyhzQQanrM8FMeAfDAy6IB0Yo7b0c50Cxa6Ax4nqn9LXyGakr5WeAMkgTIOA/GId9SZD4e5eRpq+628pOeR4O9datFltgl6r1+A4ii2VrJsDqeatGtODlX6KRKqwFHoGIa2TjgSZLuorECgYEAxeSZDOOgFsI5mB7RkRzZaQ9znJ15sgdyZiAFZAOUah4hSGdAXNAnZTlrdacduXEu3EfkpuPToX7xZSv5FRYwfBwMwCLeytlGLPjQzWejZGbo4+KqgzWb9fECDYVtDPlJ/+yLih9nt67BHweJKxYydl18rVigdVyy22X86NijSykCgYEAqKPUrXZAo+TJvmTw4tgsibJgvXBYBhmsej8mGNQw+Nyp2gV28sgm61ifIeXKS8teq+MFwGA6cHQedbsCqhMHokdhESZmlbWxhSFLihQcewBxwvrBwbaxI23yXRzwMewznZFL032PpcbqrmwFmcSSEZ3nmbvTH6ShqLW+pzDNp6MCgYBQLzdgxJ7qedqSa/JohTMG4e7rh9d2rpPJE7J7ewPZF8pOpx+qO+Gqn2COdJ+Ts2vUcAETKn9nEaPIZc/wnmQY9dioxbhWo0FPGaaphBPtq9Ez/XUv4zoFppk5V1X/isdUPsmvttf00oeIBiqrXbwmv+yz5JRn2Z7TTXjz9Ev+OQKBgQCUuoCMRzl1EgcXIqEL/0kwW6BUEqufHa9u1Ri9Vw6lvL8T6DPipMEmWK9nzuid9gtVns/ovTVtDgv7GuabplLaPQePf4WDzY11c0rSyS/hDyBFrK+LL5uEOqhAlJAGB2HyOj1clWVF+GvrTpuV5LZKUS/79pmZU7G7QCaX/0Ow7wKBgC/kDH7cmWQnWvvJ5izrx/7PogQVPOLELeUIGLu/hjsSdDKiFCxCUZ948+9NuG+DnpXDWzw//r8mPBRRGGsqFws5Aipp7yjQ3kRDCCzGelPCVhHyfmKqA+8ewXPulKS3/wIyHIvaXmsuAtTfurHtpRyzjKmCBK1Y6WQ3trIXvo7s-----END RSA PRIVATE KEY-----';
    var publicKeyPem =
        '-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAglypZU45bnf2opnRI+Y51VqouBpvDT33xIB/OtbwKzwVpi+JrjBFtfk33tE9t/dSRs79CK94HRhCWcOiLa2qWPrjeZ9SBiEyScrhIvRZVBF41zBgwQNuRvJCsKmAqlZaFNJDZxEP4repmlBn1CfVFmfrXmOKqwP5F7l9ZtucveRzsfmF1yVPFkW8TMuB3YqMiyymyqHlS8ujCsu5I8tpgPbwuxdMOY94fNhSXrYkY8IuX1g1zdq/Z1jluOaR/UqK4UpnbuJaH/F0VgDNiWh6cTD0DFGEk0b70i5wU4Q3L/S6XZQRvSuADoCbhwBKuFL5pW5n865oLVb5S3wuVdWaGwIDAQAB-----END PUBLIC KEY-----';

    ////////////////////
    //
    // RSA_KEM
    //
    ////////////////////

    it('RSA_KEM encrypt and decrypt some data - key 1024 bit - KDF1 - SHA256',
       function() {
         cryptolib('asymmetric.cipher', 'commons.utils', function(box) {

           var publicKeyPem1024 = [
             '-----BEGIN PUBLIC KEY-----',
             'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpIzTG+VAAO9tGCrqoiwJ7e3Hz',
             '2IY4Nhfvlh4ZevDje7WIqGMzg0Ewf5UBhCD1BuYklxsD/urdWH5wXT1m7tD70ZqP',
             'NjE4N6qnDmR38u0AND24p7WkT4AOCDG5hiRG5SDm6hp1vaLVEBRhUtdFb6z/m8og',
             '+6ygTIkspzesZ+RaRwIDAQAB', '-----END PUBLIC KEY-----'
           ].join('\n');

           var privateKeyPem1024 = [
             '-----BEGIN RSA PRIVATE KEY-----',
             'MIICXAIBAAKBgQCpIzTG+VAAO9tGCrqoiwJ7e3Hz2IY4Nhfvlh4ZevDje7WIqGMz',
             'g0Ewf5UBhCD1BuYklxsD/urdWH5wXT1m7tD70ZqPNjE4N6qnDmR38u0AND24p7Wk',
             'T4AOCDG5hiRG5SDm6hp1vaLVEBRhUtdFb6z/m8og+6ygTIkspzesZ+RaRwIDAQAB',
             'AoGAQZKko3+ExJJwMHd4Zl9+VuFFDISlhKV0Ii7Q/I/tVERh3NlrnE0GuQa9fhj7',
             'rgM+tnDeyG3MIIRugKGlbIKqAlWep1jGqcLw5dUy0plppB2S93BLq4XZ406iL+KS',
             '3OlJhYR9J14VcMoJjN8lGY2MYX7ywx+q5gJrREu5vBS33AECQQDUkZZX7cwyVeaB',
             'iDabdGpNxy88fIlRg1bRn29p0PmmmHcRZa0VaQm3EjbrVzg9xhd46IuBh60GGMYl',
             '4GdxiXQpAkEAy7HzJqqR+CptThAWA64hwcwWmujdjmBAJ69Scg+ZX61dpmHNiphv',
             'drpbpYu7b2yPCOI1NfkLUoRSmyd5DGao7wJAVJB2lxRrH7s8sFtYHg/6GmcbS5zf',
             'pCXz7ADZeedA6h3NgIZKjTH0Q3hjkMxp+2lK/TbGCQnIs5w3d+oGPQzJwQJBALaQ',
             '+ODYElp+FFfaHRERWlorRLt1KVa5t+aZwehPSOUzKnO8xw+Ijqa4YvneYpF8mDqb',
             'HJwSae58gNllKJ5PyOsCQHbdbSFj0aClSs0AP+jVbpzINxiObn0brcZni/UI3gul',
             'dO7vZNbWyr3h8QORN5HTg4ov4a3V8V0jlWboBZRJpvw=',
             '-----END RSA PRIVATE KEY-----'
           ].join('\n');

           var publicKey = box.forge.pki.publicKeyFromPem(publicKeyPem1024);
           var privateKey = box.forge.pki.privateKeyFromPem(privateKeyPem1024);

           // confirm that the keys have the expected modulus size
           expect(publicKey.n.bitLength()).toBe(1024);
           expect(privateKey.n.bitLength()).toBe(1024);

           // Create RSA cipher instance.
           var cipherFactory =
               new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

           var rsaCipher = cipherFactory.getCryptoAsymmetricCipher();

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

           var dataB64 = converters.base64Encode(plaintext);

           // Encrypt data and Base64 encode result.
           var encryptedDataBase64 =
               rsaCipher.encrypt(publicKeyPem1024, dataB64);

           // Base64 decode encrypted data and decrypt data.
           var decryptedDataB64 =
               rsaCipher.decrypt(privateKeyPem1024, encryptedDataBase64);

           var decryptedData = converters.base64Decode(decryptedDataB64);

           // Verify that data was successfully decrypted.
           expect(decryptedData).toBe(plaintext);
         });
       });

    it('RSA_KEM encrypt and decrypt some data - key 2048 bit - KDF2 - SHA256',
       function() {
         cryptolib('asymmetric.cipher', 'commons.utils', function(box) {

           // Create RSA cipher instance.
           var cipherFactory =
               new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

           var rsaCipher = cipherFactory.getCryptoAsymmetricCipher();

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

           var dataB64 = converters.base64Encode(plaintext);

           // Encrypt data and Base64 encode result.
           var encryptedDataBase64 = rsaCipher.encrypt(publicKeyPem, dataB64);

           // Base64 decode encrypted data and decrypt data.
           var decryptedDataB64 =
               rsaCipher.decrypt(privateKeyPem, encryptedDataBase64);

           var decryptedData = converters.base64Decode(decryptedDataB64);

           // Verify that data was successfully decrypted.
           expect(decryptedData).toBe(plaintext);
         });
       });

    it('RSA_KEM encrypt and decrypt random data (512 bytes) - key 2048 bit - KDF2 - SHA256',
       function() {

         var f = function(box) {

           var secureRandomFactory =
               new box.primitives.securerandom.factory.SecureRandomFactory();
           var secureRandom = secureRandomFactory.getCryptoRandomBytes();

           var randomBytes = secureRandom.nextRandom(512);

           var cipherFactory =
               new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

           var rsaCipher = cipherFactory.getCryptoAsymmetricCipher();

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

           var dataB64 = converters.base64Encode(randomBytes);

           // Encrypt data and Base64 encode result.
           var encryptedDataBase64 = rsaCipher.encrypt(publicKeyPem, dataB64);

           // Base64 decode encrypted data and decrypt data.
           var decryptedDataB64 =
               rsaCipher.decrypt(privateKeyPem, encryptedDataBase64);

           // Verify that data was successfully decrypted.
           expect(decryptedDataB64).toBe(dataB64);
         };

         f.policies = {
           primitives: {
             secureRandom:
                 {provider: Config.primitives.secureRandom.provider.SCYTL},
             messagedigest: {
               algorithm: Config.primitives.messagedigest.algorithm.SHA256,
               provider: Config.primitives.messagedigest.provider.FORGE
             }
           },
           asymmetric: {
             cipher: {
               algorithm: Config.asymmetric.cipher.algorithm.RSA_KEM,
               secretKeyLengthBytes: Config.asymmetric.cipher.algorithm.RSA_KEM
                                         .secretKeyLengthBytes.KL_16,
               ivLengthBytes: Config.asymmetric.cipher.algorithm.RSA_KEM
                                  .ivLengthBytes.IVL_12,
               tagLengthBytes: Config.asymmetric.cipher.algorithm.RSA_KEM
                                   .tagLengthBytes.TL_16,
               deriver:
                   Config.asymmetric.cipher.algorithm.RSA_KEM.deriver.name.KDF2,
               hash: Config.asymmetric.cipher.algorithm.RSA_KEM.deriver
                         .messagedigest.algorithm.SHA256,
               symmetricCipher:
                   Config.asymmetric.cipher.algorithm.RSA_KEM.cipher.AESGCM,
               provider: Config.asymmetric.cipher.provider.FORGE,
               secureRandom: {
                 provider: Config.asymmetric.cipher.secureRandom.provider.SCYTL
               }
             }
           }
         };

         cryptolib(
             'primitives.securerandom', 'asymmetric.cipher', 'commons.utils',
             f);
       });

    it('RSA_KEM encrypt and decrypt some data - key 2048 bit - MGF1 - SHA256',
       function() {
         cryptolib('asymmetric.cipher', 'commons.utils', function(box) {

           // Create RSA cipher instance.
           var cipherFactory =
               new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

           var rsaCipher = cipherFactory.getCryptoAsymmetricCipher();

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

           var dataB64 = converters.base64Encode(plaintext);

           // Encrypt data and Base64 encode result.
           var encryptedDataBase64 = rsaCipher.encrypt(publicKeyPem, dataB64);

           // Base64 decode encrypted data and decrypt data.
           var decryptedDataB64 =
               rsaCipher.decrypt(privateKeyPem, encryptedDataBase64);

           var decryptedData = converters.base64Decode(decryptedDataB64);

           // Verify that data was successfully decrypted.
           expect(decryptedData).toBe(plaintext);
         });
       });

    it('RSA_KEM encrypt null key, exception thrown', function() {
      cryptolib('asymmetric.cipher', 'commons.utils', function(box) {

        // Create RSA cipher instance.
        var cipherFactory =
            new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

        var rsaCipher = cipherFactory.getCryptoAsymmetricCipher();

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

        var dataB64 = converters.base64Encode(plaintext);

        expect(function() {
          rsaCipher.encrypt(null, dataB64);
        }).toThrow();
      });
    });

    it('RSA_KEM encrypt null data, exception thrown', function() {
      cryptolib('asymmetric.cipher', 'commons.utils', function(box) {

        // Create RSA cipher instance.
        var cipherFactory =
            new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

        var rsaCipher = cipherFactory.getCryptoAsymmetricCipher();

        expect(function() {
          rsaCipher.encrypt(publicKeyPem);
        }).toThrow();
      });
    });

    it('RSA_KEM decrypt null key, exception thrown', function() {
      cryptolib('asymmetric.cipher', 'commons.utils', function(box) {

        // Create RSA cipher instance.
        var cipherFactory =
            new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

        var rsaCipher = cipherFactory.getCryptoAsymmetricCipher();

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

        var dataB64 = converters.base64Encode(plaintext);

        // Encrypt data and Base64 encode result.
        var encryptedDataBase64 = rsaCipher.encrypt(publicKeyPem, dataB64);

        expect(function() {
          rsaCipher.decrypt(null, encryptedDataBase64);
        }).toThrow();
      });
    });

    it('RSA_KEM decrypt null data, exception thrown', function() {
      cryptolib('asymmetric.cipher', 'commons.utils', function(box) {

        // Create RSA cipher instance.
        var cipherFactory =
            new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

        var rsaCipher = cipherFactory.getCryptoAsymmetricCipher();

        expect(function() {
          rsaCipher.decrypt(privateKeyPem, null);
        }).toThrow();
      });
    });

    ////////////////////
    //
    // RSA_OAEP
    //
    ////////////////////
    it('RSA_OAEP encrypt and decrypt some data.', function() {
      cryptolib('asymmetric.cipher', 'commons.utils', function() {

        var f = function(box) {

          // Create RSA cipher instance.
          var cipherFactory =
              new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

          var rsaCipher = cipherFactory.getCryptoAsymmetricCipher();

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

          var dataB64 = converters.base64Encode(plaintext);
          // Encrypt data and Base64 encode result.
          var encryptedDataBase64 = rsaCipher.encrypt(publicKeyPem, dataB64);

          // Base64 decode encrypted data and decrypt data.
          var decryptedDataB64 =
              rsaCipher.decrypt(privateKeyPem, encryptedDataBase64);

          var decryptedData = converters.base64Decode(decryptedDataB64);

          // Verify that data was successfully decrypted.
          expect(decryptedData).toBe(plaintext);
        };

        f.policies = {
          asymmetric: {
            cipher: {
              algorithm: Config.asymmetric.cipher.algorithm.RSA_OAEP,
              provider: Config.asymmetric.cipher.provider.FORGE,
              secureRandom: {
                provider: Config.asymmetric.cipher.secureRandom.provider.SCYTL
              }
            }
          }
        };

        cryptolib('asymmetric.cipher', 'commons.utils', f);
      });
    });

    it('RSA_OAEP decrypt some data that was RSA encrypted using Java.',
       function() {
         cryptolib('asymmetric.cipher', 'commons.utils', function() {
           var f = function(box) {
             var asymmetricEncryptedDataBase64 =
                 'SuIt3oT4a98kF+7sReHLxYuVTV8GtspAUf2607aTIdnLkHrN5gYNNeXoOHuVXZtm2AneV165PsRtUpEapGtzaeWpNZS7rhnPU2JjQ26p0PkR0A6nXGX7sn7QlH8kJ/etN7wVilHNGRlM2nvgMpbIhue/X3+OQ6ROYavDfEs9XC8QCJxgD5CvkDJANzfNaybPdf4yZ+QFL90T4ZePEy/PZbIPtZMWIz8xTUkZs4SUbtfph+8tX4o+0b1lCBQlAeqegZQcycN0/JkgsQdRi5l02wrg0lDj4QMFzB9h3uBhzv3Wn4DlN9OQtHYMkPz/9MurRhHkqKJBSkBcTKawTmcA9Q==';
             // Create RSA cipher instance.
             var cipherFactory =
                 new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

             var rsaCipher = cipherFactory.getCryptoAsymmetricCipher();

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

             // Base64 decode encrypted data and decrypt data.
             var decryptedDataB64 = rsaCipher.decrypt(
                 privateKeyPem, asymmetricEncryptedDataBase64);

             var decryptedData = converters.base64Decode(decryptedDataB64);

             // Verify that data was successfully decrypted.
             expect(decryptedData).toBe(plaintext);
           };

           f.policies = {
             asymmetric: {
               cipher: {
                 algorithm: Config.asymmetric.cipher.algorithm.RSA_OAEP,
                 provider: Config.asymmetric.cipher.provider.FORGE,
                 secureRandom: {
                   provider:
                       Config.asymmetric.cipher.secureRandom.provider.SCYTL
                 }
               }
             }
           };

           cryptolib('asymmetric.cipher', 'commons.utils', f);
         });
       });

    it('RSA_OAEP throw an error when the given algorithm is wrong.',
       function() {

         var f = function(box) {

           var cipherFactory =
               new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

           expect(function() {
             cipherFactory.getCryptoAsymmetricCipher();
           }).toThrow();
         };

         f.policies = {
           asymmetric: {
             cipher: {
               algorithm: 'WRONG',
               provider: 'Forge',
               secureRandom: {provider: 'Scytl'}
             }
           }
         };

         cryptolib('asymmetric.cipher', 'commons.utils', f);
       });

    it('RSA_OAEP throw an error when the given provider is wrong.', function() {

      var f = function(box) {


        var cipherFactory =
            new box.asymmetric.cipher.factory.AsymmetricCipherFactory();

        expect(function() {
          cipherFactory.getCryptoAsymmetricCipher();
        }).toThrow();
      };

      f.policies = {
        asymmetric: {
          cipher: {
            algorithm: 'CORRECT',
            provider: 'AAA',
            secureRandom: {provider: 'Scytl'}
          }
        }
      };

      cryptolib('asymmetric.cipher', 'commons.utils', f);
    });
  });
});
