clientauthask.js (6063B)
1 /* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 "use strict"; 8 9 const { parse, pemToDER } = ChromeUtils.importESModule( 10 "chrome://global/content/certviewer/certDecoder.mjs" 11 ); 12 13 /** 14 * @file Implements the functionality of clientauthask.xhtml: a dialog that allows 15 * a user pick a client certificate for TLS client authentication. 16 * @param {object} window.arguments.0 17 * An Object with the properties: 18 * {String} hostname 19 * The hostname of the server requesting client authentication. 20 * {Array<nsIX509Cert>} certArray 21 * Array of certificates the user can choose from 22 * {Object} retVal 23 * Object to set the return values of calling the dialog on. 24 * See ClientAuthAskReturnValues. 25 */ 26 27 /** 28 * @typedef ClientAuthAskReturnValues 29 * @type {object} 30 * @property {nsIX509Cert} cert 31 * The certificate, if chosen. null otherwise. 32 * @property {number} rememberDuration 33 * Set to Ci.nsIClientAuthRememberService.Once if the decision should 34 * be remembered once, Ci.nsIClientAuthRememberService.Session if the 35 * decision should be remembered for this session, or 36 * Ci.nsIClientAuthRememberService.Permanent if the decision should 37 * be remembered permanently. 38 */ 39 40 /** 41 * The array of certs the user can choose from. 42 * 43 * @type {Array<nsIX509Cert>} 44 */ 45 var certArray; 46 47 /** 48 * The checkbox storing whether the user wants to remember the selected cert. 49 * 50 * @type {HTMLInputElement} Element checkbox, has to have |checked| property. 51 */ 52 var args; 53 54 async function onLoad() { 55 let rememberSetting = Services.prefs.getIntPref( 56 "security.client_auth_certificate_default_remember_setting" 57 ); 58 document.getElementById("rememberSetting").value = 59 rememberSetting >= 0 && rememberSetting <= 2 ? rememberSetting : 2; 60 61 let propBag = window.arguments[0] 62 .QueryInterface(Ci.nsIWritablePropertyBag2) 63 .QueryInterface(Ci.nsIWritablePropertyBag); 64 args = {}; 65 for (let prop of propBag.enumerator) { 66 args[prop.name] = prop.value; 67 } 68 69 certArray = args.certArray; 70 71 document.l10n.setAttributes( 72 document.getElementById("clientAuthSiteIdentification"), 73 "client-auth-site-identification", 74 { hostname: args.hostname } 75 ); 76 77 let selectElement = document.getElementById("nicknames"); 78 for (let i = 0; i < certArray.length; i++) { 79 let menuItemNode = document.createXULElement("menuitem"); 80 let cert = certArray[i]; 81 let nickAndSerial = `${cert.displayName} [${cert.serialNumber}]`; 82 menuItemNode.setAttribute("value", i); 83 menuItemNode.setAttribute("label", nickAndSerial); // This is displayed. 84 selectElement.menupopup.appendChild(menuItemNode); 85 if (i == 0) { 86 selectElement.selectedItem = menuItemNode; 87 } 88 } 89 90 await setDetails(); 91 document.addEventListener("dialogaccept", doOK); 92 document.addEventListener("dialogcancel", doCancel); 93 document 94 .getElementById("nicknames") 95 .addEventListener("command", () => onCertSelected()); 96 97 Services.obs.notifyObservers( 98 document.getElementById("certAuthAsk"), 99 "cert-dialog-loaded" 100 ); 101 } 102 103 /** 104 * Populates the details section with information concerning the selected cert. 105 */ 106 async function setDetails() { 107 let index = parseInt(document.getElementById("nicknames").value); 108 let cert = certArray[index]; 109 document.l10n.setAttributes( 110 document.getElementById("clientAuthCertDetailsIssuedTo"), 111 "client-auth-cert-details-issued-to", 112 { issuedTo: cert.subjectName } 113 ); 114 document.l10n.setAttributes( 115 document.getElementById("clientAuthCertDetailsSerialNumber"), 116 "client-auth-cert-details-serial-number", 117 { serialNumber: cert.serialNumber } 118 ); 119 const formatter = new Intl.DateTimeFormat(undefined, { 120 dateStyle: "medium", 121 timeStyle: "long", 122 }); 123 document.l10n.setAttributes( 124 document.getElementById("clientAuthCertDetailsValidityPeriod"), 125 "client-auth-cert-details-validity-period", 126 { 127 notBefore: formatter.format(new Date(cert.validity.notBefore / 1000)), 128 notAfter: formatter.format(new Date(cert.validity.notAfter / 1000)), 129 } 130 ); 131 let parsedCert = await parse(pemToDER(cert.getBase64DERString())); 132 let keyUsages = parsedCert.ext.keyUsages; 133 let keyUsagesJoined = 134 keyUsages && keyUsages.purposes.length ? keyUsages.purposes.join(", ") : ""; 135 document.l10n.setAttributes( 136 document.getElementById("clientAuthCertDetailsKeyUsages"), 137 "client-auth-cert-details-key-usages", 138 { keyUsages: keyUsagesJoined } 139 ); 140 let emailAddresses = cert.getEmailAddresses(); 141 let emailAddressesJoined = emailAddresses.length 142 ? emailAddresses.join(", ") 143 : ""; 144 document.l10n.setAttributes( 145 document.getElementById("clientAuthCertDetailsEmailAddresses"), 146 "client-auth-cert-details-email-addresses", 147 { emailAddresses: emailAddressesJoined } 148 ); 149 document.l10n.setAttributes( 150 document.getElementById("clientAuthCertDetailsIssuedBy"), 151 "client-auth-cert-details-issued-by", 152 { issuedBy: cert.issuerName } 153 ); 154 document.l10n.setAttributes( 155 document.getElementById("clientAuthCertDetailsStoredOn"), 156 "client-auth-cert-details-stored-on", 157 { storedOn: cert.tokenName } 158 ); 159 } 160 161 async function onCertSelected() { 162 await setDetails(); 163 } 164 165 function getRememberSetting() { 166 return parseInt(document.getElementById("rememberSetting").value); 167 } 168 169 function doOK() { 170 let { retVals } = args; 171 let index = parseInt(document.getElementById("nicknames").value); 172 let cert = certArray[index]; 173 retVals.cert = cert; 174 retVals.rememberDuration = getRememberSetting(); 175 } 176 177 function doCancel() { 178 let { retVals } = args; 179 retVals.cert = null; 180 retVals.rememberDuration = getRememberSetting(); 181 } 182 183 window.addEventListener("load", () => onLoad());