"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var Certificate_exports = {};
__export(Certificate_exports, {
  Certificate: () => Certificate
});
module.exports = __toCommonJS(Certificate_exports);
var import_general = require("@matter/general");
var import_types = require("@matter/types");
var import_common = require("./common.js");
var import_asn = require("./definitions/asn.js");
var import_base = require("./definitions/base.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
class Certificate {
  #signature;
  #cert;
  constructor(cert) {
    this.#cert = cert;
    if ("signature" in cert) {
      this.#signature = new import_general.EcdsaSignature(cert.signature);
    }
  }
  get cert() {
    return this.#cert;
  }
  get isSigned() {
    return this.#signature !== void 0;
  }
  /**
   * Get the signature of the certificate.
   * If the certificate is not signed, it throws a CertificateError.
   */
  get signature() {
    if (this.#signature === void 0) {
      throw new import_general.CertificateError("Certificate is not signed");
    }
    return this.#signature;
  }
  /**
   * Set the signature of the certificate.
   * If the certificate is already signed, it throws a CertificateError.
   */
  set signature(signature) {
    if (this.isSigned) {
      throw new import_general.CertificateError("Certificate is already signed");
    }
    this.#signature = signature;
  }
  /**
   * Sign the certificate using the provided crypto and key.
   * It throws a CertificateError if the certificate is already signed.
   */
  async sign(crypto, key) {
    this.signature = await crypto.signEcdsa(key, this.asUnsignedDer());
  }
  /**
   * Serialize as signed DER.
   *
   * If no signature is present, throws an error.
   */
  asSignedDer() {
    const certBytes = import_general.X509.certificateToDer({
      ...matterToX509(this.cert),
      signatureAlgorithm: import_general.X962.EcdsaWithSHA256,
      signature: this.signature.der
    });
    (0, import_common.assertCertificateDerSize)(certBytes);
    return certBytes;
  }
  /**
   * Serialize as DER without signature.
   */
  asUnsignedDer() {
    const certBytes = import_general.X509.certificateToDer(matterToX509(this.cert));
    (0, import_common.assertCertificateDerSize)(certBytes);
    return certBytes;
  }
  /**
   * Convert the extensions of the certificate to ASN.1 DER format.
   */
  extensionsToAsn1(extensions) {
    const asn = {};
    Object.entries(extensions).forEach(([key, value]) => {
      if (value === void 0) {
        return;
      }
      switch (key) {
        case "basicConstraints":
          asn.basicConstraints = import_general.X509.BasicConstraints(value);
          break;
        case "keyUsage":
          asn.keyUsage = import_general.X509.KeyUsage(
            import_base.ExtensionKeyUsageSchema.encode(
              value
            )
          );
          break;
        case "extendedKeyUsage":
          asn.extendedKeyUsage = import_general.X509.ExtendedKeyUsage(value);
          break;
        case "subjectKeyIdentifier":
          asn.subjectKeyIdentifier = import_general.X509.SubjectKeyIdentifier(value);
          break;
        case "authorityKeyIdentifier":
          asn.authorityKeyIdentifier = import_general.X509.AuthorityKeyIdentifier(value);
          break;
        case "futureExtension":
          asn.futureExtension = (0, import_general.RawBytes)(import_general.Bytes.concat(...value ?? []));
          break;
      }
    });
    return asn;
  }
}
((Certificate2) => {
  async function createCertificateSigningRequest(crypto, key) {
    const request = {
      version: 0,
      subject: { organization: import_general.X520.OrganisationName("CSR") },
      publicKey: import_general.X962.PublicKeyEcPrime256v1(key.publicKey),
      endSignedBytes: (0, import_general.ContextTagged)(0)
    };
    return import_general.DerCodec.encode({
      request,
      signAlgorithm: import_general.X962.EcdsaWithSHA256,
      signature: (0, import_general.DerBitString)((await crypto.signEcdsa(key, import_general.DerCodec.encode(request))).der)
    });
  }
  Certificate2.createCertificateSigningRequest = createCertificateSigningRequest;
  function oidToSubjectField(oid) {
    const oidHex = import_general.Bytes.toHex(oid);
    const field = import_general.X520.OID_TO_FIELD_MAP[oidHex] ?? import_asn.MATTER_OID_TO_FIELD_MAP[oidHex];
    if (field !== void 0) {
      return { field, isPrintable: false };
    }
  }
  function parseSubjectOrIssuer(node) {
    const result = {};
    const { _elements: rdnSequence } = node;
    if (!rdnSequence) {
      throw new import_general.CertificateError("Invalid subject/issuer structure");
    }
    for (const rdnSet of rdnSequence) {
      const { _elements: attributeSets } = rdnSet;
      if (!attributeSets) continue;
      for (const attributeSet of attributeSets) {
        const { _elements: attrElements } = attributeSet;
        if (!attrElements || attrElements.length !== 2) continue;
        const [oidNode, valueNode] = attrElements;
        const oid = oidNode._bytes;
        const fieldInfo = oidToSubjectField(oid);
        if (fieldInfo === void 0) continue;
        let { field } = fieldInfo;
        let value;
        const valueBytes = import_general.Bytes.of(valueNode._bytes);
        const valueTag = valueNode._tag;
        switch (field) {
          case "nodeId":
          case "fabricId": {
            const hexString = import_general.Bytes.toString(valueBytes);
            value = BigInt("0x" + hexString);
            break;
          }
          case "icacId":
          case "rcacId":
          case "vvsId": {
            const hexString = import_general.Bytes.toString(valueBytes);
            const bigIntValue = BigInt("0x" + hexString);
            value = bigIntValue <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(bigIntValue) : bigIntValue;
            break;
          }
          case "firmwareSigningId":
          case "productId":
          case "vendorId": {
            const hexString = import_general.Bytes.toString(valueBytes);
            value = parseInt(hexString, 16);
            break;
          }
          case "caseAuthenticatedTag": {
            const hexString = import_general.Bytes.toString(valueBytes);
            const catValue = parseInt(hexString, 16);
            if (result.caseAuthenticatedTags !== void 0) {
              result.caseAuthenticatedTags.push(catValue);
              continue;
            }
            field = "caseAuthenticatedTags";
            value = [catValue];
            break;
          }
          default: {
            value = import_general.Bytes.toString(valueBytes);
            if (valueTag === import_general.DerType.PrintableString) {
              field += "Ps";
            }
          }
        }
        result[field] = value;
      }
    }
    return result;
  }
  Certificate2.REQUIRED_PAA_EXTENSIONS = ["basicConstraints", "keyUsage", "subjectKeyIdentifier"];
  Certificate2.REQUIRED_EXTENSIONS = [...Certificate2.REQUIRED_PAA_EXTENSIONS, "authorityKeyIdentifier"];
  function parseExtensions(extensionsNode, requiredExtensions) {
    const result = {
      basicConstraints: { isCa: false }
    };
    const { _elements: extensions } = extensionsNode;
    if (!extensions) {
      throw new import_general.CertificateError("Invalid extensions structure");
    }
    for (const ext of extensions) {
      const { _elements: extElements } = ext;
      if (!extElements || extElements.length < 2) continue;
      const oid = extElements[0]._bytes;
      const oidValue = import_general.Bytes.asBigInt(oid);
      let valueIndex = 1;
      if (extElements.length > 2 && extElements[1]._tag === import_general.DerType.Boolean) {
        valueIndex = 2;
      }
      const valueOctetString = extElements[valueIndex]._bytes;
      const valueNode = import_general.DerCodec.decode(valueOctetString);
      switch (oidValue) {
        case import_general.X509.Extensions.BASIC_CONSTRAINTS:
          {
            const { _elements: bcElements } = valueNode;
            if (bcElements && bcElements.length > 0) {
              if (bcElements[0]._tag === import_general.DerType.Boolean) {
                const bcBytes = import_general.Bytes.of(bcElements[0]._bytes);
                result.basicConstraints.isCa = bcBytes[0] !== 0;
              }
              if (bcElements.length > 1 && bcElements[1]._tag === import_general.DerType.Integer) {
                const pathLenBytes = import_general.Bytes.of(bcElements[1]._bytes);
                result.basicConstraints.pathLen = pathLenBytes[0];
              }
            }
          }
          break;
        case import_general.X509.Extensions.KEY_USAGE:
          {
            const bitString = import_general.Bytes.of(valueNode._bytes);
            if (bitString.length >= 1) {
              const usageByte = bitString[0];
              result.keyUsage = {
                digitalSignature: (usageByte & 128) !== 0,
                nonRepudiation: (usageByte & 64) !== 0,
                keyEncipherment: (usageByte & 32) !== 0,
                dataEncipherment: (usageByte & 16) !== 0,
                keyAgreement: (usageByte & 8) !== 0,
                keyCertSign: (usageByte & 4) !== 0,
                cRLSign: (usageByte & 2) !== 0,
                encipherOnly: (usageByte & 1) !== 0,
                decipherOnly: bitString.length > 1 ? (bitString[1] & 128) !== 0 : false
              };
            }
          }
          break;
        case import_general.X509.Extensions.EXTENDED_KEY_USAGE:
          {
            const { _elements: ekuElements } = valueNode;
            if (ekuElements) {
              const ekuValues = [];
              for (const eku of ekuElements) {
                const ekuOidValue = import_general.Bytes.asBigInt(eku._bytes);
                switch (ekuOidValue) {
                  case import_general.X509.ExtendedKeyUsage.SERVER_AUTH:
                    ekuValues.push(1);
                    break;
                  case import_general.X509.ExtendedKeyUsage.CLIENT_AUTH:
                    ekuValues.push(2);
                    break;
                  case import_general.X509.ExtendedKeyUsage.CODE_SIGNING:
                    ekuValues.push(3);
                    break;
                  case import_general.X509.ExtendedKeyUsage.EMAIL_PROTECTION:
                    ekuValues.push(4);
                    break;
                  case import_general.X509.ExtendedKeyUsage.TIME_STAMPING:
                    ekuValues.push(5);
                    break;
                  case import_general.X509.ExtendedKeyUsage.OCSP_SIGNING:
                    ekuValues.push(6);
                    break;
                }
              }
              if (ekuValues.length > 0) {
                result.extendedKeyUsage = ekuValues;
              }
            }
          }
          break;
        case import_general.X509.Extensions.SUBJECT_KEY_IDENTIFIER:
          result.subjectKeyIdentifier = valueNode._bytes;
          break;
        case import_general.X509.Extensions.AUTHORITY_KEY_IDENTIFIER:
          {
            const { _elements: akiElements } = valueNode;
            if (akiElements && akiElements.length > 0) {
              result.authorityKeyIdentifier = akiElements[0]._bytes;
            }
          }
          break;
      }
    }
    if (requiredExtensions.some((ext) => result[ext] === void 0)) {
      throw new import_general.CertificateError("Missing required extensions in certificate");
    }
    return result;
  }
  function parseDate(node) {
    const dateBytes = node._bytes;
    const dateString = import_general.Bytes.toString(dateBytes);
    const tag = node._tag;
    let year, month, day, hour, minute, second;
    if (tag === import_general.DerType.UtcDate) {
      year = parseInt(dateString.substring(0, 2));
      year += year >= 50 ? 1900 : 2e3;
      month = parseInt(dateString.substring(2, 4));
      day = parseInt(dateString.substring(4, 6));
      hour = parseInt(dateString.substring(6, 8));
      minute = parseInt(dateString.substring(8, 10));
      second = parseInt(dateString.substring(10, 12));
    } else if (tag === import_general.DerType.GeneralizedTime) {
      year = parseInt(dateString.substring(0, 4));
      month = parseInt(dateString.substring(4, 6));
      day = parseInt(dateString.substring(6, 8));
      hour = parseInt(dateString.substring(8, 10));
      minute = parseInt(dateString.substring(10, 12));
      second = parseInt(dateString.substring(12, 14));
    } else {
      throw new import_general.CertificateError(`Unsupported date type: ${tag}`);
    }
    const date = new Date(Date.UTC(year, month - 1, day, hour, minute, second));
    if (date.getTime() === import_general.X520.NON_WELL_DEFINED_DATE.getTime()) {
      return 0;
    }
    return Math.floor(date.getTime() / 1e3) - import_types.MATTER_EPOCH_OFFSET_S;
  }
  function parseAsn1Certificate(encodedCert, requiredExtensions = Certificate2.REQUIRED_EXTENSIONS) {
    const { _elements: rootElements } = import_general.DerCodec.decode(encodedCert);
    if (!rootElements || rootElements.length !== 3) {
      throw new import_general.CertificateError(
        `Invalid certificate structure - expected 3 root elements, got ${rootElements?.length ?? 0}`
      );
    }
    const [certificateNode, , signatureNode] = rootElements;
    const { _elements: certElements } = certificateNode;
    if (!certElements || certElements.length < 7) {
      throw new import_general.CertificateError("Invalid TBSCertificate structure");
    }
    let idx = 0;
    if (certElements[idx]._tag === 160) {
      idx++;
    }
    const serialNumber = certElements[idx++]._bytes;
    const signatureAlgorithmOid = certElements[idx]._elements?.[0]?._bytes;
    if (!signatureAlgorithmOid) {
      throw new import_general.CertificateError("Invalid signature algorithm structure");
    }
    const signatureAlgorithm = import_general.Bytes.toHex(signatureAlgorithmOid) === "2a8648ce3d040302" ? 1 : 0;
    idx++;
    const issuer = parseSubjectOrIssuer(certElements[idx++]);
    const { _elements: validityElements } = certElements[idx++];
    if (!validityElements || validityElements.length !== 2) {
      throw new import_general.CertificateError("Invalid validity structure");
    }
    const notBefore = parseDate(validityElements[0]);
    const notAfter = parseDate(validityElements[1]);
    const subject = parseSubjectOrIssuer(certElements[idx++]);
    const { _elements: publicKeyElements } = certElements[idx++];
    if (!publicKeyElements || publicKeyElements.length !== 2) {
      throw new import_general.CertificateError("Invalid public key structure");
    }
    const { _elements: algorithmElements } = publicKeyElements[0];
    if (!algorithmElements || algorithmElements.length !== 2) {
      throw new import_general.CertificateError("Invalid public key algorithm structure");
    }
    const publicKeyAlgorithmOid = import_general.Bytes.toHex(algorithmElements[0]._bytes);
    const publicKeyAlgorithm = publicKeyAlgorithmOid === "2a8648ce3d0201" ? 1 : 0;
    const ellipticCurveOid = import_general.Bytes.toHex(algorithmElements[1]._bytes);
    const ellipticCurveIdentifier = ellipticCurveOid === "2a8648ce3d030107" ? 1 : 0;
    const ellipticCurvePublicKey = import_general.Bytes.of(publicKeyElements[1]._bytes);
    if (idx >= certElements.length || certElements[idx]._tag !== 163) {
      throw new import_general.CertificateError("Missing required extensions in certificate");
    }
    const extensionsBytes = certElements[idx]._bytes;
    const extensionsSequence = import_general.DerCodec.decode(extensionsBytes);
    const extensions = parseExtensions(extensionsSequence, requiredExtensions);
    const signature = new import_general.EcdsaSignature(import_general.Bytes.of(signatureNode._bytes), "der").bytes;
    return {
      serialNumber,
      signatureAlgorithm,
      issuer,
      notBefore,
      notAfter,
      subject,
      publicKeyAlgorithm,
      ellipticCurveIdentifier,
      ellipticCurvePublicKey,
      extensions,
      signature
    };
  }
  Certificate2.parseAsn1Certificate = parseAsn1Certificate;
  async function getPublicKeyFromCsr(crypto, encodedCsr) {
    const { _elements: rootElements } = import_general.DerCodec.decode(encodedCsr);
    if (rootElements?.length !== 3) {
      throw new import_general.CertificateError("Invalid CSR data");
    }
    const [requestNode, signAlgorithmNode, signatureNode] = rootElements;
    const { _elements: requestElements } = requestNode;
    if (requestElements?.length !== 4) {
      throw new import_general.CertificateError("Invalid CSR data");
    }
    const [versionNode, subjectNode, publicKeyNode] = requestElements;
    const requestVersionBytes = import_general.Bytes.of(versionNode._bytes);
    if (requestVersionBytes.length !== 1 || requestVersionBytes[0] !== 0) {
      throw new import_general.CertificateError(`Unsupported CSR version ${requestVersionBytes[0]}`);
    }
    if (!subjectNode._elements?.length) {
      throw new import_general.CertificateError("Missing subject in CSR data");
    }
    const { _elements: publicKeyElements } = publicKeyNode;
    if (publicKeyElements?.length !== 2) {
      throw new import_general.CertificateError("Invalid CSR data");
    }
    const [publicKeyTypeNode, publicKeyBytesNode] = publicKeyElements;
    const { _elements: publicKeyTypeNodeElements } = publicKeyTypeNode;
    if (publicKeyTypeNodeElements?.length !== 2) {
      throw new import_general.CertificateError("Invalid public key type in CSR");
    }
    if (!import_general.Bytes.areEqual(publicKeyTypeNodeElements[0]._bytes, import_general.X962.PublicKeyAlgorithmEcPublicKey._bytes)) {
      throw new import_general.CertificateError("Unsupported public key algorithm in CSR");
    }
    if (!import_general.Bytes.areEqual(publicKeyTypeNodeElements[1]._bytes, import_general.X962.PublicKeyAlgorithmEcPublicKeyP256._bytes)) {
      throw new import_general.CertificateError("Unsupported public key curve in CSR");
    }
    const publicKey = publicKeyBytesNode._bytes;
    const signatureAlgorithmBytes = signAlgorithmNode._elements?.[0]?._bytes;
    if (signatureAlgorithmBytes === void 0 || !import_general.Bytes.areEqual(import_general.X962.EcdsaWithSHA256._objectId._bytes, signatureAlgorithmBytes)) {
      throw new import_general.CertificateError("Unsupported signature algorithm in CSR");
    }
    await crypto.verifyEcdsa(
      (0, import_general.PublicKey)(publicKey),
      import_general.DerCodec.encode(requestNode),
      new import_general.EcdsaSignature(signatureNode._bytes, "der")
    );
    return publicKey;
  }
  Certificate2.getPublicKeyFromCsr = getPublicKeyFromCsr;
})(Certificate || (Certificate = {}));
function matterToX509(cert) {
  const { serialNumber, notBefore, notAfter, issuer, subject, ellipticCurvePublicKey, extensions } = cert;
  return {
    serialNumber,
    validity: {
      notBefore: (0, import_asn.matterToJsDate)(notBefore),
      notAfter: (0, import_asn.matterToJsDate)(notAfter)
    },
    issuer: astOfDistinguishedName(issuer),
    subject: astOfDistinguishedName(subject),
    extensions,
    signatureAlgorithm: import_general.X962.EcdsaWithSHA256,
    publicKey: import_general.X962.PublicKeyEcPrime256v1(ellipticCurvePublicKey)
  };
}
function astOfDistinguishedName(data) {
  const ast = {};
  Object.entries(data).forEach(([key, value]) => {
    if (value === void 0) {
      return;
    }
    switch (key) {
      case "commonName":
        ast.commonName = import_general.X520.CommonName(value);
        break;
      case "surName":
        ast.surName = import_general.X520.SurName(value);
        break;
      case "serialNum":
        ast.serialNum = import_general.X520.SerialNumber(value);
        break;
      case "countryName":
        ast.countryName = import_general.X520.CountryName(value);
        break;
      case "localityName":
        ast.localityName = import_general.X520.LocalityName(value);
        break;
      case "stateOrProvinceName":
        ast.stateOrProvinceName = import_general.X520.StateOrProvinceName(value);
        break;
      case "orgName":
        ast.orgName = import_general.X520.OrganisationName(value);
        break;
      case "orgUnitName":
        ast.orgUnitName = import_general.X520.OrganizationalUnitName(value);
        break;
      case "title":
        ast.title = import_general.X520.Title(value);
        break;
      case "name":
        ast.name = import_general.X520.Name(value);
        break;
      case "givenName":
        ast.givenName = import_general.X520.GivenName(value);
        break;
      case "initials":
        ast.initials = import_general.X520.Initials(value);
        break;
      case "genQualifier":
        ast.genQualifier = import_general.X520.GenerationQualifier(value);
        break;
      case "dnQualifier":
        ast.dnQualifier = import_general.X520.DnQualifier(value);
        break;
      case "pseudonym":
        ast.pseudonym = import_general.X520.Pseudonym(value);
        break;
      case "domainComponent":
        ast.domainComponent = import_general.X520.DomainComponent(value);
        break;
      case "nodeId":
        ast.nodeId = (0, import_asn.NodeId_Matter)(value);
        break;
      case "firmwareSigningId":
        ast.firmwareSigningId = (0, import_asn.FirmwareSigningId_Matter)(value);
        break;
      case "icacId":
        ast.icacId = (0, import_asn.IcacId_Matter)(value);
        break;
      case "rcacId":
        ast.rcacId = (0, import_asn.RcacId_Matter)(value);
        break;
      case "vvsId":
        ast.vvsId = (0, import_asn.VvsId_Matter)(value);
        break;
      case "fabricId":
        ast.fabricId = (0, import_asn.FabricId_Matter)(value);
        break;
      case "caseAuthenticatedTags":
        const caseAuthenticatedTags = value;
        import_types.CaseAuthenticatedTag.validateNocTagList(caseAuthenticatedTags);
        const cat0 = caseAuthenticatedTags[0];
        const cat1 = caseAuthenticatedTags[1];
        const cat2 = caseAuthenticatedTags[2];
        if (cat0 !== void 0) {
          ast.caseAuthenticatedTag0 = (0, import_asn.NocCat_Matter)(cat0);
        }
        if (cat1 !== void 0) {
          ast.caseAuthenticatedTag1 = (0, import_asn.NocCat_Matter)(cat1);
        }
        if (cat2 !== void 0) {
          ast.caseAuthenticatedTag2 = (0, import_asn.NocCat_Matter)(cat2);
        }
        break;
      case "vendorId":
        ast.vendorId = (0, import_asn.VendorId_Matter)(value);
        break;
      case "productId":
        ast.productId = (0, import_asn.ProductId_Matter)(value);
        break;
      case "commonNamePs":
        ast.commonNamePs = import_general.X520.CommonName(value, true);
        break;
      case "surNamePs":
        ast.surNamePs = import_general.X520.SurName(value, true);
        break;
      case "serialNumPs":
        ast.serialNumPs = import_general.X520.SerialNumber(value, true);
        break;
      case "countryNamePs":
        ast.countryNamePs = import_general.X520.CountryName(value, true);
        break;
      case "localityNamePs":
        ast.localityNamePs = import_general.X520.LocalityName(value, true);
        break;
      case "stateOrProvinceNamePs":
        ast.stateOrProvinceNamePs = import_general.X520.StateOrProvinceName(value, true);
        break;
      case "orgNamePs":
        ast.orgNamePs = import_general.X520.OrganisationName(value, true);
        break;
      case "orgUnitNamePs":
        ast.orgUnitNamePs = import_general.X520.OrganizationalUnitName(value, true);
        break;
      case "titlePs":
        ast.titlePs = import_general.X520.Title(value, true);
        break;
      case "namePs":
        ast.namePs = import_general.X520.Name(value, true);
        break;
      case "givenNamePs":
        ast.givenNamePs = import_general.X520.GivenName(value, true);
        break;
      case "initialsPs":
        ast.initialsPs = import_general.X520.Initials(value, true);
        break;
      case "genQualifierPs":
        ast.genQualifierPs = import_general.X520.GenerationQualifier(value, true);
        break;
      case "dnQualifierPs":
        ast.dnQualifierPs = import_general.X520.DnQualifier(value, true);
        break;
      case "pseudonymPs":
        ast.pseudonymPs = import_general.X520.Pseudonym(value, true);
        break;
    }
  });
  return ast;
}
//# sourceMappingURL=Certificate.js.map
