summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js
blob: d75f9f207263667c894b1a81875c0a4b72cdbf88 (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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/* 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";

// Repeatedly opens the certificate viewer dialog with various certificates and
// determines that the viewer correctly identifies either what usages those
// certificates are valid for or what errors prevented the certificates from
// being verified.

var { OS } = Cu.import("resource://gre/modules/osfile.jsm", {});

add_task(function* testCAandTitle() {
  let cert = yield readCertificate("ca.pem", "CTu,CTu,CTu");
  let win = yield displayCertificate(cert);
  checkUsages(win, ["SSL Certificate Authority"]);

  // There's no real need to test the title for every cert, so we just test it
  // once here.
  Assert.equal(win.document.title, "Certificate Viewer: \u201Cca\u201D",
               "Actual and expected title should match");
  yield BrowserTestUtils.closeWindow(win);
});

add_task(function* testSSLEndEntity() {
  let cert = yield readCertificate("ssl-ee.pem", ",,");
  let win = yield displayCertificate(cert);
  checkUsages(win, ["SSL Server Certificate", "SSL Client Certificate"]);
  yield BrowserTestUtils.closeWindow(win);
});

add_task(function* testEmailEndEntity() {
  let cert = yield readCertificate("email-ee.pem", ",,");
  let win = yield displayCertificate(cert);
  checkUsages(win, ["Email Recipient Certificate", "Email Signer Certificate"]);
  yield BrowserTestUtils.closeWindow(win);
});

add_task(function* testCodeSignEndEntity() {
  let cert = yield readCertificate("code-ee.pem", ",,");
  let win = yield displayCertificate(cert);
  checkUsages(win, ["Object Signer"]);
  yield BrowserTestUtils.closeWindow(win);
});

add_task(function* testExpired() {
  let cert = yield readCertificate("expired-ca.pem", ",,");
  let win = yield displayCertificate(cert);
  checkError(win, "Could not verify this certificate because it has expired.");
  yield BrowserTestUtils.closeWindow(win);
});

add_task(function* testIssuerExpired() {
  let cert = yield readCertificate("ee-from-expired-ca.pem", ",,");
  let win = yield displayCertificate(cert);
  checkError(win,
             "Could not verify this certificate because the CA certificate " +
             "is invalid.");
  yield BrowserTestUtils.closeWindow(win);
});

add_task(function* testUnknownIssuer() {
  let cert = yield readCertificate("unknown-issuer.pem", ",,");
  let win = yield displayCertificate(cert);
  checkError(win,
             "Could not verify this certificate because the issuer is " +
             "unknown.");
  yield BrowserTestUtils.closeWindow(win);
});

add_task(function* testInsecureAlgo() {
  let cert = yield readCertificate("md5-ee.pem", ",,");
  let win = yield displayCertificate(cert);
  checkError(win,
             "Could not verify this certificate because it was signed using " +
             "a signature algorithm that was disabled because that algorithm " +
             "is not secure.");
  yield BrowserTestUtils.closeWindow(win);
});

add_task(function* testUntrusted() {
  let cert = yield readCertificate("untrusted-ca.pem", "p,p,p");
  let win = yield displayCertificate(cert);
  checkError(win,
             "Could not verify this certificate because it is not trusted.");
  yield BrowserTestUtils.closeWindow(win);
});

add_task(function* testUntrustedIssuer() {
  let cert = yield readCertificate("ee-from-untrusted-ca.pem", ",,");
  let win = yield displayCertificate(cert);
  checkError(win,
             "Could not verify this certificate because the issuer is not " +
             "trusted.");
  yield BrowserTestUtils.closeWindow(win);
});

add_task(function* testRevoked() {
  // Note that there's currently no way to un-do this. This should only be a
  // problem if another test re-uses a certificate with this same key (perhaps
  // likely) and subject (less likely).
  let certBlocklist = Cc["@mozilla.org/security/certblocklist;1"]
                        .getService(Ci.nsICertBlocklist);
  certBlocklist.revokeCertBySubjectAndPubKey(
    "MBIxEDAOBgNVBAMMB3Jldm9rZWQ=", // CN=revoked
    "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8="); // hash of the shared key
  let cert = yield readCertificate("revoked.pem", ",,");
  let win = yield displayCertificate(cert);
  // As of bug 1312827, OneCRL only applies to TLS web server certificates, so
  // this certificate will actually verify successfully for every end-entity
  // usage except TLS web server.
  checkUsages(win, ["Email Recipient Certificate", "Email Signer Certificate",
                    "Object Signer", "SSL Client Certificate"]);
  yield BrowserTestUtils.closeWindow(win);
});

add_task(function* testInvalid() {
  // This certificate has a keyUsage extension asserting cRLSign and
  // keyCertSign, but it doesn't have a basicConstraints extension. This
  // shouldn't be valid for any usage. Sadly, we give a pretty lame error
  // message in this case.
  let cert = yield readCertificate("invalid.pem", ",,");
  let win = yield displayCertificate(cert);
  checkError(win, "Could not verify this certificate for unknown reasons.");
  yield BrowserTestUtils.closeWindow(win);
});

/**
 * Given a certificate, returns a promise that will resolve when the certificate
 * viewer has opened is displaying that certificate, and has finished
 * determining its valid usages.
 *
 * @param {nsIX509Cert} certificate
 *        The certificate to view and determine usages for.
 * @return {Promise}
 *         A promise that will resolve with a handle on the opened certificate
 *         viewer window when the usages have been determined.
 */
function displayCertificate(certificate) {
  let win = window.openDialog("chrome://pippki/content/certViewer.xul", "",
                              "", certificate);
  return TestUtils.topicObserved("ViewCertDetails:CertUsagesDone",
                                 (subject, data) => subject == win)
  .then(([subject, data]) => subject, error => { throw error; });
}

/**
 * Given a certificate viewer window, finds the usages the certificate is valid
 * for.
 *
 * @param {window} win
 *        The certificate viewer window.
 * @return {String[]}
 *         An array of strings describing the usages the certificate is valid
 *         for.
 */
function getUsages(win) {
  let determinedUsages = [];
  let verifyInfoBox = win.document.getElementById("verify_info_box");
  Array.from(verifyInfoBox.children).forEach(child => {
    if (child.getAttribute("hidden") != "true" &&
        child.getAttribute("id") != "verified") {
      determinedUsages.push(child.getAttribute("value"));
    }
  });
  return determinedUsages.sort();
}

/**
 * Given a certificate viewer window, returns the error string describing a
 * failure encountered when determining the certificate's usages. It will be
 * "This certificate has been verified for the following uses:" when the
 * certificate has successfully verified for at least one usage.
 *
 * @param {window} win
 *        The certificate viewer window.
 * @return {String}
 *         A string describing the error encountered, or the success message if
 *         the certificate is valid for at least one usage.
 */
function getError(win) {
  return win.document.getElementById("verified").textContent;
}

/**
 * Given a certificate viewer window and an array of expected usage
 * descriptions, verifies that the window is actually showing that the
 * certificate has validated for those usages.
 *
 * @param {window} win
 *        The certificate viewer window.
 * @param {String[]} usages
 *        An array of expected usage descriptions.
 */
function checkUsages(win, usages) {
  Assert.equal(getError(win),
               "This certificate has been verified for the following uses:",
               "should have successful verification message");
  let determinedUsages = getUsages(win);
  usages.sort();
  Assert.equal(determinedUsages.length, usages.length,
               "number of usages as determined by cert viewer should be equal");
  while (usages.length > 0) {
    Assert.equal(determinedUsages.pop(), usages.pop(),
                 "usages as determined by cert viewer should be equal");
  }
}

/**
 * Given a certificate viewer window and an expected error, verifies that the
 * window is actually showing that error.
 *
 * @param {window} win
 *        The certificate viewer window.
 * @param {String} error
 *        The expected error message.
 */
function checkError(win, error) {
  let determinedUsages = getUsages(win);
  Assert.equal(determinedUsages.length, 0,
               "should not have any successful usages in error case");
  Assert.equal(getError(win), error,
               "determined error should be the same as expected error");
}