// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

"use strict";

function test_cert_equals() {
  let certA = constructCertFromFile("bad_certs/default-ee.pem");
  let certB = constructCertFromFile("bad_certs/default-ee.pem");
  let certC = constructCertFromFile("bad_certs/expired-ee.pem");

  ok(certA != certB,
     "Cert objects constructed from the same file should not be equal" +
     " according to the equality operators");
  ok(certA.equals(certB),
     "equals() on cert objects constructed from the same cert file should" +
     " return true");
  ok(!certA.equals(certC),
     "equals() on cert objects constructed from files for different certs" +
     " should return false");
}

function test_cert_list_serialization() {
  let certList = build_cert_chain(["default-ee", "expired-ee"]);

  // Serialize the cert list to a string
  let serHelper = Cc["@mozilla.org/network/serialization-helper;1"]
                    .getService(Ci.nsISerializationHelper);
  certList.QueryInterface(Ci.nsISerializable);
  let serialized = serHelper.serializeToString(certList);

  // Deserialize from the string and compare to the original object
  let deserialized = serHelper.deserializeObject(serialized);
  deserialized.QueryInterface(Ci.nsIX509CertList);
  ok(certList.equals(deserialized),
     "Deserialized cert list should equal the original");
}

function test_cert_pkcs7_export() {
  // This was generated by running BadCertServer locally on the bad_certs
  // directory and visiting:
  // https://good.include-subdomains.pinning.example.com:8443/
  // and then viewing the certificate chain presented (in the page info dialog)
  // and exporting it.
  // (NB: test-ca must be imported and trusted for the connection to succeed)
  const expectedPKCS7ForDefaultEE =
    "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";
  let certListDefaultEE = build_cert_chain(["default-ee", "test-ca"]);
  let pkcs7DefaultEE = certListDefaultEE.asPKCS7Blob();
  equal(btoa(pkcs7DefaultEE), expectedPKCS7ForDefaultEE,
        "PKCS7 export should work as expected for default-ee chain");

  // This was generated by running BadCertServer locally on the bad_certs
  // directory and visiting:
  // https://unknownissuer.example.com:8443/
  // and then viewing the certificate presented (in the add certificate
  // exception dialog) and exporting it.
  const expectedPKCS7ForUnknownIssuer =
    "MIAGCSqGSIb3DQEHAqCAMIACAQExADCABgkqhkiG9w0BBwEAAKCCA60wggOpMIICkaADAgE" +
    "CAhQN8MG4SddxqOKNAeWywCf4fPyU0jANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtUZX" +
    "N0IEludGVybWVkaWF0ZSB0byBkZWxldGUwIhgPMjAxNjExMjcwMDAwMDBaGA8yMDE5MDIwN" +
    "TAwMDAwMFowLjEsMCoGA1UEAwwjVGVzdCBFbmQtZW50aXR5IGZyb20gdW5rbm93biBpc3N1" +
    "ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTw" +
    "T2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs" +
    "1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkf" +
    "bmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA" +
    "dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/" +
    "l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjgcIwgb8wgYgGA1UdEQSBgDB+ghl1bm" +
    "tub3duaXNzdWVyLmV4YW1wbGUuY29tgjR1bmtub3duaXNzdWVyLmluY2x1ZGUtc3ViZG9tY" +
    "Wlucy5waW5uaW5nLmV4YW1wbGUuY29tgit1bmtub3duaXNzdWVyLnRlc3QtbW9kZS5waW5u" +
    "aW5nLmV4YW1wbGUuY29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2x" +
    "vY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0BAQsFAAOCAQEAW4Vm4ekBw7QmfxOzy6HsXN8SFh" +
    "pLpE0AD1tl0fHR8V30rM8IhrnCi6KzAWceIMY4mesE+hGSkNLArNuRCI6NtGO5TyEGHhNdr" +
    "D8CDQ+/w9by5TJIw/ZsWi2xgFRHsrmphQ8e833FEdBuhthOXwaXiVCn9/Ug6OpgsSWJp9T7" +
    "AAiwQy2L2MseyfS9kcV/W/9kHiT4NoIz/3hVeLBiTg8silFGaEB02yLomp1T+/rkpqEhNFI" +
    "JhZXCa1fL8u3Z/TN40nd756fPfHXwAVuyc5qkr5R1ulVbLKXCoJLzNicfSogsmzveNrhIv2" +
    "LP6G2P9YwjmJXM6pXlxFpsWPoyi/Z4LjEAAAAAAAAA";
  let certListUnknownIssuer = build_cert_chain(["unknownissuer"]);
  let pkcs7UnknownIssuer = certListUnknownIssuer.asPKCS7Blob();
  equal(btoa(pkcs7UnknownIssuer), expectedPKCS7ForUnknownIssuer,
        "PKCS7 export should work as expected for unknown issuer");

  // This was generated by running OCSPStaplingServer locally on the ocsp_certs
  // directory and visiting:
  // https://ocsp-stapling-with-intermediate.example.com:8443/
  // and then viewing the certificate chain presented (in the page info dialog)
  // and exporting it.
  // (NB: test-ca must be imported and trusted for the connection to succeed)
  const expectedPKCS7WithIntermediate =
    "MIAGCSqGSIb3DQEHAqCAMIACAQExADCABgkqhkiG9w0BBwEAAKCCCPEwggLTMIIBu6ADAgE" +
    "CAhRdBTvvC7swO3cbVWIGn/56DrQ+cjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZX" +
    "N0IENBMCIYDzIwMTYxMTI3MDAwMDAwWhgPMjAxOTAyMDUwMDAwMDBaMBIxEDAOBgNVBAMMB" +
    "1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braI" +
    "BjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xz" +
    "VJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCy" +
    "uwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW" +
    "7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE" +
    "LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8" +
    "wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQCDjewR53YLc3HzZKugRDbQVxjJNI" +
    "LW6fSIyW9dSglYcWh6aiOK9cZFVtzRWYEYkIlicAyTiPw34bXzxU1cK6sCSmBR+UTXbRPGb" +
    "4OOy3MRaoF1m3jxwnPkQwxezDiqJTydCbYcBu0sKwURAZOd5QK922MsOsnrLjNlpRDmuH0V" +
    "Fhb5uN2I5mM3NvMnP2Or19O1Bk//iGD6AyJfiZFcii+FsDrJhbzw6lakEV7O/EnD0kk2l7I" +
    "0VMtg1xZBbEw7P6+V9zz5cAzaaq7EB0mCE+jJckSzSETBN+7lyVD8gwmHYxxZfPnUM/yvPb" +
    "MU9L3xWD/z6HHwO6r+9m7BT+2pHjBCMIIC3TCCAcWgAwIBAgIUa0X7/7DlTaedpgrIJg25i" +
    "BPOkIMwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE1MDEwMTAw" +
    "MDAwMFoYDzIwMjUwMTAxMDAwMDAwWjAcMRowGAYDVQQDDBFUZXN0IEludGVybWVkaWF0ZTC" +
    "CASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6u" +
    "Q1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8H" +
    "mnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhh" +
    "eZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaM" +
    "Mkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5" +
    "kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMdMBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EB" +
    "AMCAQYwDQYJKoZIhvcNAQELBQADggEBAILNZM9yT9ylMpjyi0tXaDORzpHiJ8vEoVKk98bC" +
    "2BQF0kMEEB547p+Ms8zdJY00Bxe9qigT8rQwKprXq5RvgIZ32QLn/yMPiCp/e6zBdsx77Tk" +
    "fmnSnxvPi+0nlA+eM8JYN0UST4vWD4vPPX9GgZDVoGQTiF3hUivJ5R8sHb/ozcSukMKQQ22" +
    "+AIU7w6wyAIbCAG7Pab4k2XFAeEnUZsl9fCym5jsPN9Pnv9rlBi6h8shHw1R2ROXjgxubji" +
    "Mr3B456vFTJImLJjyA1iTSlr/+VXGUYg6Z0/HYnsO00+8xUKM71dPxGAfIFNaSscpykrGFL" +
    "vocT/kym6r8galxCJUowggM1MIICHaADAgECAhQVzotp3WBWjtrWSBnwzntPfhsirTANBgk" +
    "qhkiG9w0BAQsFADAcMRowGAYDVQQDDBFUZXN0IEludGVybWVkaWF0ZTAiGA8yMDE2MTEyNz" +
    "AwMDAwMFoYDzIwMTkwMjA1MDAwMDAwWjAsMSowKAYDVQQDDCFUZXN0IEVuZC1lbnRpdHkgd" +
    "2l0aCBJbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGo" +
    "RI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHE" +
    "IeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7q" +
    "dw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCAB" +
    "iTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd" +
    "q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjWzBZMCMGA1U" +
    "dEQQcMBqCCWxvY2FsaG9zdIINKi5leGFtcGxlLmNvbTAyBggrBgEFBQcBAQQmMCQwIgYIKw" +
    "YBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJKoZIhvcNAQELBQADggEBACPsk" +
    "NbuRLteV4TnhgK1lnMk0v6Y7qVo7NtlRc+O5obGVkwvbt4CV225ZL+IEamvykjXr+a3ajWb" +
    "ai3v+nVB4RvijEcx5dB2VVZC61vWt1qeKPAqr2IWazi7TXmUUL3KO3zRfkeUyKUa9eKeZa4" +
    "zdB33dT/a/XYDRj5dwg5H6C0EU3x2hYpp1ZVJ8ECUa2atWi7sYc1xcbeVkKYlLF/8PcsIEs" +
    "qC1o/O6WORvJn0dVIJeIkmgOi1DYmfKea+YE17yeEOfH/JmjyvxTgkHYDajjOBHUQeV/H9D" +
    "urmv2hM99GHlmMqf0WwOfhUSKvED1sTk94l4VLgNDyZNyZap2BSLqIxAAAAAAAAAA==";
  let certListWithIntermediate = build_cert_chain(["ocspEEWithIntermediate",
                                                   "test-int", "test-ca"],
                                                   "ocsp_certs");
  let pkcs7WithIntermediate = certListWithIntermediate.asPKCS7Blob();
  equal(btoa(pkcs7WithIntermediate), expectedPKCS7WithIntermediate,
        "PKCS7 export should work as expected for chain with intermediate");
}

