forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1302 lines
		
	
	
	
		
			40 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1302 lines
		
	
	
	
		
			40 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* 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 };
 | 
						|
}
 | 
						|
 | 
						|
globalThis.certDecoderInitializer = certDecoderInitializer;
 | 
						|
/* eslint-disable-next-line no-unused-vars */
 | 
						|
var EXPORTED_SYMBOLS = ["certDecoderInitializer"];
 |