/*
 * 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 certificate = require('../lib/index');
var constants = require('../lib/constants');
var forge = require('node-forge');

var BigInteger = forge.jsbn.BigInteger;

describe('The certificate module should be able to ...', function() {
  var EPOCH_TIME_LENGTH = 10;

  var rootCertificatePem_ =
      '-----BEGIN CERTIFICATE-----MIIEKjCCAxKgAwIBAgIQQtWFdPN4NAvUIWyyJyUlbTANBgkqhkiG9w0BAQsFADBUMRAwDgYDVQQDDAdSb290IENOMRYwFAYDVQQLDA1Sb290IE9yZyBVbml0MREwDwYDVQQKDAhSb290IE9yZzEVMBMGA1UEBhMMUm9vdCBDb3VudHJ5MB4XDTE0MDYxODEwMjMyOFoXDTE1MDYxODEwMjMyOFowVDEQMA4GA1UEAwwHUm9vdCBDTjEWMBQGA1UECwwNUm9vdCBPcmcgVW5pdDERMA8GA1UECgwIUm9vdCBPcmcxFTATBgNVBAYTDFJvb3QgQ291bnRyeTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ4AidkWId1zr4IZgItjE9uv38eB8fIGJGEts2n/XNh/EJ7NYZyBzvKSJR83v7LVJjc4pup5crWyS0B5lQ7uuD3/XZ9QpQFtnGiqlKOKH45zsw3ekaHEAco07L2dZznBuQTGLTlhrmvkCFRa8b8WYC+k90oUPvSd/9S4kA1Jlo9JDKHLer0SbjZCcVRXoLSBKmGWE0xSNfmNuZNONDRbtHvSA8A10AOtxKii9w464MXYzmPil7uM1Og1HC+FXCmkzLNfqQ31Om0jra3nLmrCpBJjRPX7svVnoxajRqpazVQnmJjjpzV7yNLwnR9W8OPwqanXcbxmTkrXMxfLxiVXDFUCAwEAAaOB9zCB9DAPBgNVHRMBAf8EBTADAQH/MDUGCCsGAQUFBwEBAQH/BCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AudGhhd3RlLmNvbTA0BgNVHR8BAf8EKjAoMCagJKAihiBodHRwOi8vY3JsLnZlcmlzaWduLmNvbS9wY2EzLmNybDArBgNVHREBAf8EITAfpB0wGzEZMBcGA1UEAwwQUHJpdmF0ZUxhYmVsMy0xNTAOBgNVHQ8BAf8EBAMCAQYwNwYDVR0lAQH/BC0wKwYIKwYBBQUHAwEGCCsGAQUFBwMCBgpghkgBhvhFAQgBBglghkgBhvhCBAEwDQYJKoZIhvcNAQELBQADggEBADmtmjApZAXIkGLaZCdkRnhel53BtEdQnG990Oo/tBBboqy2ipum9ByTj3hNWJB3zuPN77rkrek9rbookNcCgVWhHtTk1lUpUK6ZohDsZh8k0MqIhkz+X+HiWGRsEOptjsCaknyWcWb4aXAevMAQMPm/ktkpQ8AOxAq+gtieewWQZP3kGPhBBCfn8TGjdrn9+ymf8EIbAUFXQ8m+oWeNlrdWhqzRXwQbj4EDds1kZdTo0nCYUdH+XEBF9nMyhAxSQWzCKQQTRFWv1dr3dKapzfgrdH8wEgvptiBYCY62O5+3DxiNK/VWquHz6S5GqIwkmSPDPMUU/qK3SNG3xIL1U1k=-----END CERTIFICATE-----';
  var rootRsaPublicKeyPem_ =
      '-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAngCJ2RYh3XOvghmAi2MT26/fx4Hx8gYkYS2zaf9c2H8Qns1hnIHO8pIlHze/stUmNzim6nlytbJLQHmVDu64Pf9dn1ClAW2caKqUo4ofjnOzDd6RocQByjTsvZ1nOcG5BMYtOWGua+QIVFrxvxZgL6T3ShQ+9J3/1LiQDUmWj0kMoct6vRJuNkJxVFegtIEqYZYTTFI1+Y25k040NFu0e9IDwDXQA63EqKL3DjrgxdjOY+KXu4zU6DUcL4VcKaTMs1+pDfU6bSOtrecuasKkEmNE9fuy9WejFqNGqlrNVCeYmOOnNXvI0vCdH1bw4/CpqddxvGZOStczF8vGJVcMVQIDAQAB-----END PUBLIC KEY-----';
  var rootSerialNumber_ = '88837713778966677489555326888277517677';
  var rootNotBefore_ = '1403087008';
  var rootNotAfter_ = '1434623008';
  var rootCn_ = 'Root CN';
  var rootOrgUnit_ = 'Root Org Unit';
  var rootOrg_ = 'Root Org';
  var rootCountry_ = 'Root Country';

  var leafCertificatePem_ =
      '-----BEGIN CERTIFICATE-----MIIEMzCCAxugAwIBAgIQRbaPaToIM+VS/d6etgYZ4jANBgkqhkiG9w0BAQsFADBUMRAwDgYDVQQDDAdSb290IENOMRYwFAYDVQQLDA1Sb290IE9yZyBVbml0MREwDwYDVQQKDAhSb290IE9yZzEVMBMGA1UEBhMMUm9vdCBDb3VudHJ5MB4XDTE0MDYxODEwMjMyOFoXDTE1MDYxODEwMjMyOFowYDETMBEGA1UEAwwKU3ViamVjdCBDTjEZMBcGA1UECwwQU3ViamVjdCBPcmcgVW5pdDEUMBIGA1UECgwLU3ViamVjdCBPcmcxGDAWBgNVBAYTD1N1YmplY3QgQ291bnRyeTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIJcqWVOOW539qKZ0SPmOdVaqLgabw0998SAfzrW8Cs8FaYvia4wRbX5N97RPbf3UkbO/QiveB0YQlnDoi2tqlj643mfUgYhMknK4SL0WVQReNcwYMEDbkbyQrCpgKpWWhTSQ2cRD+K3qZpQZ9Qn1RZn615jiqsD+Re5fWbbnL3kc7H5hdclTxZFvEzLgd2KjIsspsqh5UvLowrLuSPLaYD28LsXTDmPeHzYUl62JGPCLl9YNc3av2dY5bjmkf1KiuFKZ27iWh/xdFYAzYloenEw9AxRhJNG+9IucFOENy/0ul2UEb0rgA6Am4cASrhS+aVuZ/OuaC1W+Ut8LlXVmhsCAwEAAaOB9DCB8TAMBgNVHRMBAf8EAjAAMDUGCCsGAQUFBwEBAQH/BCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AudGhhd3RlLmNvbTA0BgNVHR8BAf8EKjAoMCagJKAihiBodHRwOi8vY3JsLnZlcmlzaWduLmNvbS9wY2EzLmNybDArBgNVHREBAf8EITAfpB0wGzEZMBcGA1UEAwwQUHJpdmF0ZUxhYmVsMy0xNTAOBgNVHQ8BAf8EBAMCBsAwNwYDVR0lAQH/BC0wKwYIKwYBBQUHAwEGCCsGAQUFBwMCBgpghkgBhvhFAQgBBglghkgBhvhCBAEwDQYJKoZIhvcNAQELBQADggEBAAWZDJD6bg4ohHewszrAbL2tdUNxhrwCgNaHUhwNK43kiLGH0U9innhL1i0jP1VHNkL1G/+ZCo1qzh/Usji/jtlurfAWtrXku6VRF9NP+itKOY5jJ91Ijkc7t4dgoeJq6iMHn6JbDKIQ88r/Ikd0GdF04o5Qjqq1HlUVmqyIOHeHFla4i4tOxTyUBj34eE1No/xmaKYV1QtR1dqSHblR7OagEo7Dd3fXp7iSrKrXaN0Ef/6zeF3zjU5SMKcUcU9d3CbhS/CrGb+UGlqTXgzPXQWESH9AqBNl67+HF3mYktDQOZYPT5WRO5IKSko2cy9pP9UCsLk4oU3xyOxacWDpk1k=-----END CERTIFICATE-----';
  var leafRsaPublicKeyPem_ =
      '-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAglypZU45bnf2opnRI+Y51VqouBpvDT33xIB/OtbwKzwVpi+JrjBFtfk33tE9t/dSRs79CK94HRhCWcOiLa2qWPrjeZ9SBiEyScrhIvRZVBF41zBgwQNuRvJCsKmAqlZaFNJDZxEP4repmlBn1CfVFmfrXmOKqwP5F7l9ZtucveRzsfmF1yVPFkW8TMuB3YqMiyymyqHlS8ujCsu5I8tpgPbwuxdMOY94fNhSXrYkY8IuX1g1zdq/Z1jluOaR/UqK4UpnbuJaH/F0VgDNiWh6cTD0DFGEk0b70i5wU4Q3L/S6XZQRvSuADoCbhwBKuFL5pW5n865oLVb5S3wuVdWaGwIDAQAB-----END PUBLIC KEY-----';
  var leafSerialNumber_ = '92664638458902967092551440174596626914';
  var leafNotBefore_ = '1403087008';
  var leafNotAfter_ = '1434623008';
  var leafCn_ = 'Subject CN';
  var leafOrgUnit_ = 'Subject Org Unit';
  var leafOrg_ = 'Subject Org';
  var leafCountry_ = 'Subject Country';

  var certificateService_;

  beforeAll(function() {
    certificateService_ = certificate.newService();
  });

  describe('create a certificate service that should be able to ..', function() {
    it('load a root X.509 certificate into a new X509Certificate object and retrieve its contents',
       function() {
         // Load root certificate object.
         var rootCertificate =
             certificateService_.newX509Certificate(rootCertificatePem_);

         // Check that expected fields can be retrieved from root certificate.
         expect(new BigInteger(rootCertificate.serialNumber, 16))
             .toEqual(new BigInteger(rootSerialNumber_));
         var notBefore =
             rootCertificate.notBefore.getTime().toString().substring(
                 0, EPOCH_TIME_LENGTH);
         expect(notBefore).toBe(rootNotBefore_);
         var notAfter = rootCertificate.notAfter.getTime().toString().substring(
             0, EPOCH_TIME_LENGTH);
         expect(notAfter).toBe(rootNotAfter_);
         expect(rootCertificate.subjectCommonName).toBe(rootCn_);
         expect(rootCertificate.subjectOrganizationalUnit).toBe(rootOrgUnit_);
         expect(rootCertificate.subjectOrganization).toBe(rootOrg_);
         expect(rootCertificate.subjectCountry).toBe(rootCountry_);
         expect(rootCertificate.issuerCommonName).toBe(rootCn_);
         expect(rootCertificate.issuerOrganizationalUnit).toBe(rootOrgUnit_);
         expect(rootCertificate.issuerOrganization).toBe(rootOrg_);
         expect(rootCertificate.issuerCountry).toBe(rootCountry_);

         // Check that public key retrieved from root certificate is same as
         // original.
         var rootPublicKeyPem = removeNewLineChars(rootCertificate.publicKey);
         expect(rootPublicKeyPem).toBe(rootRsaPublicKeyPem_);
         var rootPublicKey = forge.pki.publicKeyFromPem(rootPublicKeyPem);
         var rootPublicKeyOrig =
             forge.pki.publicKeyFromPem(rootRsaPublicKeyPem_);
         expect(rootPublicKey.e).toEqual(rootPublicKeyOrig.e);
         expect(rootPublicKey.n).toEqual(rootPublicKeyOrig.n);

         // Check that expected basic constraints flags can be retrieved from
         // certificate.
         var basicConstraints = rootCertificate.basicConstraints;
         expect((basicConstraints.ca)).toBe(true);

         // Check that expected key usage flags can be retrieved from
         // certificate.
         var keyUsageExtension = rootCertificate.keyUsageExtension;
         expect(keyUsageExtension.digitalSignature).toBe(false);
         expect(keyUsageExtension.nonRepudiation).toBe(false);
         expect(keyUsageExtension.keyEncipherment).toBe(false);
         expect(keyUsageExtension.dataEncipherment).toBe(false);
         expect(keyUsageExtension.keyAgreement).toBe(false);
         expect(keyUsageExtension.keyCertSign).toBe(true);
         expect(keyUsageExtension.crlSign).toBe(true);
         expect(keyUsageExtension.encipherOnly).toBe(false);
         expect(keyUsageExtension.decipherOnly).toBe(false);

         // Check that root certificate is self-signed.
         expect(rootCertificate.verify(rootCertificatePem_)).toBe(true);

         // Serialize root certificate to PEM format.
         var rootCertificatePem = rootCertificate.toPem();
         var offset = rootCertificatePem.indexOf('\n') + 1;
         expect(rootCertificatePem.indexOf('\n', offset))
             .toBe(offset + constants.PEM_LINE_LENGTH + 1);
         expect(removeNewLineChars(rootCertificatePem))
             .toBe(rootCertificatePem_);
       });

    it('load a leaf X.509 certificate into a new X509Certificate object and retrieve its contents',
       function() {
         // Load leaf certificate.
         var leafCertificate =
             certificateService_.newX509Certificate(leafCertificatePem_);

         // Check that expected fields can be retrieved from leaf certificate.
         expect(new BigInteger(leafCertificate.serialNumber, 16))
             .toEqual(new forge.jsbn.BigInteger(leafSerialNumber_));
         var notBefore =
             leafCertificate.notBefore.getTime().toString().substring(
                 0, EPOCH_TIME_LENGTH);
         expect(notBefore).toBe(leafNotBefore_);
         var notAfter = leafCertificate.notAfter.getTime().toString().substring(
             0, EPOCH_TIME_LENGTH);
         expect(notAfter).toBe(leafNotAfter_);
         expect(leafCertificate.subjectCommonName).toBe(leafCn_);
         expect(leafCertificate.subjectOrganizationalUnit).toBe(leafOrgUnit_);
         expect(leafCertificate.subjectOrganization).toBe(leafOrg_);
         expect(leafCertificate.subjectCountry).toBe(leafCountry_);
         expect(leafCertificate.issuerCommonName).toBe(rootCn_);
         expect(leafCertificate.issuerOrganizationalUnit).toBe(rootOrgUnit_);
         expect(leafCertificate.issuerOrganization).toBe(rootOrg_);
         expect(leafCertificate.issuerCountry).toBe(rootCountry_);

         // Check that public key retrieved from certificate is same as
         // original.
         var leafPublicKeyPem = removeNewLineChars(leafCertificate.publicKey);
         expect(leafPublicKeyPem).toBe(leafRsaPublicKeyPem_);
         var leafPublicKey = forge.pki.publicKeyFromPem(leafPublicKeyPem);
         var leafPublicKeyOrig =
             forge.pki.publicKeyFromPem(leafRsaPublicKeyPem_);
         expect(leafPublicKey.e).toEqual(leafPublicKeyOrig.e);
         expect(leafPublicKey.n).toEqual(leafPublicKeyOrig.n);

         // Check that expected basic constraints flags can be retrieved from
         // certificate.
         var basicConstraints = leafCertificate.basicConstraints;
         expect((basicConstraints.ca)).toBe(false);

         // Check that expected key usage flags can be retrieved from
         // certificate.
         var keyUsageExtension = leafCertificate.keyUsageExtension;
         expect(keyUsageExtension.digitalSignature).toBe(true);
         expect(keyUsageExtension.nonRepudiation).toBe(true);
         expect(keyUsageExtension.keyEncipherment).toBe(false);
         expect(keyUsageExtension.dataEncipherment).toBe(false);
         expect(keyUsageExtension.keyAgreement).toBe(false);
         expect(keyUsageExtension.keyCertSign).toBe(false);
         expect(keyUsageExtension.crlSign).toBe(false);
         expect(keyUsageExtension.encipherOnly).toBe(false);
         expect(keyUsageExtension.decipherOnly).toBe(false);

         // Verify that leaf certificate was signed by root certificate.
         var rootCertificate =
             certificateService_.newX509Certificate(rootCertificatePem_);
         expect(rootCertificate.verify(leafCertificatePem_)).toBe(true);

         // Serialize leaf certificate to PEM format.
         var leafCertificatePem = leafCertificate.toPem();
         var offset = leafCertificatePem.indexOf('\n') + 1;
         expect(leafCertificatePem.indexOf('\n', offset))
             .toBe(offset + constants.PEM_LINE_LENGTH + 1);
         expect(removeNewLineChars(leafCertificatePem))
             .toBe(leafCertificatePem_);
       });
  });

  function removeNewLineChars(str) {
    return str.replace(/(\r\n|\n|\r)/gm, '');
  }
});