function test_security_info_serialization(securityInfo, expectedErrorCode) {
  // Serialize the securityInfo to a string
  let serHelper = Cc["@mozilla.org/network/serialization-helper;1"]
                    .getService(Ci.nsISerializationHelper);
  let serialized = serHelper.serializeToString(securityInfo);

  // Deserialize from the string and compare to the original object
  let deserialized = serHelper.deserializeObject(serialized);
  deserialized.QueryInterface(Ci.nsITransportSecurityInfo);
  equal(securityInfo.securityState, deserialized.securityState,
        "Original and deserialized security state should match");
  equal(securityInfo.errorMessage, deserialized.errorMessage,
        "Original and deserialized error message should match");
  equal(securityInfo.errorCode, expectedErrorCode,
        "Original and expected error code should match");
  equal(deserialized.errorCode, expectedErrorCode,
        "Deserialized and expected error code should match");
}

function run_test() {
  do_get_profile();
  add_tls_server_setup("BadCertServer", "bad_certs");

  // Test nsIX509Cert.equals
  add_test(function() {
    test_cert_equals();
    run_next_test();
  });

  // Test serialization of nsIX509CertList
  add_test(function() {
    test_cert_list_serialization();
    run_next_test();
  });

  add_test(function() {
    test_cert_pkcs7_export();
    run_next_test();
  });

  // Test successful connection (failedCertChain should be null)
  add_connection_test(
    // re-use pinning certs (keeler)
    "good.include-subdomains.pinning.example.com", PRErrorCodeSuccess, null,
    function withSecurityInfo(aTransportSecurityInfo) {
      aTransportSecurityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
      test_security_info_serialization(aTransportSecurityInfo, 0);
      equal(aTransportSecurityInfo.failedCertChain, null,
            "failedCertChain for a successful connection should be null");
    }
  );

  // Test overrideable connection failure (failedCertChain should be non-null)
  add_connection_test(
    "expired.example.com",
    SEC_ERROR_EXPIRED_CERTIFICATE,
    null,
    function withSecurityInfo(securityInfo) {
      securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
      test_security_info_serialization(securityInfo, SEC_ERROR_EXPIRED_CERTIFICATE);
      notEqual(securityInfo.failedCertChain, null,
               "failedCertChain should not be null for an overrideable" +
               " connection failure");
      let originalCertChain = build_cert_chain(["expired-ee", "test-ca"]);
      ok(originalCertChain.equals(securityInfo.failedCertChain),
         "failedCertChain should equal the original cert chain for an" +
         " overrideable connection failure");
    }
  );

  // Test overrideable connection failure (failedCertChain should be non-null)
  add_connection_test(
    "unknownissuer.example.com",
    SEC_ERROR_UNKNOWN_ISSUER,
    null,
    function withSecurityInfo(securityInfo) {
      securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
      test_security_info_serialization(securityInfo, SEC_ERROR_UNKNOWN_ISSUER);
      notEqual(securityInfo.failedCertChain, null,
               "failedCertChain should not be null for an overrideable" +
               " connection failure");
      let originalCertChain = build_cert_chain(["unknownissuer"]);
      ok(originalCertChain.equals(securityInfo.failedCertChain),
         "failedCertChain should equal the original cert chain for an" +
         " overrideable connection failure");
    }
  );

  // Test non-overrideable error (failedCertChain should be non-null)
  add_connection_test(
    "inadequatekeyusage.example.com",
    SEC_ERROR_INADEQUATE_KEY_USAGE,
    null,
    function withSecurityInfo(securityInfo) {
      securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
      test_security_info_serialization(securityInfo, SEC_ERROR_INADEQUATE_KEY_USAGE);
      notEqual(securityInfo.failedCertChain, null,
               "failedCertChain should not be null for a non-overrideable" +
               " connection failure");
      let originalCertChain = build_cert_chain(["inadequatekeyusage-ee", "test-ca"]);
      ok(originalCertChain.equals(securityInfo.failedCertChain),
         "failedCertChain should equal the original cert chain for a" +
         " non-overrideable connection failure");
    }
  );

  run_next_test();
}
