summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/unit/test_cert_chains.js
blob: dd1fc936970058c4fe2cbe840dde1562aca02a66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// -*- 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 build_cert_chain(certNames) {
  let certList = Cc["@mozilla.org/security/x509certlist;1"]
                   .createInstance(Ci.nsIX509CertList);
  certNames.forEach(function(certName) {
    let cert = constructCertFromFile("bad_certs/" + certName + ".pem");
    certList.addCert(cert);
  });
  return certList;
}

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_bad_cert_list_serialization() {
  // Normally the serialization of an nsIX509CertList consists of some header
  // junk (IIDs and whatnot), 4 bytes representing how many nsIX509Cert follow,
  // and then the serialization of each nsIX509Cert. This serialization consists
  // of the header junk for an nsIX509CertList with 1 "nsIX509Cert", but then
  // instead of an nsIX509Cert, the subsequent bytes represent the serialization
  // of another nsIX509CertList (with 0 nsIX509Cert). This test ensures that
  // nsIX509CertList safely handles this unexpected input when deserializing.
  const badCertListSerialization =
    "lZ+xZWUXSH+rm9iRO+UxlwAAAAAAAAAAwAAAAAAAAEYAAAABlZ+xZWUXSH+rm9iRO+UxlwAAAAAA" +
    "AAAAwAAAAAAAAEYAAAAA";
  let serHelper = Cc["@mozilla.org/network/serialization-helper;1"]
                    .getService(Ci.nsISerializationHelper);
  throws(() => serHelper.deserializeObject(badCertListSerialization),
         /NS_ERROR_UNEXPECTED/,
         "deserializing a bogus nsIX509CertList should throw NS_ERROR_UNEXPECTED");
}

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

  throws(() => certList.addCert(null), /NS_ERROR_ILLEGAL_VALUE/,
         "trying to add a null cert to an nsIX509CertList should throw");

  // 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_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_bad_cert_list_serialization();
    run_next_test();
  });

  add_test(function() {
    test_cert_list_serialization();
    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 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();
}