diff --git a/.eslintrc.js b/.eslintrc.js index 694ff1bfbd33..d1dda36e0188 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1716,8 +1716,8 @@ module.exports = { "toolkit/components/antitracking/test/browser/head.js", "toolkit/components/antitracking/test/browser/storageAccessAPIHelpers.js", "toolkit/components/certviewer/tests/browser/adjustedCerts.js", - "toolkit/components/certviewer/tests/chrome/CSoutput.js", - "toolkit/components/certviewer/tests/chrome/parseOutput.js", + "toolkit/components/certviewer/tests/chrome/CSoutput.mjs", + "toolkit/components/certviewer/tests/chrome/parseOutput.mjs", "toolkit/components/cleardata/tests/browser/browser_css_cache.js", "toolkit/components/cleardata/tests/browser/browser_image_cache.js", "toolkit/components/cleardata/tests/browser/browser_preflight_cache.js", diff --git a/.hgignore b/.hgignore index 1699f2248070..acb5b37362df 100644 --- a/.hgignore +++ b/.hgignore @@ -246,10 +246,6 @@ gfx/wgpu/target gfx/wgpu/.*/build gfx/wr/target/ -# Ignore this files in certviewer -toolkit/components/certviewer/content/node_modules/ -toolkit/components/certviewer/content/package-lock.json - # Ignore Rust/Cargo output from running `cargo` directly for image_builder docker image ^taskcluster/docker/image_builder/build-image/target diff --git a/devtools/shared/network-observer/NetworkHelper.sys.mjs b/devtools/shared/network-observer/NetworkHelper.sys.mjs index 8bacdb2dacb2..d54ddda79d19 100644 --- a/devtools/shared/network-observer/NetworkHelper.sys.mjs +++ b/devtools/shared/network-observer/NetworkHelper.sys.mjs @@ -73,31 +73,11 @@ XPCOMUtils.defineLazyModuleGetters(lazy, { NetUtil: "resource://gre/modules/NetUtil.jsm", }); +// It would make sense to put this in the above +// ChromeUtils.defineESModuleGetters, but that doesn't seem to work. XPCOMUtils.defineLazyGetter(lazy, "certDecoder", () => { - 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 + const { parse, pemToDER } = ChromeUtils.importESModule( + "chrome://global/content/certviewer/certDecoder.mjs" ); return { parse, pemToDER }; }); diff --git a/security/manager/pki/resources/content/clientauthask.js b/security/manager/pki/resources/content/clientauthask.js index da9fc07abc47..782b0d7204a2 100644 --- a/security/manager/pki/resources/content/clientauthask.js +++ b/security/manager/pki/resources/content/clientauthask.js @@ -6,30 +6,8 @@ /* 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 +const { parse, pemToDER } = ChromeUtils.importESModule( + "chrome://global/content/certviewer/certDecoder.mjs" ); /** diff --git a/toolkit/components/certviewer/content/.eslintrc.js b/toolkit/components/certviewer/content/.eslintrc.js deleted file mode 100644 index 53d23f831d06..000000000000 --- a/toolkit/components/certviewer/content/.eslintrc.js +++ /dev/null @@ -1,17 +0,0 @@ -/* 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/. */ - -module.exports = { - parserOptions: { - sourceType: "module", - }, - env: { - node: true, - }, - globals: { - asn1js: true, - pvutils: true, - pkijs: true, - }, -}; diff --git a/toolkit/components/certviewer/content/README.md b/toolkit/components/certviewer/content/README.md deleted file mode 100644 index 53f104072b26..000000000000 --- a/toolkit/components/certviewer/content/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Certificate Viewer - -## Dependencies - -[PKI.js](https://github.com/PeculiarVentures/PKI.js) -[ASN1.js](https://github.com/PeculiarVentures/ASN1.js) -[pvutils.js](https://github.com/PeculiarVentures/pvutils) -[Browserify](http://browserify.org/) - -## Updating dependencies - -Install all the dependencies doing `npm i`. - -Run `npm run build` any time you add something new to `pvutils.js`, `pkijs.js`, `asn1.js` or any other file required in that one. diff --git a/toolkit/components/certviewer/content/asn1js.js b/toolkit/components/certviewer/content/asn1js.js deleted file mode 100644 index 8ff263532520..000000000000 --- a/toolkit/components/certviewer/content/asn1js.js +++ /dev/null @@ -1,9 +0,0 @@ -/* 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/. */ - -const asn1js = require("asn1js"); // version 2.0.22 - -module.exports = { - asn1js, -}; diff --git a/toolkit/components/certviewer/content/bundle.sh b/toolkit/components/certviewer/content/bundle.sh deleted file mode 100755 index ec51463af29a..000000000000 --- a/toolkit/components/certviewer/content/bundle.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -# 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 https://mozilla.org/MPL/2.0/. - -# Helper script to bundle the given library and amend it to be loadable in more -# contexts. - -WHICH="${1}" - -# Run the browserify command -./node_modules/browserify/bin/cmd.js "$WHICH".js --standalone "$WHICH" -o ./vendor/"$WHICH"_bundle.jsm - -# Amend 'this' in the first line to 'globalThis' -sed -e '1s/{g=this}/{g=globalThis}/' -i "" ./vendor/"$WHICH"_bundle.jsm - -# Append code to export the library -echo "var $WHICH = globalThis.$WHICH;" >> ./vendor/"$WHICH"_bundle.jsm -echo "var EXPORTED_SYMBOLS = [\"$WHICH\"];" >> ./vendor/"$WHICH"_bundle.jsm diff --git a/toolkit/components/certviewer/content/certDecoder.jsm b/toolkit/components/certviewer/content/certDecoder.jsm deleted file mode 100644 index 9c3b7f96de98..000000000000 --- a/toolkit/components/certviewer/content/certDecoder.jsm +++ /dev/null @@ -1,1304 +0,0 @@ -/* 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/. */ - -function certDecoderInitializer( - Integer, - fromBER, - Certificate, - fromBase64, - stringToArrayBuffer, - crypto -) { - const getTimeZone = () => { - let timeZone = new Date().toString().match(/\(([A-Za-z\s].*)\)/); - if (timeZone === null) { - // America/Chicago - timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; - } else if (timeZone.length > 1) { - timeZone = timeZone[1]; // Central Daylight Time - } else { - timeZone = "Local Time"; // not sure if this is right, but let's go with it for now - } - return timeZone; - }; - - const getPublicKeyInfo = x509 => { - let spki = Object.assign( - { - crv: undefined, - e: undefined, - kty: undefined, - n: undefined, - keysize: undefined, - x: undefined, - xy: undefined, - y: undefined, - }, - x509.subjectPublicKeyInfo - ); - - if (spki.kty === "RSA") { - spki.e = b64urltodec(spki.e); // exponent - spki.keysize = b64urltohex(spki.n).length * 8; // key size in bits - spki.n = hashify(b64urltohex(spki.n)); // modulus - } else if (spki.kty === "EC") { - spki.kty = "Elliptic Curve"; - spki.keysize = parseInt(spki.crv.split("-")[1]); // this is a bit hacky - spki.x = hashify(b64urltohex(spki.x)); // x coordinate - spki.y = hashify(b64urltohex(spki.y)); // y coordinate - spki.xy = `04:${spki.x}:${spki.y}`; // 04 (uncompressed) public key - } - return spki; - }; - - const getX509Ext = (extensions, v) => { - for (var extension in extensions) { - if (extensions[extension].extnID === v) { - return extensions[extension]; - } - } - return { - extnValue: undefined, - parsedValue: undefined, - }; - }; - - const getKeyUsages = (x509, criticalExtensions) => { - let keyUsages = { - critical: criticalExtensions.includes("2.5.29.15"), - purposes: [], - }; - - let keyUsagesBS = getX509Ext(x509.extensions, "2.5.29.15").parsedValue; - if (keyUsagesBS !== undefined) { - // parse the bit string, shifting as necessary - let unusedBits = keyUsagesBS.valueBlock.unusedBits; - keyUsagesBS = parseInt(keyUsagesBS.valueBlock.valueHex, 16) >> unusedBits; - - // iterate through the bit string - strings.keyUsages.slice(unusedBits - 1).forEach(usage => { - if (keyUsagesBS & 1) { - keyUsages.purposes.push(usage); - } - - keyUsagesBS = keyUsagesBS >> 1; - }); - - // reverse the order for legibility - keyUsages.purposes.reverse(); - } - - return keyUsages; - }; - - const parseSubsidiary = distinguishedNames => { - const subsidiary = { - cn: "", - dn: [], - entries: [], - }; - - distinguishedNames.forEach(dn => { - const name = strings.names[dn.type]; - const value = dn.value.valueBlock.value; - - if (name === undefined) { - subsidiary.dn.push(`OID.${dn.type}=${value}`); - subsidiary.entries.push([`OID.${dn.type}`, value]); - } else if (name.short === undefined) { - subsidiary.dn.push(`OID.${dn.type}=${value}`); - subsidiary.entries.push([name.long, value]); - } else { - subsidiary.dn.push(`${name.short}=${value}`); - subsidiary.entries.push([name.long, value]); - - // add the common name for tab display - if (name.short === "cn") { - subsidiary.cn = value; - } - } - }); - - // turn path into a string - subsidiary.dn = subsidiary.dn.join(", "); - - return subsidiary; - }; - - const getSubjectAltNames = (x509, criticalExtensions) => { - let san = getX509Ext(x509.extensions, "2.5.29.17").parsedValue; - if (san && san.hasOwnProperty("altNames")) { - san = Object.keys(san.altNames).map(x => { - const type = san.altNames[x].type; - - switch (type) { - case 4: // directory - return [ - strings.san[type], - parseSubsidiary(san.altNames[x].value.typesAndValues).dn, - ]; - case 7: // ip address - let address = san.altNames[x].value.valueBlock.valueHex; - - if (address.length === 8) { - // ipv4 - return [ - strings.san[type], - address - .match(/.{1,2}/g) - .map(x => parseInt(x, 16)) - .join("."), - ]; - } else if (address.length === 32) { - // ipv6 - return [ - strings.san[type], - address - .toLowerCase() - .match(/.{1,4}/g) - .join(":") - .replace(/\b:?(?:0+:?){2,}/, "::"), - ]; - } - return [strings.san[type], "Unknown IP address"]; - - default: - return [strings.san[type], san.altNames[x].value]; - } - }); - } else { - san = []; - } - san = { - altNames: san, - critical: criticalExtensions.includes("2.5.29.17"), - }; - return san; - }; - - const getBasicConstraints = (x509, criticalExtensions) => { - let basicConstraints; - const basicConstraintsExt = getX509Ext(x509.extensions, "2.5.29.19"); - if (basicConstraintsExt && basicConstraintsExt.parsedValue) { - basicConstraints = { - cA: - basicConstraintsExt.parsedValue.cA !== undefined && - basicConstraintsExt.parsedValue.cA, - critical: criticalExtensions.includes("2.5.29.19"), - }; - } - return basicConstraints; - }; - - const getEKeyUsages = (x509, criticalExtensions) => { - let eKeyUsages = getX509Ext(x509.extensions, "2.5.29.37").parsedValue; - if (eKeyUsages) { - eKeyUsages = { - critical: criticalExtensions.includes("2.5.29.37"), - purposes: eKeyUsages.keyPurposes.map(x => strings.eKU[x] || x), - }; - } - return eKeyUsages; - }; - - const getSubjectKeyID = (x509, criticalExtensions) => { - let sKID = getX509Ext(x509.extensions, "2.5.29.14").parsedValue; - if (sKID) { - sKID = { - critical: criticalExtensions.includes("2.5.29.14"), - id: hashify(sKID.valueBlock.valueHex), - }; - } - return sKID; - }; - - const getAuthorityKeyID = (x509, criticalExtensions) => { - let aKID = getX509Ext(x509.extensions, "2.5.29.35").parsedValue; - if (!aKID || !aKID.keyIdentifier) { - return null; - } - aKID = { - critical: criticalExtensions.includes("2.5.29.35"), - id: hashify(aKID.keyIdentifier.valueBlock.valueHex), - }; - return aKID; - }; - - const getCRLPoints = (x509, criticalExtensions) => { - let crlPoints = getX509Ext(x509.extensions, "2.5.29.31").parsedValue; - if (crlPoints) { - crlPoints = { - critical: criticalExtensions.includes("2.5.29.31"), - points: crlPoints.distributionPoints.map( - x => x.distributionPoint[0].value - ), - }; - } - return crlPoints; - }; - - const getOcspStaple = (x509, criticalExtensions) => { - let ocspStaple = getX509Ext(x509.extensions, "1.3.6.1.5.5.7.1.24") - .extnValue; - if (ocspStaple && ocspStaple.valueBlock.valueHex === "3003020105") { - ocspStaple = { - critical: criticalExtensions.includes("1.3.6.1.5.5.7.1.24"), - required: true, - }; - } else { - ocspStaple = { - critical: criticalExtensions.includes("1.3.6.1.5.5.7.1.24"), - required: false, - }; - } - return ocspStaple; - }; - - const getAuthorityInfoAccess = (x509, criticalExtensions) => { - let aia = getX509Ext(x509.extensions, "1.3.6.1.5.5.7.1.1").parsedValue; - if (aia) { - aia = aia.accessDescriptions.map(x => { - return { - location: x.accessLocation.value, - method: strings.aia[x.accessMethod], - }; - }); - } - - aia = { - descriptions: aia, - critical: criticalExtensions.includes("1.3.6.1.5.5.7.1.1"), - }; - return aia; - }; - - const getSCTs = (x509, criticalExtensions) => { - let scts = getX509Ext(x509.extensions, "1.3.6.1.4.1.11129.2.4.2") - .parsedValue; - if (scts) { - scts = Object.keys(scts.timestamps).map(x => { - let logId = scts.timestamps[x].logID.toLowerCase(); - let sctsTimestamp = scts.timestamps[x].timestamp; - return { - logId: hashify(logId), - name: ctLogNames.hasOwnProperty(logId) - ? ctLogNames[logId] - : undefined, - signatureAlgorithm: `${scts.timestamps[x].hashAlgorithm.replace( - "sha", - "SHA-" - )} ${scts.timestamps[x].signatureAlgorithm.toUpperCase()}`, - timestamp: `${sctsTimestamp.toLocaleString()} (${getTimeZone()})`, - timestampUTC: sctsTimestamp.toUTCString(), - version: scts.timestamps[x].version + 1, - }; - }); - } else { - scts = []; - } - - scts = { - critical: criticalExtensions.includes("1.3.6.1.4.1.11129.2.4.2"), - timestamps: scts, - }; - return scts; - }; - - const getCertificatePolicies = (x509, criticalExtensions) => { - let cp = getX509Ext(x509.extensions, "2.5.29.32").parsedValue; - if (cp && cp.hasOwnProperty("certificatePolicies")) { - cp = cp.certificatePolicies.map(x => { - let id = x.policyIdentifier; - let name = strings.cps.hasOwnProperty(id) - ? strings.cps[id].name - : undefined; - let qualifiers = undefined; - let value = strings.cps.hasOwnProperty(id) - ? strings.cps[id].value - : undefined; - - // ansi organization identifiers - if (id.startsWith("2.16.840.")) { - value = id; - id = "2.16.840"; - name = strings.cps["2.16.840"].name; - } - - // statement identifiers - if (id.startsWith("1.3.6.1.4.1")) { - value = id; - id = "1.3.6.1.4.1"; - name = strings.cps["1.3.6.1.4.1"].name; - } - - if (x.hasOwnProperty("policyQualifiers")) { - qualifiers = x.policyQualifiers.map(qualifier => { - let id = qualifier.policyQualifierId; - let name = strings.cps.hasOwnProperty(id) - ? strings.cps[id].name - : undefined; - let value = qualifier.qualifier.valueBlock.value; - - // sometimes they are multiple qualifier subblocks, and for now we'll - // only return the first one because it's getting really messy at this point - if (Array.isArray(value) && value.length === 1) { - value = value[0].valueBlock.value; - } else if (Array.isArray(value) && value.length > 1) { - value = "(currently unsupported)"; - } - - return { - id, - name, - value, - }; - }); - } - - return { - id, - name, - qualifiers, - value, - }; - }); - } - - cp = { - critical: criticalExtensions.includes("2.5.29.32"), - policies: cp, - }; - return cp; - }; - - const getMicrosoftCryptographicExtensions = (x509, criticalExtensions) => { - // now let's parse the Microsoft cryptographic extensions - let msCrypto = { - caVersion: getX509Ext(x509.extensions, "1.3.6.1.4.1.311.21.1") - .parsedValue, - certificatePolicies: getX509Ext(x509.extensions, "1.3.6.1.4.1.311.21.10") - .parsedValue, - certificateTemplate: getX509Ext(x509.extensions, "1.3.6.1.4.1.311.21.7") - .parsedValue, - certificateType: getX509Ext(x509.extensions, "1.3.6.1.4.1.311.20.2") - .parsedValue, - previousHash: getX509Ext(x509.extensions, "1.3.6.1.4.1.311.21.2") - .parsedValue, - }; - - if ( - msCrypto.caVersion && - Number.isInteger(msCrypto.caVersion.keyIndex) && - Number.isInteger(msCrypto.caVersion.certificateIndex) - ) { - msCrypto.caVersion = { - critical: criticalExtensions.includes("1.3.6.1.4.1.311.21.1"), - caRenewals: msCrypto.caVersion.certificateIndex, - keyReuses: - msCrypto.caVersion.certificateIndex - msCrypto.caVersion.keyIndex, - }; - } - - if (msCrypto.certificatePolicies) { - msCrypto.certificatePolicies = { - critical: criticalExtensions.includes("1.3.6.1.4.1.311.21.10"), - purposes: msCrypto.certificatePolicies.certificatePolicies.map( - x => strings.eKU[x.policyIdentifier] || x.policyIdentifier - ), - }; - } - - if (msCrypto.certificateTemplate) { - msCrypto.certificateTemplate = { - critical: criticalExtensions.includes("1.3.6.1.4.1.311.21.7"), - id: msCrypto.certificateTemplate.extnID, - major: msCrypto.certificateTemplate.templateMajorVersion, - minor: msCrypto.certificateTemplate.templateMinorVersion, - }; - } - - if (msCrypto.certificateType) { - msCrypto.certificateType = { - critical: criticalExtensions.includes("1.3.6.1.4.1.311.20.2"), - type: - strings.microsoftCertificateTypes[ - msCrypto.certificateType.valueBlock.value - ] || "Unknown", - }; - } - - if (msCrypto.previousHash) { - msCrypto.previousHash = { - critical: criticalExtensions.includes("1.3.6.1.4.1.311.21.2"), - previousHash: hashify(msCrypto.previousHash.valueBlock.valueHex), - }; - } - - msCrypto.exists = !!( - msCrypto.caVersion || - msCrypto.certificatePolicies || - msCrypto.certificateTemplate || - msCrypto.certificateType || - msCrypto.previousHash - ); - - return msCrypto; - }; - - const b64ToPEM = string => { - let wrapped = string.match(/.{1,64}/g).join("\r\n"); - return `-----BEGIN CERTIFICATE-----\r\n${wrapped}\r\n-----END CERTIFICATE-----\r\n`; - }; - - const parse = async certificate => { - // certificate could be an array of BER or an array of buffers - const supportedExtensions = [ - "1.3.6.1.4.1.311.20.2", // microsoft certificate type - "1.3.6.1.4.1.311.21.2", // microsoft certificate previous hash - "1.3.6.1.4.1.311.21.7", // microsoft certificate template - "1.3.6.1.4.1.311.21.1", // microsoft certification authority renewal - "1.3.6.1.4.1.311.21.10", // microsoft certificate policies - "1.3.6.1.4.1.11129.2.4.2", // embedded scts - "1.3.6.1.5.5.7.1.1", // authority info access - "1.3.6.1.5.5.7.1.24", // ocsp stapling - "1.3.101.77", // ct redaction - deprecated and not displayed - "2.5.29.14", // subject key identifier - "2.5.29.15", // key usages - "2.5.29.17", // subject alt names - "2.5.29.19", // basic constraints - "2.5.29.31", // crl points - "2.5.29.32", // certificate policies - "2.5.29.35", // authority key identifier - "2.5.29.37", // extended key usage - ]; - - let timeZone = getTimeZone(); - - // parse the certificate - const asn1 = fromBER(certificate); - - let x509 = new Certificate({ schema: asn1.result }); - x509 = x509.toJSON(); - - // convert the cert to PEM - const certPEM = b64ToPEM( - btoa(String.fromCharCode.apply(null, new Uint8Array(certificate))) - ); - - // get which extensions are critical - const criticalExtensions = []; - if (x509.extensions) { - x509.extensions.forEach(ext => { - if (ext.hasOwnProperty("critical") && ext.critical === true) { - criticalExtensions.push(ext.extnID); - } - }); - } - const spki = getPublicKeyInfo(x509); - const keyUsages = getKeyUsages(x509, criticalExtensions); - const san = getSubjectAltNames(x509, criticalExtensions); - const basicConstraints = getBasicConstraints(x509, criticalExtensions); - const eKeyUsages = getEKeyUsages(x509, criticalExtensions); - const sKID = getSubjectKeyID(x509, criticalExtensions); - const aKID = getAuthorityKeyID(x509, criticalExtensions); - const crlPoints = getCRLPoints(x509, criticalExtensions); - const ocspStaple = getOcspStaple(x509, criticalExtensions); - const aia = getAuthorityInfoAccess(x509, criticalExtensions); - const scts = getSCTs(x509, criticalExtensions); - const cp = getCertificatePolicies(x509, criticalExtensions); - const msCrypto = getMicrosoftCryptographicExtensions( - x509, - criticalExtensions - ); - - // determine which extensions weren't supported - let unsupportedExtensions = []; - if (x509.extensions) { - x509.extensions.forEach(ext => { - if (!supportedExtensions.includes(ext.extnID)) { - unsupportedExtensions.push(ext.extnID); - } - }); - } - - // the output shell - return { - ext: { - aia, - aKID, - basicConstraints, - crlPoints, - cp, - eKeyUsages, - keyUsages, - msCrypto, - ocspStaple, - scts, - sKID, - san, - }, - files: { - der: undefined, // TODO: implement! - pem: encodeURI(certPEM), - }, - fingerprint: { - sha1: await hash("SHA-1", certificate), - sha256: await hash("SHA-256", certificate), - }, - issuer: parseSubsidiary(x509.issuer.typesAndValues), - notBefore: `${x509.notBefore.value.toLocaleString()} (${timeZone})`, - notBeforeUTC: x509.notBefore.value.toUTCString(), - notAfter: `${x509.notAfter.value.toLocaleString()} (${timeZone})`, - notAfterUTC: x509.notAfter.value.toUTCString(), - subject: parseSubsidiary(x509.subject.typesAndValues), - serialNumber: hashify( - getObjPath(x509, "serialNumber.valueBlock.valueHex") - ), - signature: { - name: strings.signature[getObjPath(x509, "signature.algorithmId")], - type: getObjPath(x509, "signature.algorithmId"), - }, - subjectPublicKeyInfo: spki, - unsupportedExtensions, - version: (x509.version + 1).toString(), - }; - }; - - const ctLogNames = { - "9606c02c690033aa1d145f59c6e2648d0549f0df96aab8db915a70d8ecf390a5": - "Akamai CT", - "39376f545f7b4607f59742d768cd5d2437bf3473b6534a4834bcf72e681c83c9": - "Alpha CT", - a577ac9ced7548dd8f025b67a241089df86e0f476ec203c2ecbedb185f282638: - "CNNIC CT", - cdb5179b7fc1c046feea31136a3f8f002e6182faf8896fecc8b2f5b5ab604900: - "Certly.IO", - "1fbc36e002ede97f40199e86b3573b8a4217d80187746ad0da03a06054d20df4": - "Cloudflare “Nimbus2017”", - db74afeecb29ecb1feca3e716d2ce5b9aabb36f7847183c75d9d4f37b61fbf64: - "Cloudflare “Nimbus2018”", - "747eda8331ad331091219cce254f4270c2bffd5e422008c6373579e6107bcc56": - "Cloudflare “Nimbus2019”", - "5ea773f9df56c0e7b536487dd049e0327a919a0c84a112128418759681714558": - "Cloudflare “Nimbus2020”", - "4494652eb0eeceafc44007d8a8fe28c0dae682bed8cb31b53fd33396b5b681a8": - "Cloudflare “Nimbus2021”", - "41c8cab1df22464a10c6a13a0942875e4e318b1b03ebeb4bc768f090629606f6": - "Cloudflare “Nimbus2022”", - "7a328c54d8b72db620ea38e0521ee98416703213854d3bd22bc13a57a352eb52": - "Cloudflare “Nimbus2023”", - "6ff141b5647e4222f7ef052cefae7c21fd608e27d2af5a6e9f4b8a37d6633ee5": - "DigiCert Nessie2018", - fe446108b1d01ab78a62ccfeab6ab2b2babff3abdad80a4d8b30df2d0008830c: - "DigiCert Nessie2019", - c652a0ec48ceb3fcab170992c43a87413309e80065a26252401ba3362a17c565: - "DigiCert Nessie2020", - eec095ee8d72640f92e3c3b91bc712a3696a097b4b6a1a1438e647b2cbedc5f9: - "DigiCert Nessie2021", - "51a3b0f5fd01799c566db837788f0ca47acc1b27cbf79e88429a0dfed48b05e5": - "DigiCert Nessie2022", - b3737707e18450f86386d605a9dc11094a792db1670c0b87dcf0030e7936a59a: - "DigiCert Nessie2023", - "5614069a2fd7c2ecd3f5e1bd44b23ec74676b9bc99115cc0ef949855d689d0dd": - "DigiCert Server", - "8775bfe7597cf88c43995fbdf36eff568d475636ff4ab560c1b4eaff5ea0830f": - "DigiCert Server 2", - c1164ae0a772d2d4392dc80ac10770d4f0c49bde991a4840c1fa075164f63360: - "DigiCert Yeti2018", - e2694bae26e8e94009e8861bb63b83d43ee7fe7488fba48f2893019dddf1dbfe: - "DigiCert Yeti2019", - f095a459f200d18240102d2f93888ead4bfe1d47e399e1d034a6b0a8aa8eb273: - "DigiCert Yeti2020", - "5cdc4392fee6ab4544b15e9ad456e61037fbd5fa47dca17394b25ee6f6c70eca": - "DigiCert Yeti2021", - "2245450759552456963fa12ff1f76d86e0232663adc04b7f5dc6835c6ee20f02": - "DigiCert Yeti2022", - "35cf191bbfb16c57bf0fad4c6d42cbbbb627202651ea3fe12aefa803c33bd64c": - "DigiCert Yeti2023", - "717ea7420975be84a2723553f1777c26dd51af4e102144094d9019b462fb6668": - "GDCA 1", - "14308d90ccd030135005c01ca526d81e84e87624e39b6248e08f724aea3bb42a": - "GDCA 2", - c9cf890a21109c666cc17a3ed065c930d0e0135a9feba85af14210b8072421aa: - "GDCA CT #1", - "924a30f909336ff435d6993a10ac75a2c641728e7fc2d659ae6188ffad40ce01": - "GDCA CT #2", - fad4c97cc49ee2f8ac85c5ea5cea09d0220dbbf4e49c6b50662ff868f86b8c28: - "Google “Argon2017”", - a4501269055a15545e6211ab37bc103f62ae5576a45e4b1714453e1b22106a25: - "Google “Argon2018”", - "63f2dbcde83bcc2ccf0b728427576b33a48d61778fbd75a638b1c768544bd88d": - "Google “Argon2019”", - b21e05cc8ba2cd8a204e8766f92bb98a2520676bdafa70e7b249532def8b905e: - "Google “Argon2020”", - f65c942fd1773022145418083094568ee34d131933bfdf0c2f200bcc4ef164e3: - "Google “Argon2021”", - "2979bef09e393921f056739f63a577e5be577d9c600af8f94d5d265c255dc784": - "Google “Argon2022”", - "68f698f81f6482be3a8ceeb9281d4cfc71515d6793d444d10a67acbb4f4ffbc4": - "Google “Aviator”", - c3bf03a7e1ca8841c607bae3ff4270fca5ec45b186ebbe4e2cf3fc778630f5f6: - "Google “Crucible”", - "1d024b8eb1498b344dfd87ea3efc0996f7506f235d1d497061a4773c439c25fb": - "Google “Daedalus”", - "293c519654c83965baaa50fc5807d4b76fbf587a2972dca4c30cf4e54547f478": - "Google “Icarus”", - a4b90990b418581487bb13a2cc67700a3c359804f91bdfb8e377cd0ec80ddc10: - "Google “Pilot”", - ee4bbdb775ce60bae142691fabe19e66a30f7e5fb072d88300c47b897aa8fdcb: - "Google “Rocketeer”", - bbd9dfbc1f8a71b593942397aa927b473857950aab52e81a909664368e1ed185: - "Google “Skydiver”", - "52eb4b225ec896974850675f23e43bc1d021e3214ce52ecd5fa87c203cdfca03": - "Google “Solera2018”", - "0b760e9a8b9a682f88985b15e947501a56446bba8830785c3842994386450c00": - "Google “Solera2019”", - "1fc72ce5a1b799f400c359bff96ca3913548e8644220610952e9ba1774f7bac7": - "Google “Solera2020”", - a3c99845e80ab7ce00157b3742df0207dd272b2b602ecf98ee2c12db9c5ae7e7: - "Google “Solera2021”", - "697aafca1a6b536fae21205046debad7e0eaea13d2432e6e9d8fb379f2b9aaf3": - "Google “Solera2022”", - a899d8780c9290aaf462f31880ccfbd52451e970d0fbf591ef75b0d99b645681: - "Google “Submariner”", - b0cc83e5a5f97d6baf7c09cc284904872ac7e88b132c6350b7c6fd26e16c6c77: - "Google “Testtube”", - b10cd559a6d67846811f7df9a51532739ac48d703bea0323da5d38755bc0ad4e: - "Google “Xenon2018”", - "084114980071532c16190460bcfc47fdc2653afa292c72b37ff863ae29ccc9f0": - "Google “Xenon2019”", - "07b75c1be57d68fff1b0c61d2315c7bae6577c5794b76aeebc613a1a69d3a21c": - "Google “Xenon2020”", - "7d3ef2f88fff88556824c2c0ca9e5289792bc50e78097f2e6a9768997e22f0d7": - "Google “Xenon2021”", - "46a555eb75fa912030b5a28969f4f37d112c4174befd49b885abf2fc70fe6d47": - "Google “Xenon2022”", - "7461b4a09cfb3d41d75159575b2e7649a445a8d27709b0cc564a6482b7eb41a3": - "Izenpe", - "8941449c70742e06b9fc9ce7b116ba0024aa36d59af44f0204404f00f7ea8566": - "Izenpe “Argi”", - "296afa2d568bca0d2ea844956ae9721fc35fa355ecda99693aafd458a71aefdd": - "Let“s Encrypt ”Clicky”", - "537b69a3564335a9c04904e39593b2c298eb8d7a6e83023635c627248cd6b440": - "Nordu “flimsy”", - aae70b7f3cb8d566c86c2f16979c9f445f69ab0eb4535589b2f77a030104f3cd: - "Nordu “plausible”", - e0127629e90496564e3d0147984498aa48f8adb16600eb7902a1ef9909906273: - "PuChuangSiDa CT", - cf55e28923497c340d5206d05353aeb25834b52f1f8dc9526809f212efdd7ca6: - "SHECA CT 1", - "32dc59c2d4c41968d56e14bc61ac8f0e45db39faf3c155aa4252f5001fa0c623": - "SHECA CT 2", - db76fdadac65e7d09508886e2159bd8b90352f5fead3e3dc5e22eb350acc7b98: - "Sectigo (Comodo) “Dodo” CT", - "6f5376ac31f03119d89900a45115ff77151c11d902c10029068db2089a37d913": - "Sectigo (Comodo) “Mammoth” CT", - "5581d4c2169036014aea0b9b573c53f0c0e43878702508172fa3aa1d0713d30c": - "Sectigo (Comodo) “Sabre” CT", - "34bb6ad6c3df9c03eea8a499ff7891486c9d5e5cac92d01f7bfd1bce19db48ef": - "StartCom", - ddeb1d2b7a0d4fa6208b81ad8168707e2e8e9d01d55c888d3d11c4cdb6ecbecc: - "Symantec", - a7ce4a4e6207e0addee5fdaa4b1f86768767b5d002a55d47310e7e670a95eab2: - "Symantec Deneb", - "15970488d7b997a05beb52512adee8d2e8b4a3165264121a9fabfbd5f85ad93f": - "Symantec “Sirius”", - bc78e1dfc5f63c684649334da10fa15f0979692009c081b4f3f6917f3ed9b8a5: - "Symantec “Vega”", - b0b784bc81c0ddc47544e883f05985bb9077d134d8ab88b2b2e533980b8e508b: - "Up In The Air “Behind the Sofa”", - ac3b9aed7fa9674757159e6d7d575672f9d98100941e9bdeffeca1313b75782d: "Venafi", - "03019df3fd85a69a8ebd1facc6da9ba73e469774fe77f579fc5a08b8328c1d6b": - "Venafi Gen2 CT", - "41b2dc2e89e63ce4af1ba7bb29bf68c6dee6f9f1cc047e30dffae3b3ba259263": - "WoSign", - "63d0006026dde10bb0601f452446965ee2b6ea2cd4fbc95ac866a550af9075b7": - "WoSign 2", - "9e4ff73dc3ce220b69217c899e468076abf8d78636d5ccfc85a31a75628ba88b": - "WoSign CT #1", - "659b3350f43b12cc5ea5ab4ec765d3fde6c88243777778e72003f9eb2b8c3129": - "Let's Encrypt Oak 2019", - e712f2b0377e1a62fb8ec90c6184f1ea7b37cb561d11265bf3e0f34bf241546e: - "Let's Encrypt Oak 2020", - "9420bc1e8ed58d6c88731f828b222c0dd1da4d5e6c4f943d61db4e2f584da2c2": - "Let's Encrypt Oak 2021", - dfa55eab68824f1f6cadeeb85f4e3e5aeacda212a46a5e8e3b12c020445c2a73: - "Let's Encrypt Oak 2022", - b73efb24df9c4dba75f239c5ba58f46c5dfc42cf7a9f35c49e1d098125edb499: - "Let's Encrypt Oak 2023", - "849f5f7f58d2bf7b54ecbd74611cea45c49c98f1d6481bc6f69e8c174f24f3cf": - "Let's Encrypt Testflume 2019", - c63f2218c37d56a6aa06b596da8e53d4d7156d1e9bac8e44d2202de64d69d9dc: - "Let's Encrypt Testflume 2020", - "03edf1da9776b6f38c341e39ed9d707a7570369cf9844f327fe9e14138361b60": - "Let's Encrypt Testflume 2021", - "2327efda352510dbc019ef491ae3ff1cc5a479bce37878360ee318cffb64f8c8": - "Let's Encrypt Testflume 2022", - "5534b7ab5a6ac3a7cbeba65487b2a2d71b48f650fa17c5197c97a0cb2076f3c6": - "Let's Encrypt Testflume 2023", - }; - - const strings = { - ux: { - upload: "Upload Certificate", - }, - - names: { - // Directory Pilot Attributes - "0.9.2342.19200300.100.1.1": { - short: "uid", - long: "User ID", - }, - "0.9.2342.19200300.100.1.25": { - short: "dc", - long: "Domain Component", - }, - - // PKCS-9 - "1.2.840.113549.1.9.1": { - short: "e", - long: "Email Address", - }, - - // Incorporated Locations - "1.3.6.1.4.1.311.60.2.1.1": { - short: undefined, - long: "Inc. Locality", - }, - "1.3.6.1.4.1.311.60.2.1.2": { - short: undefined, - long: "Inc. State / Province", - }, - "1.3.6.1.4.1.311.60.2.1.3": { - short: undefined, - long: "Inc. Country", - }, - - // microsoft cryptographic extensions - "1.3.6.1.4.1.311.21.7": { - name: { - short: "Certificate Template", - long: "Microsoft Certificate Template", - }, - }, - "1.3.6.1.4.1.311.21.10": { - name: { - short: "Certificate Policies", - long: "Microsoft Certificate Policies", - }, - }, - - // certificate extensions - "1.3.6.1.4.1.11129.2.4.2": { - name: { - short: "Embedded SCTs", - long: "Embedded Signed Certificate Timestamps", - }, - }, - "1.3.6.1.5.5.7.1.1": { - name: { - short: undefined, - long: "Authority Information Access", - }, - }, - "1.3.6.1.5.5.7.1.24": { - name: { - short: "OCSP Stapling", - long: "Online Certificate Status Protocol Stapling", - }, - }, - - // X.500 attribute types - "2.5.4.1": { - short: undefined, - long: "Aliased Entry", - }, - "2.5.4.2": { - short: undefined, - long: "Knowledge Information", - }, - "2.5.4.3": { - short: "cn", - long: "Common Name", - }, - "2.5.4.4": { - short: "sn", - long: "Surname", - }, - "2.5.4.5": { - short: "serialNumber", - long: "Serial Number", - }, - "2.5.4.6": { - short: "c", - long: "Country", - }, - "2.5.4.7": { - short: "l", - long: "Locality", - }, - "2.5.4.8": { - short: "s", - long: "State / Province", - }, - "2.5.4.9": { - short: "street", - long: "Stress Address", - }, - "2.5.4.10": { - short: "o", - long: "Organization", - }, - "2.5.4.11": { - short: "ou", - long: "Organizational Unit", - }, - "2.5.4.12": { - short: "t", - long: "Title", - }, - "2.5.4.13": { - short: "description", - long: "Description", - }, - "2.5.4.14": { - short: undefined, - long: "Search Guide", - }, - "2.5.4.15": { - short: undefined, - long: "Business Category", - }, - "2.5.4.16": { - short: undefined, - long: "Postal Address", - }, - "2.5.4.17": { - short: "postalCode", - long: "Postal Code", - }, - "2.5.4.18": { - short: "POBox", - long: "PO Box", - }, - "2.5.4.19": { - short: undefined, - long: "Physical Delivery Office Name", - }, - "2.5.4.20": { - short: "phone", - long: "Phone Number", - }, - "2.5.4.21": { - short: undefined, - long: "Telex Number", - }, - "2.5.4.22": { - short: undefined, - long: "Teletex Terminal Identifier", - }, - "2.5.4.23": { - short: undefined, - long: "Fax Number", - }, - "2.5.4.24": { - short: undefined, - long: "X.121 Address", - }, - "2.5.4.25": { - short: undefined, - long: "International ISDN Number", - }, - "2.5.4.26": { - short: undefined, - long: "Registered Address", - }, - "2.5.4.27": { - short: undefined, - long: "Destination Indicator", - }, - "2.5.4.28": { - short: undefined, - long: "Preferred Delivery Method", - }, - "2.5.4.29": { - short: undefined, - long: "Presentation Address", - }, - "2.5.4.30": { - short: undefined, - long: "Supported Application Context", - }, - "2.5.4.31": { - short: undefined, - long: "Member", - }, - "2.5.4.32": { - short: undefined, - long: "Owner", - }, - "2.5.4.33": { - short: undefined, - long: "Role Occupant", - }, - "2.5.4.34": { - short: undefined, - long: "See Also", - }, - "2.5.4.35": { - short: undefined, - long: "User Password", - }, - "2.5.4.36": { - short: undefined, - long: "User Certificate", - }, - "2.5.4.37": { - short: undefined, - long: "CA Certificate", - }, - "2.5.4.38": { - short: undefined, - long: "Authority Revocation List", - }, - "2.5.4.39": { - short: undefined, - long: "Certificate Revocation List", - }, - "2.5.4.40": { - short: undefined, - long: "Cross-certificate Pair", - }, - "2.5.4.41": { - short: undefined, - long: "Name", - }, - "2.5.4.42": { - short: "g", - long: "Given Name", - }, - "2.5.4.43": { - short: "i", - long: "Initials", - }, - "2.5.4.44": { - short: undefined, - long: "Generation Qualifier", - }, - "2.5.4.45": { - short: undefined, - long: "Unique Identifier", - }, - "2.5.4.46": { - short: undefined, - long: "DN Qualifier", - }, - "2.5.4.47": { - short: undefined, - long: "Enhanced Search Guide", - }, - "2.5.4.48": { - short: undefined, - long: "Protocol Information", - }, - "2.5.4.49": { - short: "dn", - long: "Distinguished Name", - }, - "2.5.4.50": { - short: undefined, - long: "Unique Member", - }, - "2.5.4.51": { - short: undefined, - long: "House Identifier", - }, - "2.5.4.52": { - short: undefined, - long: "Supported Algorithms", - }, - "2.5.4.53": { - short: undefined, - long: "Delta Revocation List", - }, - "2.5.4.58": { - short: undefined, - long: "Attribute Certificate Attribute", // huh - }, - "2.5.4.65": { - short: undefined, - long: "Pseudonym", - }, - - // extensions - "2.5.29.14": { - name: { - short: "Subject Key ID", - long: "Subject Key Identifier", - }, - }, - "2.5.29.15": { - name: { - short: undefined, - long: "Key Usages", - }, - }, - "2.5.29.17": { - name: { - short: "Subject Alt Names", - long: "Subject Alternative Names", - }, - }, - "2.5.29.19": { - name: { - short: undefined, - long: "Basic Constraints", - }, - }, - "2.5.29.31": { - name: { - short: "CRL Endpoints", - long: "Certificate Revocation List Endpoints", - }, - }, - "2.5.29.32": { - name: { - short: undefined, - long: "Certificate Policies", - }, - }, - "2.5.29.35": { - name: { - short: "Authority Key ID", - long: "Authority Key Identifier", - }, - }, - "2.5.29.37": { - name: { - short: undefined, - long: "Extended Key Usages", - }, - }, - }, - - keyUsages: [ - "CRL Signing", - "Certificate Signing", - "Key Agreement", - "Data Encipherment", - "Key Encipherment", - "Non-Repudiation", - "Digital Signature", - ], - - san: [ - "Other Name", - "RFC 822 Name", - "DNS Name", - "X.400 Address", - "Directory Name", - "EDI Party Name", - "URI", - "IP Address", - "Registered ID", - ], - - eKU: { - "1.3.6.1.4.1.311.10.3.1": "Certificate Trust List (CTL) Signing", - "1.3.6.1.4.1.311.10.3.2": "Timestamp Signing", - "1.3.6.1.4.1.311.10.3.4": "EFS Encryption", - "1.3.6.1.4.1.311.10.3.4.1": "EFS Recovery", - "1.3.6.1.4.1.311.10.3.5": - "Windows Hardware Quality Labs (WHQL) Cryptography", - "1.3.6.1.4.1.311.10.3.7": "Windows NT 5 Cryptography", - "1.3.6.1.4.1.311.10.3.8": "Windows NT Embedded Cryptography", - "1.3.6.1.4.1.311.10.3.10": "Qualified Subordination", - "1.3.6.1.4.1.311.10.3.11": "Escrowed Key Recovery", - "1.3.6.1.4.1.311.10.3.12": "Document Signing", - "1.3.6.1.4.1.311.10.5.1": "Digital Rights Management", - "1.3.6.1.4.1.311.10.6.1": "Key Pack Licenses", - "1.3.6.1.4.1.311.10.6.2": "License Server", - "1.3.6.1.4.1.311.20.2.1": "Enrollment Agent", - "1.3.6.1.4.1.311.20.2.2": "Smartcard Login", - "1.3.6.1.4.1.311.21.5": "Certificate Authority Private Key Archival", - "1.3.6.1.4.1.311.21.6": "Key Recovery Agent", - "1.3.6.1.4.1.311.21.19": "Directory Service Email Replication", - "1.3.6.1.5.5.7.3.1": "Server Authentication", - "1.3.6.1.5.5.7.3.2": "Client Authentication", - "1.3.6.1.5.5.7.3.3": "Code Signing", - "1.3.6.1.5.5.7.3.4": "E-mail Protection", - "1.3.6.1.5.5.7.3.5": "IPsec End System", - "1.3.6.1.5.5.7.3.6": "IPsec Tunnel", - "1.3.6.1.5.5.7.3.7": "IPSec User", - "1.3.6.1.5.5.7.3.8": "Timestamping", - "1.3.6.1.5.5.7.3.9": "OCSP Signing", - "1.3.6.1.5.5.8.2.2": "Internet Key Exchange (IKE)", - }, - - signature: { - "1.2.840.113549.1.1.4": "MD5 with RSA Encryption", - "1.2.840.113549.1.1.5": "SHA-1 with RSA Encryption", - "1.2.840.113549.1.1.11": "SHA-256 with RSA Encryption", - "1.2.840.113549.1.1.12": "SHA-384 with RSA Encryption", - "1.2.840.113549.1.1.13": "SHA-512 with RSA Encryption", - "1.2.840.10040.4.3": "DSA with SHA-1", - "2.16.840.1.101.3.4.3.2": "DSA with SHA-256", - "1.2.840.10045.4.1": "ECDSA with SHA-1", - "1.2.840.10045.4.3.2": "ECDSA with SHA-256", - "1.2.840.10045.4.3.3": "ECDSA with SHA-384", - "1.2.840.10045.4.3.4": "ECDSA with SHA-512", - }, - - aia: { - "1.3.6.1.5.5.7.48.1": "Online Certificate Status Protocol (OCSP)", - "1.3.6.1.5.5.7.48.2": "CA Issuers", - }, - - // this includes qualifiers as well - cps: { - "1.3.6.1.4.1": { - name: "Statement Identifier", - value: undefined, - }, - "1.3.6.1.5.5.7.2.1": { - name: "Practices Statement", - value: undefined, - }, - "1.3.6.1.5.5.7.2.2": { - name: "User Notice", - value: undefined, - }, - "2.16.840": { - name: "ANSI Organizational Identifier", - value: undefined, - }, - "2.23.140.1.1": { - name: "Certificate Type", - value: "Extended Validation", - }, - "2.23.140.1.2.1": { - name: "Certificate Type", - value: "Domain Validation", - }, - "2.23.140.1.2.2": { - name: "Certificate Type", - value: "Organization Validation", - }, - "2.23.140.1.2.3": { - name: "Certificate Type", - value: "Individual Validation", - }, - "2.23.140.1.3": { - name: "Certificate Type", - value: "Extended Validation (Code Signing)", - }, - "2.23.140.1.31": { - name: "Certificate Type", - value: ".onion Extended Validation", - }, - "2.23.140.2.1": { - name: "Certificate Type", - value: "Test Certificate", - }, - }, - - microsoftCertificateTypes: { - Administrator: "Administrator", - CA: "Root Certification Authority", - CAExchange: "CA Exchange", - CEPEncryption: "CEP Encryption", - CertificateRequestAgent: "Certificate Request Agent", - ClientAuth: "Authenticated Session", - CodeSigning: "Code Signing", - CrossCA: "Cross Certification Authority", - CTLSigning: "Trust List Signing", - DirectoryEmailReplication: "Directory Email Replication", - DomainController: "Domain Controller", - DomainControllerAuthentication: "Domain Controller Authentication", - EFS: "Basic EFS", - EFSRecovery: "EFS Recovery Agent", - EnrollmentAgent: "Enrollment Agent", - EnrollmentAgentOffline: "Exchange Enrollment Agent (Offline request)", - ExchangeUser: "Exchange User", - ExchangeUserSignature: "Exchange Signature Only", - IPSECIntermediateOffline: "IPSec (Offline request)", - IPSECIntermediateOnline: "IPSEC", - KerberosAuthentication: "Kerberos Authentication", - KeyRecoveryAgent: "Key Recovery Agent", - Machine: "Computer", - MachineEnrollmentAgent: "Enrollment Agent (Computer)", - OCSPResponseSigning: "OCSP Response Signing", - OfflineRouter: "Router (Offline request)", - RASAndIASServer: "RAS and IAS Server", - SmartcardLogon: "Smartcard Logon", - SmartcardUser: "Smartcard User", - SubCA: "Subordinate Certification Authority", - User: "User", - UserSignature: "User Signature Only", - WebServer: "Web Server", - Workstation: "Workstation Authentication", - }, - }; - - const b64urltodec = b64 => { - return new Integer({ - valueHex: stringToArrayBuffer(fromBase64("AQAB", true, true)), - }).valueBlock._valueDec; - }; - - const b64urltohex = b64 => { - const hexBuffer = new Integer({ - valueHex: stringToArrayBuffer(fromBase64(b64, true, true)), - }).valueBlock._valueHex; - const hexArray = Array.from(new Uint8Array(hexBuffer)); - - return hexArray.map(b => ("00" + b.toString(16)).slice(-2)); - }; - - // this particular prototype override makes it easy to chain down complex objects - const getObjPath = (obj, path) => { - path = path.split("."); - for (let i = 0, len = path.length; i < len; i++) { - if (Array.isArray(obj[path[i]])) { - obj = obj[path[i]][path[i + 1]]; - i++; - } else { - obj = obj[path[i]]; - } - } - return obj; - }; - - const hash = async (algo, buffer) => { - const hashBuffer = await crypto.subtle.digest(algo, buffer); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - - return hashArray - .map(b => ("00" + b.toString(16)).slice(-2)) - .join(":") - .toUpperCase(); - }; - - const hashify = hash => { - if (typeof hash === "string") { - return hash - .match(/.{2}/g) - .join(":") - .toUpperCase(); - } - return hash.join(":").toUpperCase(); - }; - - const pemToDER = pem => { - return stringToArrayBuffer(atob(pem)); - }; - - return { parse, pemToDER }; -} - -// This must be removed when all consumer is converted to ES module. -// eslint-disable-next-line mozilla/reject-globalThis-modification -globalThis.certDecoderInitializer = certDecoderInitializer; -/* eslint-disable-next-line no-unused-vars */ -var EXPORTED_SYMBOLS = ["certDecoderInitializer"]; diff --git a/toolkit/components/certviewer/content/certDecoder.mjs b/toolkit/components/certviewer/content/certDecoder.mjs new file mode 100644 index 000000000000..a5e665e64c92 --- /dev/null +++ b/toolkit/components/certviewer/content/certDecoder.mjs @@ -0,0 +1,1270 @@ +/* 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 { + Certificate, + ECNamedCurves, + ECPublicKey, + RSAPublicKey, +} from "./vendor/pkijs.js"; + +const getTimeZone = () => { + let timeZone = new Date().toString().match(/\(([A-Za-z\s].*)\)/); + if (timeZone === null) { + // America/Chicago + timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + } else if (timeZone.length > 1) { + timeZone = timeZone[1]; // Central Daylight Time + } else { + timeZone = "Local Time"; // not sure if this is right, but let's go with it for now + } + return timeZone; +}; + +const getPublicKeyInfo = x509 => { + let publicKey = x509.subjectPublicKeyInfo.parsedKey; + if (publicKey instanceof RSAPublicKey) { + let modulusJSON = publicKey.modulus.toJSON(); + let modulusHex = modulusJSON.valueBlock.valueHex; + return { + e: publicKey.publicExponent.toJSON().valueBlock.valueDec, + kty: "RSA", + n: hashify(modulusHex), + keysize: modulusHex.length * 4, // key size in bits + }; + } + if (publicKey instanceof ECPublicKey) { + let x = hashify(publicKey.x); + let y = hashify(publicKey.y); + let curve = ECNamedCurves.find(publicKey.namedCurve); + let keysize = curve ? curve.size * 8 : undefined; + return { + kty: "Elliptic Curve", + keysize, + x, // x coordinate + y, // y coordinate + xy: `04:${x}:${y}`, // 04 (uncompressed) public key + }; + } + return { kty: "Unknown" }; +}; + +const getX509Ext = (extensions, v) => { + for (var extension in extensions) { + if (extensions[extension].extnID === v) { + return extensions[extension].toJSON().parsedValue; + } + } + return undefined; +}; + +const getKeyUsages = (x509, criticalExtensions) => { + let keyUsages = { + critical: criticalExtensions.includes("2.5.29.15"), + purposes: [], + }; + + let keyUsagesBS = getX509Ext(x509.extensions, "2.5.29.15"); + if (keyUsagesBS !== undefined) { + // parse the bit string, shifting as necessary + let unusedBits = keyUsagesBS.valueBlock.unusedBits; + keyUsagesBS = parseInt(keyUsagesBS.valueBlock.valueHex, 16) >> unusedBits; + + // iterate through the bit string + strings.keyUsages.slice(unusedBits - 1).forEach(usage => { + if (keyUsagesBS & 1) { + keyUsages.purposes.push(usage); + } + + keyUsagesBS = keyUsagesBS >> 1; + }); + + // reverse the order for legibility + keyUsages.purposes.reverse(); + } + + return keyUsages; +}; + +const parseSubsidiary = distinguishedNames => { + const subsidiary = { + cn: "", + dn: [], + entries: [], + }; + + distinguishedNames.forEach(dn => { + const name = strings.names[dn.type]; + const value = dn.value.valueBlock.value; + + if (name === undefined) { + subsidiary.dn.push(`OID.${dn.type}=${value}`); + subsidiary.entries.push([`OID.${dn.type}`, value]); + } else if (name.short === undefined) { + subsidiary.dn.push(`OID.${dn.type}=${value}`); + subsidiary.entries.push([name.long, value]); + } else { + subsidiary.dn.push(`${name.short}=${value}`); + subsidiary.entries.push([name.long, value]); + + // add the common name for tab display + if (name.short === "cn") { + subsidiary.cn = value; + } + } + }); + + // turn path into a string + subsidiary.dn = subsidiary.dn.join(", "); + + return subsidiary; +}; + +const getSubjectAltNames = (x509, criticalExtensions) => { + let san = getX509Ext(x509.extensions, "2.5.29.17"); + if (san && san.hasOwnProperty("altNames")) { + san = Object.keys(san.altNames).map(index => { + const type = san.altNames[index].type; + + switch (type) { + case 4: // directory + return [ + strings.san[type], + parseSubsidiary(san.altNames[index].value.typesAndValues).dn, + ]; + case 7: // ip address + let address = san.altNames[index].value.valueBlock.valueHex; + + if (address.length === 8) { + // ipv4 + return [ + strings.san[type], + address + .match(/.{1,2}/g) + .map(x => parseInt(x, 16)) + .join("."), + ]; + } else if (address.length === 32) { + // ipv6 + return [ + strings.san[type], + address + .toLowerCase() + .match(/.{1,4}/g) + .join(":") + .replace(/\b:?(?:0+:?){2,}/, "::"), + ]; + } + return [strings.san[type], "Unknown IP address"]; + + default: + return [strings.san[type], san.altNames[index].value]; + } + }); + } else { + san = []; + } + san = { + altNames: san, + critical: criticalExtensions.includes("2.5.29.17"), + }; + return san; +}; + +const getBasicConstraints = (x509, criticalExtensions) => { + let basicConstraints; + const basicConstraintsExt = getX509Ext(x509.extensions, "2.5.29.19"); + if (basicConstraintsExt) { + basicConstraints = { + cA: basicConstraintsExt.cA !== undefined && basicConstraintsExt.cA, + critical: criticalExtensions.includes("2.5.29.19"), + }; + } + return basicConstraints; +}; + +const getEKeyUsages = (x509, criticalExtensions) => { + let eKeyUsages = getX509Ext(x509.extensions, "2.5.29.37"); + if (eKeyUsages) { + eKeyUsages = { + critical: criticalExtensions.includes("2.5.29.37"), + purposes: eKeyUsages.keyPurposes.map(x => strings.eKU[x] || x), + }; + } + return eKeyUsages; +}; + +const getSubjectKeyID = (x509, criticalExtensions) => { + let sKID = getX509Ext(x509.extensions, "2.5.29.14"); + if (sKID) { + sKID = { + critical: criticalExtensions.includes("2.5.29.14"), + id: hashify(sKID.valueBlock.valueHex), + }; + } + return sKID; +}; + +const getAuthorityKeyID = (x509, criticalExtensions) => { + let aKID = getX509Ext(x509.extensions, "2.5.29.35"); + if (!aKID || !aKID.keyIdentifier) { + return null; + } + aKID = { + critical: criticalExtensions.includes("2.5.29.35"), + id: hashify(aKID.keyIdentifier.valueBlock.valueHex), + }; + return aKID; +}; + +const getCRLPoints = (x509, criticalExtensions) => { + let crlPoints = getX509Ext(x509.extensions, "2.5.29.31"); + if (crlPoints) { + crlPoints = { + critical: criticalExtensions.includes("2.5.29.31"), + points: crlPoints.distributionPoints.map( + x => x.distributionPoint[0].value + ), + }; + } + return crlPoints; +}; + +const getOcspStaple = (x509, criticalExtensions) => { + let ocspStaple = getX509Ext(x509.extensions, "1.3.6.1.5.5.7.1.24"); + if (ocspStaple && ocspStaple.valueBeforeDecode === "3003020105") { + ocspStaple = { + critical: criticalExtensions.includes("1.3.6.1.5.5.7.1.24"), + required: true, + }; + } else { + ocspStaple = { + critical: criticalExtensions.includes("1.3.6.1.5.5.7.1.24"), + required: false, + }; + } + return ocspStaple; +}; + +const getAuthorityInfoAccess = (x509, criticalExtensions) => { + let aia = getX509Ext(x509.extensions, "1.3.6.1.5.5.7.1.1"); + if (aia) { + aia = aia.accessDescriptions.map(x => { + return { + location: x.accessLocation.value, + method: strings.aia[x.accessMethod], + }; + }); + } + + aia = { + descriptions: aia, + critical: criticalExtensions.includes("1.3.6.1.5.5.7.1.1"), + }; + return aia; +}; + +const getSCTs = (x509, criticalExtensions) => { + let scts = getX509Ext(x509.extensions, "1.3.6.1.4.1.11129.2.4.2"); + if (scts) { + scts = Object.keys(scts.timestamps).map(x => { + let logId = scts.timestamps[x].logID.toLowerCase(); + let sctsTimestamp = scts.timestamps[x].timestamp; + return { + logId: hashify(logId), + name: ctLogNames.hasOwnProperty(logId) ? ctLogNames[logId] : undefined, + signatureAlgorithm: `${scts.timestamps[x].hashAlgorithm.replace( + "sha", + "SHA-" + )} ${scts.timestamps[x].signatureAlgorithm.toUpperCase()}`, + timestamp: `${sctsTimestamp.toLocaleString()} (${getTimeZone()})`, + timestampUTC: sctsTimestamp.toUTCString(), + version: scts.timestamps[x].version + 1, + }; + }); + } else { + scts = []; + } + + scts = { + critical: criticalExtensions.includes("1.3.6.1.4.1.11129.2.4.2"), + timestamps: scts, + }; + return scts; +}; + +const getCertificatePolicies = (x509, criticalExtensions) => { + let cp = getX509Ext(x509.extensions, "2.5.29.32"); + if (cp && cp.hasOwnProperty("certificatePolicies")) { + cp = cp.certificatePolicies.map(x => { + let id = x.policyIdentifier; + let name = strings.cps.hasOwnProperty(id) + ? strings.cps[id].name + : undefined; + let qualifiers = undefined; + let value = strings.cps.hasOwnProperty(id) + ? strings.cps[id].value + : undefined; + + // ansi organization identifiers + if (id.startsWith("2.16.840.")) { + value = id; + id = "2.16.840"; + name = strings.cps["2.16.840"].name; + } + + // statement identifiers + if (id.startsWith("1.3.6.1.4.1")) { + value = id; + id = "1.3.6.1.4.1"; + name = strings.cps["1.3.6.1.4.1"].name; + } + + if (x.hasOwnProperty("policyQualifiers")) { + qualifiers = x.policyQualifiers.map(qualifier => { + let qualifierId = qualifier.policyQualifierId; + let qualifierName = strings.cps.hasOwnProperty(qualifierId) + ? strings.cps[qualifierId].name + : undefined; + let qualifierValue = qualifier.qualifier.valueBlock.value; + + // sometimes they are multiple qualifier subblocks, and for now we'll + // only return the first one because it's getting really messy at this point + if (Array.isArray(qualifierValue) && qualifierValue.length === 1) { + qualifierValue = qualifierValue[0].valueBlock.value; + } else if ( + Array.isArray(qualifierValue) && + qualifierValue.length > 1 + ) { + qualifierValue = "(currently unsupported)"; + } + + return { + qualifierId, + qualifierName, + qualifierValue, + }; + }); + } + + return { + id, + name, + qualifiers, + value, + }; + }); + } + + cp = { + critical: criticalExtensions.includes("2.5.29.32"), + policies: cp, + }; + return cp; +}; + +const getMicrosoftCryptographicExtensions = (x509, criticalExtensions) => { + // now let's parse the Microsoft cryptographic extensions + let msCrypto = { + caVersion: getX509Ext(x509.extensions, "1.3.6.1.4.1.311.21.1"), + certificatePolicies: getX509Ext(x509.extensions, "1.3.6.1.4.1.311.21.10"), + certificateTemplate: getX509Ext(x509.extensions, "1.3.6.1.4.1.311.21.7"), + certificateType: getX509Ext(x509.extensions, "1.3.6.1.4.1.311.20.2"), + previousHash: getX509Ext(x509.extensions, "1.3.6.1.4.1.311.21.2"), + }; + + if ( + msCrypto.caVersion && + Number.isInteger(msCrypto.caVersion.keyIndex) && + Number.isInteger(msCrypto.caVersion.certificateIndex) + ) { + msCrypto.caVersion = { + critical: criticalExtensions.includes("1.3.6.1.4.1.311.21.1"), + caRenewals: msCrypto.caVersion.certificateIndex, + keyReuses: + msCrypto.caVersion.certificateIndex - msCrypto.caVersion.keyIndex, + }; + } + + if (msCrypto.certificatePolicies) { + msCrypto.certificatePolicies = { + critical: criticalExtensions.includes("1.3.6.1.4.1.311.21.10"), + purposes: msCrypto.certificatePolicies.certificatePolicies.map( + x => strings.eKU[x.policyIdentifier] || x.policyIdentifier + ), + }; + } + + if (msCrypto.certificateTemplate) { + msCrypto.certificateTemplate = { + critical: criticalExtensions.includes("1.3.6.1.4.1.311.21.7"), + id: msCrypto.certificateTemplate.extnID, + major: msCrypto.certificateTemplate.templateMajorVersion, + minor: msCrypto.certificateTemplate.templateMinorVersion, + }; + } + + if (msCrypto.certificateType) { + msCrypto.certificateType = { + critical: criticalExtensions.includes("1.3.6.1.4.1.311.20.2"), + type: + strings.microsoftCertificateTypes[ + msCrypto.certificateType.valueBlock.value + ] || "Unknown", + }; + } + + if (msCrypto.previousHash) { + msCrypto.previousHash = { + critical: criticalExtensions.includes("1.3.6.1.4.1.311.21.2"), + previousHash: hashify(msCrypto.previousHash.valueBlock.valueHex), + }; + } + + msCrypto.exists = !!( + msCrypto.caVersion || + msCrypto.certificatePolicies || + msCrypto.certificateTemplate || + msCrypto.certificateType || + msCrypto.previousHash + ); + + return msCrypto; +}; + +const b64ToPEM = string => { + let wrapped = string.match(/.{1,64}/g).join("\r\n"); + return `-----BEGIN CERTIFICATE-----\r\n${wrapped}\r\n-----END CERTIFICATE-----\r\n`; +}; + +export const parse = async certificate => { + // certificate could be an array of BER or an array of buffers + const supportedExtensions = [ + "1.3.6.1.4.1.311.20.2", // microsoft certificate type + "1.3.6.1.4.1.311.21.2", // microsoft certificate previous hash + "1.3.6.1.4.1.311.21.7", // microsoft certificate template + "1.3.6.1.4.1.311.21.1", // microsoft certification authority renewal + "1.3.6.1.4.1.311.21.10", // microsoft certificate policies + "1.3.6.1.4.1.11129.2.4.2", // embedded scts + "1.3.6.1.5.5.7.1.1", // authority info access + "1.3.6.1.5.5.7.1.24", // ocsp stapling + "1.3.101.77", // ct redaction - deprecated and not displayed + "2.5.29.14", // subject key identifier + "2.5.29.15", // key usages + "2.5.29.17", // subject alt names + "2.5.29.19", // basic constraints + "2.5.29.31", // crl points + "2.5.29.32", // certificate policies + "2.5.29.35", // authority key identifier + "2.5.29.37", // extended key usage + ]; + + let timeZone = getTimeZone(); + + // parse the certificate + let x509 = Certificate.fromBER(certificate); + + // convert the cert to PEM + const certPEM = b64ToPEM( + btoa(String.fromCharCode.apply(null, new Uint8Array(certificate))) + ); + + // get which extensions are critical + const criticalExtensions = []; + if (x509.extensions) { + x509.extensions.forEach(ext => { + if (ext.hasOwnProperty("critical") && ext.critical === true) { + criticalExtensions.push(ext.extnID); + } + }); + } + const spki = getPublicKeyInfo(x509); + const keyUsages = getKeyUsages(x509, criticalExtensions); + const san = getSubjectAltNames(x509, criticalExtensions); + const basicConstraints = getBasicConstraints(x509, criticalExtensions); + const eKeyUsages = getEKeyUsages(x509, criticalExtensions); + const sKID = getSubjectKeyID(x509, criticalExtensions); + const aKID = getAuthorityKeyID(x509, criticalExtensions); + const crlPoints = getCRLPoints(x509, criticalExtensions); + const ocspStaple = getOcspStaple(x509, criticalExtensions); + const aia = getAuthorityInfoAccess(x509, criticalExtensions); + const scts = getSCTs(x509, criticalExtensions); + const cp = getCertificatePolicies(x509, criticalExtensions); + const msCrypto = getMicrosoftCryptographicExtensions( + x509, + criticalExtensions + ); + + // determine which extensions weren't supported + let unsupportedExtensions = []; + if (x509.extensions) { + x509.extensions.forEach(ext => { + if (!supportedExtensions.includes(ext.extnID)) { + unsupportedExtensions.push(ext.extnID); + } + }); + } + + // the output shell + return { + ext: { + aia, + aKID, + basicConstraints, + crlPoints, + cp, + eKeyUsages, + keyUsages, + msCrypto, + ocspStaple, + scts, + sKID, + san, + }, + files: { + der: undefined, // TODO: implement! + pem: encodeURI(certPEM), + }, + fingerprint: { + sha1: await hash("SHA-1", certificate), + sha256: await hash("SHA-256", certificate), + }, + issuer: parseSubsidiary(x509.issuer.typesAndValues), + notBefore: `${x509.notBefore.value.toLocaleString()} (${timeZone})`, + notBeforeUTC: x509.notBefore.value.toUTCString(), + notAfter: `${x509.notAfter.value.toLocaleString()} (${timeZone})`, + notAfterUTC: x509.notAfter.value.toUTCString(), + subject: parseSubsidiary(x509.subject.typesAndValues), + serialNumber: hashify(getObjPath(x509, "serialNumber.valueBlock.valueHex")), + signature: { + name: strings.signature[getObjPath(x509, "signature.algorithmId")], + type: getObjPath(x509, "signature.algorithmId"), + }, + subjectPublicKeyInfo: spki, + unsupportedExtensions, + version: (x509.version + 1).toString(), + }; +}; + +const ctLogNames = { + "9606c02c690033aa1d145f59c6e2648d0549f0df96aab8db915a70d8ecf390a5": + "Akamai CT", + "39376f545f7b4607f59742d768cd5d2437bf3473b6534a4834bcf72e681c83c9": + "Alpha CT", + a577ac9ced7548dd8f025b67a241089df86e0f476ec203c2ecbedb185f282638: "CNNIC CT", + cdb5179b7fc1c046feea31136a3f8f002e6182faf8896fecc8b2f5b5ab604900: "Certly.IO", + "1fbc36e002ede97f40199e86b3573b8a4217d80187746ad0da03a06054d20df4": + "Cloudflare “Nimbus2017”", + db74afeecb29ecb1feca3e716d2ce5b9aabb36f7847183c75d9d4f37b61fbf64: + "Cloudflare “Nimbus2018”", + "747eda8331ad331091219cce254f4270c2bffd5e422008c6373579e6107bcc56": + "Cloudflare “Nimbus2019”", + "5ea773f9df56c0e7b536487dd049e0327a919a0c84a112128418759681714558": + "Cloudflare “Nimbus2020”", + "4494652eb0eeceafc44007d8a8fe28c0dae682bed8cb31b53fd33396b5b681a8": + "Cloudflare “Nimbus2021”", + "41c8cab1df22464a10c6a13a0942875e4e318b1b03ebeb4bc768f090629606f6": + "Cloudflare “Nimbus2022”", + "7a328c54d8b72db620ea38e0521ee98416703213854d3bd22bc13a57a352eb52": + "Cloudflare “Nimbus2023”", + "6ff141b5647e4222f7ef052cefae7c21fd608e27d2af5a6e9f4b8a37d6633ee5": + "DigiCert Nessie2018", + fe446108b1d01ab78a62ccfeab6ab2b2babff3abdad80a4d8b30df2d0008830c: + "DigiCert Nessie2019", + c652a0ec48ceb3fcab170992c43a87413309e80065a26252401ba3362a17c565: + "DigiCert Nessie2020", + eec095ee8d72640f92e3c3b91bc712a3696a097b4b6a1a1438e647b2cbedc5f9: + "DigiCert Nessie2021", + "51a3b0f5fd01799c566db837788f0ca47acc1b27cbf79e88429a0dfed48b05e5": + "DigiCert Nessie2022", + b3737707e18450f86386d605a9dc11094a792db1670c0b87dcf0030e7936a59a: + "DigiCert Nessie2023", + "5614069a2fd7c2ecd3f5e1bd44b23ec74676b9bc99115cc0ef949855d689d0dd": + "DigiCert Server", + "8775bfe7597cf88c43995fbdf36eff568d475636ff4ab560c1b4eaff5ea0830f": + "DigiCert Server 2", + c1164ae0a772d2d4392dc80ac10770d4f0c49bde991a4840c1fa075164f63360: + "DigiCert Yeti2018", + e2694bae26e8e94009e8861bb63b83d43ee7fe7488fba48f2893019dddf1dbfe: + "DigiCert Yeti2019", + f095a459f200d18240102d2f93888ead4bfe1d47e399e1d034a6b0a8aa8eb273: + "DigiCert Yeti2020", + "5cdc4392fee6ab4544b15e9ad456e61037fbd5fa47dca17394b25ee6f6c70eca": + "DigiCert Yeti2021", + "2245450759552456963fa12ff1f76d86e0232663adc04b7f5dc6835c6ee20f02": + "DigiCert Yeti2022", + "35cf191bbfb16c57bf0fad4c6d42cbbbb627202651ea3fe12aefa803c33bd64c": + "DigiCert Yeti2023", + "717ea7420975be84a2723553f1777c26dd51af4e102144094d9019b462fb6668": "GDCA 1", + "14308d90ccd030135005c01ca526d81e84e87624e39b6248e08f724aea3bb42a": "GDCA 2", + c9cf890a21109c666cc17a3ed065c930d0e0135a9feba85af14210b8072421aa: + "GDCA CT #1", + "924a30f909336ff435d6993a10ac75a2c641728e7fc2d659ae6188ffad40ce01": + "GDCA CT #2", + fad4c97cc49ee2f8ac85c5ea5cea09d0220dbbf4e49c6b50662ff868f86b8c28: + "Google “Argon2017”", + a4501269055a15545e6211ab37bc103f62ae5576a45e4b1714453e1b22106a25: + "Google “Argon2018”", + "63f2dbcde83bcc2ccf0b728427576b33a48d61778fbd75a638b1c768544bd88d": + "Google “Argon2019”", + b21e05cc8ba2cd8a204e8766f92bb98a2520676bdafa70e7b249532def8b905e: + "Google “Argon2020”", + f65c942fd1773022145418083094568ee34d131933bfdf0c2f200bcc4ef164e3: + "Google “Argon2021”", + "2979bef09e393921f056739f63a577e5be577d9c600af8f94d5d265c255dc784": + "Google “Argon2022”", + "68f698f81f6482be3a8ceeb9281d4cfc71515d6793d444d10a67acbb4f4ffbc4": + "Google “Aviator”", + c3bf03a7e1ca8841c607bae3ff4270fca5ec45b186ebbe4e2cf3fc778630f5f6: + "Google “Crucible”", + "1d024b8eb1498b344dfd87ea3efc0996f7506f235d1d497061a4773c439c25fb": + "Google “Daedalus”", + "293c519654c83965baaa50fc5807d4b76fbf587a2972dca4c30cf4e54547f478": + "Google “Icarus”", + a4b90990b418581487bb13a2cc67700a3c359804f91bdfb8e377cd0ec80ddc10: + "Google “Pilot”", + ee4bbdb775ce60bae142691fabe19e66a30f7e5fb072d88300c47b897aa8fdcb: + "Google “Rocketeer”", + bbd9dfbc1f8a71b593942397aa927b473857950aab52e81a909664368e1ed185: + "Google “Skydiver”", + "52eb4b225ec896974850675f23e43bc1d021e3214ce52ecd5fa87c203cdfca03": + "Google “Solera2018”", + "0b760e9a8b9a682f88985b15e947501a56446bba8830785c3842994386450c00": + "Google “Solera2019”", + "1fc72ce5a1b799f400c359bff96ca3913548e8644220610952e9ba1774f7bac7": + "Google “Solera2020”", + a3c99845e80ab7ce00157b3742df0207dd272b2b602ecf98ee2c12db9c5ae7e7: + "Google “Solera2021”", + "697aafca1a6b536fae21205046debad7e0eaea13d2432e6e9d8fb379f2b9aaf3": + "Google “Solera2022”", + a899d8780c9290aaf462f31880ccfbd52451e970d0fbf591ef75b0d99b645681: + "Google “Submariner”", + b0cc83e5a5f97d6baf7c09cc284904872ac7e88b132c6350b7c6fd26e16c6c77: + "Google “Testtube”", + b10cd559a6d67846811f7df9a51532739ac48d703bea0323da5d38755bc0ad4e: + "Google “Xenon2018”", + "084114980071532c16190460bcfc47fdc2653afa292c72b37ff863ae29ccc9f0": + "Google “Xenon2019”", + "07b75c1be57d68fff1b0c61d2315c7bae6577c5794b76aeebc613a1a69d3a21c": + "Google “Xenon2020”", + "7d3ef2f88fff88556824c2c0ca9e5289792bc50e78097f2e6a9768997e22f0d7": + "Google “Xenon2021”", + "46a555eb75fa912030b5a28969f4f37d112c4174befd49b885abf2fc70fe6d47": + "Google “Xenon2022”", + "7461b4a09cfb3d41d75159575b2e7649a445a8d27709b0cc564a6482b7eb41a3": "Izenpe", + "8941449c70742e06b9fc9ce7b116ba0024aa36d59af44f0204404f00f7ea8566": + "Izenpe “Argi”", + "296afa2d568bca0d2ea844956ae9721fc35fa355ecda99693aafd458a71aefdd": + "Let“s Encrypt ”Clicky”", + "537b69a3564335a9c04904e39593b2c298eb8d7a6e83023635c627248cd6b440": + "Nordu “flimsy”", + aae70b7f3cb8d566c86c2f16979c9f445f69ab0eb4535589b2f77a030104f3cd: + "Nordu “plausible”", + e0127629e90496564e3d0147984498aa48f8adb16600eb7902a1ef9909906273: + "PuChuangSiDa CT", + cf55e28923497c340d5206d05353aeb25834b52f1f8dc9526809f212efdd7ca6: + "SHECA CT 1", + "32dc59c2d4c41968d56e14bc61ac8f0e45db39faf3c155aa4252f5001fa0c623": + "SHECA CT 2", + db76fdadac65e7d09508886e2159bd8b90352f5fead3e3dc5e22eb350acc7b98: + "Sectigo (Comodo) “Dodo” CT", + "6f5376ac31f03119d89900a45115ff77151c11d902c10029068db2089a37d913": + "Sectigo (Comodo) “Mammoth” CT", + "5581d4c2169036014aea0b9b573c53f0c0e43878702508172fa3aa1d0713d30c": + "Sectigo (Comodo) “Sabre” CT", + "34bb6ad6c3df9c03eea8a499ff7891486c9d5e5cac92d01f7bfd1bce19db48ef": + "StartCom", + ddeb1d2b7a0d4fa6208b81ad8168707e2e8e9d01d55c888d3d11c4cdb6ecbecc: "Symantec", + a7ce4a4e6207e0addee5fdaa4b1f86768767b5d002a55d47310e7e670a95eab2: + "Symantec Deneb", + "15970488d7b997a05beb52512adee8d2e8b4a3165264121a9fabfbd5f85ad93f": + "Symantec “Sirius”", + bc78e1dfc5f63c684649334da10fa15f0979692009c081b4f3f6917f3ed9b8a5: + "Symantec “Vega”", + b0b784bc81c0ddc47544e883f05985bb9077d134d8ab88b2b2e533980b8e508b: + "Up In The Air “Behind the Sofa”", + ac3b9aed7fa9674757159e6d7d575672f9d98100941e9bdeffeca1313b75782d: "Venafi", + "03019df3fd85a69a8ebd1facc6da9ba73e469774fe77f579fc5a08b8328c1d6b": + "Venafi Gen2 CT", + "41b2dc2e89e63ce4af1ba7bb29bf68c6dee6f9f1cc047e30dffae3b3ba259263": "WoSign", + "63d0006026dde10bb0601f452446965ee2b6ea2cd4fbc95ac866a550af9075b7": + "WoSign 2", + "9e4ff73dc3ce220b69217c899e468076abf8d78636d5ccfc85a31a75628ba88b": + "WoSign CT #1", + "659b3350f43b12cc5ea5ab4ec765d3fde6c88243777778e72003f9eb2b8c3129": + "Let's Encrypt Oak 2019", + e712f2b0377e1a62fb8ec90c6184f1ea7b37cb561d11265bf3e0f34bf241546e: + "Let's Encrypt Oak 2020", + "9420bc1e8ed58d6c88731f828b222c0dd1da4d5e6c4f943d61db4e2f584da2c2": + "Let's Encrypt Oak 2021", + dfa55eab68824f1f6cadeeb85f4e3e5aeacda212a46a5e8e3b12c020445c2a73: + "Let's Encrypt Oak 2022", + b73efb24df9c4dba75f239c5ba58f46c5dfc42cf7a9f35c49e1d098125edb499: + "Let's Encrypt Oak 2023", + "849f5f7f58d2bf7b54ecbd74611cea45c49c98f1d6481bc6f69e8c174f24f3cf": + "Let's Encrypt Testflume 2019", + c63f2218c37d56a6aa06b596da8e53d4d7156d1e9bac8e44d2202de64d69d9dc: + "Let's Encrypt Testflume 2020", + "03edf1da9776b6f38c341e39ed9d707a7570369cf9844f327fe9e14138361b60": + "Let's Encrypt Testflume 2021", + "2327efda352510dbc019ef491ae3ff1cc5a479bce37878360ee318cffb64f8c8": + "Let's Encrypt Testflume 2022", + "5534b7ab5a6ac3a7cbeba65487b2a2d71b48f650fa17c5197c97a0cb2076f3c6": + "Let's Encrypt Testflume 2023", +}; + +const strings = { + ux: { + upload: "Upload Certificate", + }, + + names: { + // Directory Pilot Attributes + "0.9.2342.19200300.100.1.1": { + short: "uid", + long: "User ID", + }, + "0.9.2342.19200300.100.1.25": { + short: "dc", + long: "Domain Component", + }, + + // PKCS-9 + "1.2.840.113549.1.9.1": { + short: "e", + long: "Email Address", + }, + + // Incorporated Locations + "1.3.6.1.4.1.311.60.2.1.1": { + short: undefined, + long: "Inc. Locality", + }, + "1.3.6.1.4.1.311.60.2.1.2": { + short: undefined, + long: "Inc. State / Province", + }, + "1.3.6.1.4.1.311.60.2.1.3": { + short: undefined, + long: "Inc. Country", + }, + + // microsoft cryptographic extensions + "1.3.6.1.4.1.311.21.7": { + name: { + short: "Certificate Template", + long: "Microsoft Certificate Template", + }, + }, + "1.3.6.1.4.1.311.21.10": { + name: { + short: "Certificate Policies", + long: "Microsoft Certificate Policies", + }, + }, + + // certificate extensions + "1.3.6.1.4.1.11129.2.4.2": { + name: { + short: "Embedded SCTs", + long: "Embedded Signed Certificate Timestamps", + }, + }, + "1.3.6.1.5.5.7.1.1": { + name: { + short: undefined, + long: "Authority Information Access", + }, + }, + "1.3.6.1.5.5.7.1.24": { + name: { + short: "OCSP Stapling", + long: "Online Certificate Status Protocol Stapling", + }, + }, + + // X.500 attribute types + "2.5.4.1": { + short: undefined, + long: "Aliased Entry", + }, + "2.5.4.2": { + short: undefined, + long: "Knowledge Information", + }, + "2.5.4.3": { + short: "cn", + long: "Common Name", + }, + "2.5.4.4": { + short: "sn", + long: "Surname", + }, + "2.5.4.5": { + short: "serialNumber", + long: "Serial Number", + }, + "2.5.4.6": { + short: "c", + long: "Country", + }, + "2.5.4.7": { + short: "l", + long: "Locality", + }, + "2.5.4.8": { + short: "s", + long: "State / Province", + }, + "2.5.4.9": { + short: "street", + long: "Stress Address", + }, + "2.5.4.10": { + short: "o", + long: "Organization", + }, + "2.5.4.11": { + short: "ou", + long: "Organizational Unit", + }, + "2.5.4.12": { + short: "t", + long: "Title", + }, + "2.5.4.13": { + short: "description", + long: "Description", + }, + "2.5.4.14": { + short: undefined, + long: "Search Guide", + }, + "2.5.4.15": { + short: undefined, + long: "Business Category", + }, + "2.5.4.16": { + short: undefined, + long: "Postal Address", + }, + "2.5.4.17": { + short: "postalCode", + long: "Postal Code", + }, + "2.5.4.18": { + short: "POBox", + long: "PO Box", + }, + "2.5.4.19": { + short: undefined, + long: "Physical Delivery Office Name", + }, + "2.5.4.20": { + short: "phone", + long: "Phone Number", + }, + "2.5.4.21": { + short: undefined, + long: "Telex Number", + }, + "2.5.4.22": { + short: undefined, + long: "Teletex Terminal Identifier", + }, + "2.5.4.23": { + short: undefined, + long: "Fax Number", + }, + "2.5.4.24": { + short: undefined, + long: "X.121 Address", + }, + "2.5.4.25": { + short: undefined, + long: "International ISDN Number", + }, + "2.5.4.26": { + short: undefined, + long: "Registered Address", + }, + "2.5.4.27": { + short: undefined, + long: "Destination Indicator", + }, + "2.5.4.28": { + short: undefined, + long: "Preferred Delivery Method", + }, + "2.5.4.29": { + short: undefined, + long: "Presentation Address", + }, + "2.5.4.30": { + short: undefined, + long: "Supported Application Context", + }, + "2.5.4.31": { + short: undefined, + long: "Member", + }, + "2.5.4.32": { + short: undefined, + long: "Owner", + }, + "2.5.4.33": { + short: undefined, + long: "Role Occupant", + }, + "2.5.4.34": { + short: undefined, + long: "See Also", + }, + "2.5.4.35": { + short: undefined, + long: "User Password", + }, + "2.5.4.36": { + short: undefined, + long: "User Certificate", + }, + "2.5.4.37": { + short: undefined, + long: "CA Certificate", + }, + "2.5.4.38": { + short: undefined, + long: "Authority Revocation List", + }, + "2.5.4.39": { + short: undefined, + long: "Certificate Revocation List", + }, + "2.5.4.40": { + short: undefined, + long: "Cross-certificate Pair", + }, + "2.5.4.41": { + short: undefined, + long: "Name", + }, + "2.5.4.42": { + short: "g", + long: "Given Name", + }, + "2.5.4.43": { + short: "i", + long: "Initials", + }, + "2.5.4.44": { + short: undefined, + long: "Generation Qualifier", + }, + "2.5.4.45": { + short: undefined, + long: "Unique Identifier", + }, + "2.5.4.46": { + short: undefined, + long: "DN Qualifier", + }, + "2.5.4.47": { + short: undefined, + long: "Enhanced Search Guide", + }, + "2.5.4.48": { + short: undefined, + long: "Protocol Information", + }, + "2.5.4.49": { + short: "dn", + long: "Distinguished Name", + }, + "2.5.4.50": { + short: undefined, + long: "Unique Member", + }, + "2.5.4.51": { + short: undefined, + long: "House Identifier", + }, + "2.5.4.52": { + short: undefined, + long: "Supported Algorithms", + }, + "2.5.4.53": { + short: undefined, + long: "Delta Revocation List", + }, + "2.5.4.58": { + short: undefined, + long: "Attribute Certificate Attribute", // huh + }, + "2.5.4.65": { + short: undefined, + long: "Pseudonym", + }, + + // extensions + "2.5.29.14": { + name: { + short: "Subject Key ID", + long: "Subject Key Identifier", + }, + }, + "2.5.29.15": { + name: { + short: undefined, + long: "Key Usages", + }, + }, + "2.5.29.17": { + name: { + short: "Subject Alt Names", + long: "Subject Alternative Names", + }, + }, + "2.5.29.19": { + name: { + short: undefined, + long: "Basic Constraints", + }, + }, + "2.5.29.31": { + name: { + short: "CRL Endpoints", + long: "Certificate Revocation List Endpoints", + }, + }, + "2.5.29.32": { + name: { + short: undefined, + long: "Certificate Policies", + }, + }, + "2.5.29.35": { + name: { + short: "Authority Key ID", + long: "Authority Key Identifier", + }, + }, + "2.5.29.37": { + name: { + short: undefined, + long: "Extended Key Usages", + }, + }, + }, + + keyUsages: [ + "CRL Signing", + "Certificate Signing", + "Key Agreement", + "Data Encipherment", + "Key Encipherment", + "Non-Repudiation", + "Digital Signature", + ], + + san: [ + "Other Name", + "RFC 822 Name", + "DNS Name", + "X.400 Address", + "Directory Name", + "EDI Party Name", + "URI", + "IP Address", + "Registered ID", + ], + + eKU: { + "1.3.6.1.4.1.311.10.3.1": "Certificate Trust List (CTL) Signing", + "1.3.6.1.4.1.311.10.3.2": "Timestamp Signing", + "1.3.6.1.4.1.311.10.3.4": "EFS Encryption", + "1.3.6.1.4.1.311.10.3.4.1": "EFS Recovery", + "1.3.6.1.4.1.311.10.3.5": + "Windows Hardware Quality Labs (WHQL) Cryptography", + "1.3.6.1.4.1.311.10.3.7": "Windows NT 5 Cryptography", + "1.3.6.1.4.1.311.10.3.8": "Windows NT Embedded Cryptography", + "1.3.6.1.4.1.311.10.3.10": "Qualified Subordination", + "1.3.6.1.4.1.311.10.3.11": "Escrowed Key Recovery", + "1.3.6.1.4.1.311.10.3.12": "Document Signing", + "1.3.6.1.4.1.311.10.5.1": "Digital Rights Management", + "1.3.6.1.4.1.311.10.6.1": "Key Pack Licenses", + "1.3.6.1.4.1.311.10.6.2": "License Server", + "1.3.6.1.4.1.311.20.2.1": "Enrollment Agent", + "1.3.6.1.4.1.311.20.2.2": "Smartcard Login", + "1.3.6.1.4.1.311.21.5": "Certificate Authority Private Key Archival", + "1.3.6.1.4.1.311.21.6": "Key Recovery Agent", + "1.3.6.1.4.1.311.21.19": "Directory Service Email Replication", + "1.3.6.1.5.5.7.3.1": "Server Authentication", + "1.3.6.1.5.5.7.3.2": "Client Authentication", + "1.3.6.1.5.5.7.3.3": "Code Signing", + "1.3.6.1.5.5.7.3.4": "E-mail Protection", + "1.3.6.1.5.5.7.3.5": "IPsec End System", + "1.3.6.1.5.5.7.3.6": "IPsec Tunnel", + "1.3.6.1.5.5.7.3.7": "IPSec User", + "1.3.6.1.5.5.7.3.8": "Timestamping", + "1.3.6.1.5.5.7.3.9": "OCSP Signing", + "1.3.6.1.5.5.8.2.2": "Internet Key Exchange (IKE)", + }, + + signature: { + "1.2.840.113549.1.1.4": "MD5 with RSA Encryption", + "1.2.840.113549.1.1.5": "SHA-1 with RSA Encryption", + "1.2.840.113549.1.1.11": "SHA-256 with RSA Encryption", + "1.2.840.113549.1.1.12": "SHA-384 with RSA Encryption", + "1.2.840.113549.1.1.13": "SHA-512 with RSA Encryption", + "1.2.840.10040.4.3": "DSA with SHA-1", + "2.16.840.1.101.3.4.3.2": "DSA with SHA-256", + "1.2.840.10045.4.1": "ECDSA with SHA-1", + "1.2.840.10045.4.3.2": "ECDSA with SHA-256", + "1.2.840.10045.4.3.3": "ECDSA with SHA-384", + "1.2.840.10045.4.3.4": "ECDSA with SHA-512", + }, + + aia: { + "1.3.6.1.5.5.7.48.1": "Online Certificate Status Protocol (OCSP)", + "1.3.6.1.5.5.7.48.2": "CA Issuers", + }, + + // this includes qualifiers as well + cps: { + "1.3.6.1.4.1": { + name: "Statement Identifier", + value: undefined, + }, + "1.3.6.1.5.5.7.2.1": { + name: "Practices Statement", + value: undefined, + }, + "1.3.6.1.5.5.7.2.2": { + name: "User Notice", + value: undefined, + }, + "2.16.840": { + name: "ANSI Organizational Identifier", + value: undefined, + }, + "2.23.140.1.1": { + name: "Certificate Type", + value: "Extended Validation", + }, + "2.23.140.1.2.1": { + name: "Certificate Type", + value: "Domain Validation", + }, + "2.23.140.1.2.2": { + name: "Certificate Type", + value: "Organization Validation", + }, + "2.23.140.1.2.3": { + name: "Certificate Type", + value: "Individual Validation", + }, + "2.23.140.1.3": { + name: "Certificate Type", + value: "Extended Validation (Code Signing)", + }, + "2.23.140.1.31": { + name: "Certificate Type", + value: ".onion Extended Validation", + }, + "2.23.140.2.1": { + name: "Certificate Type", + value: "Test Certificate", + }, + }, + + microsoftCertificateTypes: { + Administrator: "Administrator", + CA: "Root Certification Authority", + CAExchange: "CA Exchange", + CEPEncryption: "CEP Encryption", + CertificateRequestAgent: "Certificate Request Agent", + ClientAuth: "Authenticated Session", + CodeSigning: "Code Signing", + CrossCA: "Cross Certification Authority", + CTLSigning: "Trust List Signing", + DirectoryEmailReplication: "Directory Email Replication", + DomainController: "Domain Controller", + DomainControllerAuthentication: "Domain Controller Authentication", + EFS: "Basic EFS", + EFSRecovery: "EFS Recovery Agent", + EnrollmentAgent: "Enrollment Agent", + EnrollmentAgentOffline: "Exchange Enrollment Agent (Offline request)", + ExchangeUser: "Exchange User", + ExchangeUserSignature: "Exchange Signature Only", + IPSECIntermediateOffline: "IPSec (Offline request)", + IPSECIntermediateOnline: "IPSEC", + KerberosAuthentication: "Kerberos Authentication", + KeyRecoveryAgent: "Key Recovery Agent", + Machine: "Computer", + MachineEnrollmentAgent: "Enrollment Agent (Computer)", + OCSPResponseSigning: "OCSP Response Signing", + OfflineRouter: "Router (Offline request)", + RASAndIASServer: "RAS and IAS Server", + SmartcardLogon: "Smartcard Logon", + SmartcardUser: "Smartcard User", + SubCA: "Subordinate Certification Authority", + User: "User", + UserSignature: "User Signature Only", + WebServer: "Web Server", + Workstation: "Workstation Authentication", + }, +}; + +function stringToArrayBuffer(string) { + let result = new Uint8Array(string.length); + for (let i = 0; i < string.length; i++) { + result[i] = string.charCodeAt(i); + } + return result; +} + +// this particular prototype override makes it easy to chain down complex objects +const getObjPath = (obj, path) => { + path = path.split("."); + for (let i = 0, len = path.length; i < len; i++) { + if (Array.isArray(obj[path[i]])) { + obj = obj[path[i]][path[i + 1]]; + i++; + } else { + obj = obj[path[i]]; + } + } + return obj; +}; + +const arrayBufferToHex = arrayBuffer => { + const array = Array.from(new Uint8Array(arrayBuffer)); + + return array + .map(b => ("00" + b.toString(16)).slice(-2)) + .join(":") + .toUpperCase(); +}; + +const hash = async (algo, buffer) => { + const hashBuffer = await crypto.subtle.digest(algo, buffer); + return arrayBufferToHex(hashBuffer); +}; + +const hashify = rawHash => { + if (typeof rawHash === "string") { + return rawHash + .match(/.{2}/g) + .join(":") + .toUpperCase(); + } + if (rawHash instanceof ArrayBuffer) { + return arrayBufferToHex(rawHash); + } + return rawHash.join(":").toUpperCase(); +}; + +export const pemToDER = pem => { + return stringToArrayBuffer(atob(pem)); +}; diff --git a/toolkit/components/certviewer/content/certviewer.html b/toolkit/components/certviewer/content/certviewer.html index 5de0d205c4f8..abd415ad5b35 100644 --- a/toolkit/components/certviewer/content/certviewer.html +++ b/toolkit/components/certviewer/content/certviewer.html @@ -11,9 +11,9 @@ - - - + + +