diff options
Diffstat (limited to 'security/manager/pki/resources/content/clientauthask.js')
-rw-r--r-- | security/manager/pki/resources/content/clientauthask.js | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/security/manager/pki/resources/content/clientauthask.js b/security/manager/pki/resources/content/clientauthask.js new file mode 100644 index 0000000000..da9fc07abc --- /dev/null +++ b/security/manager/pki/resources/content/clientauthask.js @@ -0,0 +1,200 @@ +/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 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/. */ +/* import-globals-from pippki.js */ +"use strict"; + +const { asn1js } = ChromeUtils.import( + "chrome://global/content/certviewer/asn1js_bundle.jsm" +); +const { pkijs } = ChromeUtils.import( + "chrome://global/content/certviewer/pkijs_bundle.jsm" +); +const { pvutils } = ChromeUtils.import( + "chrome://global/content/certviewer/pvutils_bundle.jsm" +); + +const { Integer, fromBER } = asn1js.asn1js; +const { Certificate } = pkijs.pkijs; +const { fromBase64, stringToArrayBuffer } = pvutils.pvutils; + +const { certDecoderInitializer } = ChromeUtils.import( + "chrome://global/content/certviewer/certDecoder.jsm" +); +const { parse, pemToDER } = certDecoderInitializer( + Integer, + fromBER, + Certificate, + fromBase64, + stringToArrayBuffer, + crypto +); + +/** + * @file Implements the functionality of clientauthask.xhtml: a dialog that allows + * a user pick a client certificate for TLS client authentication. + * @param {string} window.arguments.0 + * The hostname of the server requesting client authentication. + * @param {string} window.arguments.1 + * The Organization of the server cert. + * @param {string} window.arguments.2 + * The Organization of the issuer of the server cert. + * @param {number} window.arguments.3 + * The port of the server. + * @param {nsISupports} window.arguments.4 + * List of certificates the user can choose from, queryable to + * nsIArray<nsIX509Cert>. + * @param {nsISupports} window.arguments.5 + * Object to set the return values of calling the dialog on, queryable + * to the underlying type of ClientAuthAskReturnValues. + */ + +/** + * @typedef ClientAuthAskReturnValues + * @type {nsIWritablePropertyBag2} + * @property {boolean} certChosen + * Set to true if the user chose a cert and accepted the dialog, false + * otherwise. + * @property {boolean} rememberSelection + * Set to true if the user wanted their cert selection to be + * remembered, false otherwise. + * @property {number} selectedIndex + * The index the chosen cert is at for the given cert list. Undefined + * value if |certChosen| is not true. + */ + +/** + * The pippki <stringbundle> element. + * + * @type {stringbundle} + * @see {@link toolkit/content/widgets/stringbundle.js} + */ +var bundle; +/** + * The array of certs the user can choose from. + * + * @type {nsIArray<nsIX509Cert>} + */ +var certArray; +/** + * The checkbox storing whether the user wants to remember the selected cert. + * + * @type {HTMLInputElement} Element checkbox, has to have |checked| property. + */ +var rememberBox; + +async function onLoad() { + bundle = document.getElementById("pippki_bundle"); + let rememberSetting = Services.prefs.getBoolPref( + "security.remember_cert_checkbox_default_setting" + ); + + rememberBox = document.getElementById("rememberBox"); + rememberBox.label = bundle.getString("clientAuthRemember"); + rememberBox.checked = rememberSetting; + + let hostname = window.arguments[0]; + let org = window.arguments[1]; + let issuerOrg = window.arguments[2]; + let port = window.arguments[3]; + let formattedOrg = bundle.getFormattedString("clientAuthMessage1", [org]); + let formattedIssuerOrg = bundle.getFormattedString("clientAuthMessage2", [ + issuerOrg, + ]); + let formattedHostnameAndPort = bundle.getFormattedString( + "clientAuthHostnameAndPort", + [hostname, port.toString()] + ); + setText("hostname", formattedHostnameAndPort); + setText("organization", formattedOrg); + setText("issuer", formattedIssuerOrg); + + let selectElement = document.getElementById("nicknames"); + certArray = window.arguments[4].QueryInterface(Ci.nsIArray); + for (let i = 0; i < certArray.length; i++) { + let menuItemNode = document.createXULElement("menuitem"); + let cert = certArray.queryElementAt(i, Ci.nsIX509Cert); + let nickAndSerial = bundle.getFormattedString("clientAuthNickAndSerial", [ + cert.displayName, + cert.serialNumber, + ]); + menuItemNode.setAttribute("value", i); + menuItemNode.setAttribute("label", nickAndSerial); // This is displayed. + selectElement.menupopup.appendChild(menuItemNode); + if (i == 0) { + selectElement.selectedItem = menuItemNode; + } + } + + await setDetails(); + document.addEventListener("dialogaccept", doOK); + document.addEventListener("dialogcancel", doCancel); + + Services.obs.notifyObservers( + document.getElementById("certAuthAsk"), + "cert-dialog-loaded" + ); +} + +/** + * Populates the details section with information concerning the selected cert. + */ +async function setDetails() { + let index = parseInt(document.getElementById("nicknames").value); + let cert = certArray.queryElementAt(index, Ci.nsIX509Cert); + + const formatter = new Intl.DateTimeFormat(undefined, { + dateStyle: "medium", + timeStyle: "long", + }); + let detailLines = [ + bundle.getFormattedString("clientAuthIssuedTo", [cert.subjectName]), + bundle.getFormattedString("clientAuthSerial", [cert.serialNumber]), + bundle.getFormattedString("clientAuthValidityPeriod", [ + formatter.format(new Date(cert.validity.notBefore / 1000)), + formatter.format(new Date(cert.validity.notAfter / 1000)), + ]), + ]; + let parsedCert = await parse(pemToDER(cert.getBase64DERString())); + let keyUsages = parsedCert.ext.keyUsages; + if (keyUsages && keyUsages.purposes.length) { + detailLines.push( + bundle.getFormattedString("clientAuthKeyUsages", [keyUsages.purposes]) + ); + } + let emailAddresses = cert.getEmailAddresses(); + if (emailAddresses.length) { + let joinedAddresses = emailAddresses.join(", "); + detailLines.push( + bundle.getFormattedString("clientAuthEmailAddresses", [joinedAddresses]) + ); + } + detailLines.push( + bundle.getFormattedString("clientAuthIssuedBy", [cert.issuerName]) + ); + detailLines.push( + bundle.getFormattedString("clientAuthStoredOn", [cert.tokenName]) + ); + + document.getElementById("details").value = detailLines.join("\n"); +} + +async function onCertSelected() { + await setDetails(); +} + +function doOK() { + let retVals = window.arguments[5].QueryInterface(Ci.nsIWritablePropertyBag2); + retVals.setPropertyAsBool("certChosen", true); + let index = parseInt(document.getElementById("nicknames").value); + retVals.setPropertyAsUint32("selectedIndex", index); + retVals.setPropertyAsBool("rememberSelection", rememberBox.checked); +} + +function doCancel() { + let retVals = window.arguments[5].QueryInterface(Ci.nsIWritablePropertyBag2); + retVals.setPropertyAsBool("certChosen", false); + retVals.setPropertyAsBool("rememberSelection", rememberBox.checked); +} |