1 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
2 // Any copyright is dedicated to the Public Domain.
3 // http://creativecommons.org/publicdomain/zero/1.0/
7 * Test certificate (i.e. build/pgo/certs/mochitest.client).
15 var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
16 var certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
22 const { MockRegistrar } = ChromeUtils.importESModule(
23 "resource://testing-common/MockRegistrar.sys.mjs"
26 function findCertByCommonName(commonName) {
27 for (let cert of certDB.getCerts()) {
28 if (cert.commonName == commonName) {
35 async function testHelper(connectURL, expectedURL) {
36 let win = await BrowserTestUtils.openNewBrowserWindow();
38 await SpecialPowers.pushPrefEnv({
39 set: [["security.default_personal_cert", "Ask Every Time"]],
42 BrowserTestUtils.startLoadingURIString(
43 win.gBrowser.selectedBrowser,
47 await BrowserTestUtils.browserLoaded(
48 win.gBrowser.selectedBrowser,
53 let loadedURL = win.gBrowser.selectedBrowser.documentURI.spec;
55 loadedURL.startsWith(expectedURL),
56 `Expected and actual URLs should match (got '${loadedURL}', expected '${expectedURL}')`
61 // This clears the TLS session cache so we don't use a previously-established
62 // ticket to connect and bypass selecting a client auth certificate in
67 async function openRequireClientCert() {
68 gClientAuthDialogService.chooseCertificateCalled = false;
70 "https://requireclientcert.example.com:443",
71 "https://requireclientcert.example.com/"
75 async function openRequireClientCert2() {
76 gClientAuthDialogService.chooseCertificateCalled = false;
78 "https://requireclientcert-2.example.com:443",
79 "https://requireclientcert-2.example.com/"
83 // Mock implementation of nsIClientAuthRememberService
84 const gClientAuthRememberService = {
85 forgetRememberedDecision(key) {
90 "Expected to get the same key that was passed in getDecisions()"
97 asciiHost: "example.com",
99 entryKey: "exampleKey1",
102 asciiHost: "example.org",
104 entryKey: "exampleKey2",
107 asciiHost: "example.test",
109 entryKey: "exampleKey3",
112 asciiHost: "unavailable.example.com",
113 // This dbKey should not correspond to any real certificate. The first
114 // 8 bytes have to be 0, followed by the lengths of the serial number
115 // and issuer distinguished name, respectively, and then followed by
116 // the bytes of the serial number and finally the encoded issuer
117 // distinguished name. In this case, the serial number is a single 0
118 // byte and the issuer distinguished name is a DER SEQUENCE of length 0
119 // (the bytes 0x30 and 0).
120 // See also the documentation in nsNSSCertificateDB::FindCertByDBKey.
121 dbKey: "AAAAAAAAAAAAAAABAAAAAgAeAA==",
122 entryKey: "exampleKey4",
127 QueryInterface: ChromeUtils.generateQI(["nsIClientAuthRememberService"]),
130 const gClientAuthDialogService = {
131 _chooseCertificateCalled: false,
133 get chooseCertificateCalled() {
134 return this._chooseCertificateCalled;
137 set chooseCertificateCalled(value) {
138 this._chooseCertificateCalled = value;
141 chooseCertificate(hostname, certArray, loadContext, callback) {
142 this.chooseCertificateCalled = true;
143 callback.certificateChosen(certArray[0], true);
146 QueryInterface: ChromeUtils.generateQI([Ci.nsIClientAuthDialogService]),
149 add_task(async function testRememberedDecisionsUI() {
150 cert = findCertByCommonName("Mochitest client");
151 cert2 = await readCertificate("pgo-ca-all-usages.pem", ",,");
152 cert3 = await readCertificate("client-cert-via-intermediate.pem", ",,");
153 isnot(cert, null, "Should be able to find the test client cert");
154 isnot(cert2, null, "Should be able to find pgo-ca-all-usages.pem");
155 isnot(cert3, null, "Should be able to find client-cert-via-intermediate.pem");
157 let clientAuthRememberServiceCID = MockRegistrar.register(
158 "@mozilla.org/security/clientAuthRememberService;1",
159 gClientAuthRememberService
162 let win = await openCertManager();
164 let listItems = win.document
165 .getElementById("rememberedList")
166 .querySelectorAll("richlistitem");
171 "rememberedList has expected number of items"
174 let labels = win.document
175 .getElementById("rememberedList")
176 .querySelectorAll("label");
181 "rememberedList has expected number of labels"
184 await BrowserTestUtils.waitForCondition(
185 () => !!labels[10].textContent.length,
186 "Localized label is populated"
189 let expectedHosts = [
193 "unavailable.example.com",
201 let expectedNames = [
211 labels[10].textContent,
213 let expectedSerialNumbers = [
219 let serialNumbers = [
223 labels[11].textContent,
226 for (let i = 0; i < listItems.length; i++) {
227 Assert.equal(hosts[i], expectedHosts[i], "got expected asciiHost");
228 Assert.equal(names[i], expectedNames[i], "got expected commonName");
231 expectedSerialNumbers[i],
232 "got expected serialNumber"
236 win.document.getElementById("rememberedList").selectedIndex = 1;
237 win.document.getElementById("remembered_deleteButton").click();
239 Assert.ok(deleted, "Expected forgetRememberedDecision() to get called");
241 win.document.getElementById("certmanager").acceptDialog();
242 await BrowserTestUtils.windowClosed(win);
244 MockRegistrar.unregister(clientAuthRememberServiceCID);
247 add_task(async function testDeletingRememberedDecisions() {
248 let clientAuthDialogServiceCID = MockRegistrar.register(
249 "@mozilla.org/security/ClientAuthDialogService;1",
250 gClientAuthDialogService
252 let cars = Cc["@mozilla.org/security/clientAuthRememberService;1"].getService(
253 Ci.nsIClientAuthRememberService
256 await openRequireClientCert();
258 gClientAuthDialogService.chooseCertificateCalled,
259 "chooseCertificate should have been called if visiting 'requireclientcert.example.com' for the first time"
262 await openRequireClientCert();
264 !gClientAuthDialogService.chooseCertificateCalled,
265 "chooseCertificate should not have been called if visiting 'requireclientcert.example.com' for the second time"
268 await openRequireClientCert2();
270 gClientAuthDialogService.chooseCertificateCalled,
271 "chooseCertificate should have been called if visiting 'requireclientcert-2.example.com' for the first time"
274 let originAttributes = { privateBrowsingId: 0 };
275 cars.deleteDecisionsByHost("requireclientcert.example.com", originAttributes);
277 await openRequireClientCert();
279 gClientAuthDialogService.chooseCertificateCalled,
280 "chooseCertificate should have been called after removing all remembered decisions for 'requireclientcert.example.com'"
283 await openRequireClientCert2();
285 !gClientAuthDialogService.chooseCertificateCalled,
286 "chooseCertificate should not have been called if visiting 'requireclientcert-2.example.com' for the second time"
289 MockRegistrar.unregister(clientAuthDialogServiceCID);