/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import {
  Bytes,
  Crypto,
  CryptoError,
  DataReader,
  DataWriter,
  EcdsaSignature,
  Endian,
  InternalError,
  Logger,
  MATTER_CRYPTO_PRIMITIVES_VERSION,
  PublicKey,
  UnexpectedDataError
} from "@matter/general";
import { Icac, Noc, PeerSet, Rcac, Vvsc } from "@matter/protocol";
import { ReceivedStatusResponseError, StatusResponse } from "@matter/types";
import { OperationalCredentialsClient } from "../operational-credentials/OperationalCredentialsClient.js";
const logger = Logger.get("VendorIdVerification");
const VERIFICATION_STATEMENT_VERSION = 33;
const FABRIC_BINDING_MESSAGE_SIZE = 76;
const VERIFICATION_STATEMENT_SIZE = 85;
const FABRIC_BINDING_VERSION = MATTER_CRYPTO_PRIMITIVES_VERSION;
var VendorIdVerification;
((VendorIdVerification2) => {
  function dataToSign(data) {
    const {
      fabricBindingVersion = FABRIC_BINDING_VERSION,
      clientChallenge,
      attChallenge,
      fabricIndex,
      fabric
    } = data;
    if (fabricBindingVersion !== FABRIC_BINDING_VERSION) {
      throw new InternalError(`Unsupported Fabric Binding Version ${fabricBindingVersion}`);
    }
    const tbsWriter = new DataWriter(Endian.Big);
    tbsWriter.writeUInt8(fabricBindingVersion);
    tbsWriter.writeByteArray(clientChallenge);
    tbsWriter.writeByteArray(attChallenge);
    tbsWriter.writeUInt8(fabricIndex);
    tbsWriter.writeUInt8(fabricBindingVersion);
    tbsWriter.writeByteArray(fabric.rootPublicKey);
    tbsWriter.writeUInt64(fabric.fabricId);
    tbsWriter.writeUInt16(fabric.rootVendorId);
    if (fabric.vidVerificationStatement) {
      tbsWriter.writeByteArray(fabric.vidVerificationStatement);
    }
    return {
      fabricBindingVersion,
      signatureData: tbsWriter.toByteArray()
    };
  }
  VendorIdVerification2.dataToSign = dataToSign;
  async function verify(node, options) {
    const crypto = node.env.get(Crypto);
    const clientChallenge = crypto.randomBytes(32);
    const {
      fabric: { fabricIndex }
    } = options;
    let signVerificationResponse;
    try {
      signVerificationResponse = await node.commandsOf(OperationalCredentialsClient).signVidVerificationRequest({ fabricIndex, clientChallenge });
      if (fabricIndex !== signVerificationResponse.fabricIndex) {
        throw new StatusResponse.InvalidCommandError(
          `Fabric Index mismatch: expected ${fabricIndex}, got ${signVerificationResponse.fabricIndex}`
        );
      }
    } catch (error) {
      ReceivedStatusResponseError.accept(error);
      logger.warn("Could not verify VendorId: peer rejected the signing request", error);
      return void 0;
    }
    const peerAddress = node.peerAddress;
    if (!peerAddress) {
      logger.warn("Could not verify VendorId: Node is not a commissioned peer");
      return void 0;
    }
    const sessions = node.env.maybeGet(PeerSet)?.get(peerAddress)?.sessions;
    if (!sessions?.size) {
      logger.warn("Could not verify VendorId: Node has no session established");
      return void 0;
    }
    const { noc, rcac, fabric } = options;
    for (const session of sessions) {
      if (await verifyData(crypto, {
        clientChallenge,
        attChallenge: session.attestationChallengeKey,
        signVerificationResponse,
        noc,
        rcac,
        fabric
      })) {
        return true;
      }
    }
    return false;
  }
  VendorIdVerification2.verify = verify;
  async function verifyData(crypto, options) {
    const {
      clientChallenge,
      attChallenge,
      signVerificationResponse: { fabricBindingVersion, signature },
      noc: { noc, icac, vvsc },
      rcac,
      fabric: { vendorId: rootVendorId, vidVerificationStatement, fabricIndex }
    } = options;
    const {
      subject: { fabricId },
      ellipticCurvePublicKey: rootPublicKey
    } = Noc.fromTlv(noc).cert;
    const tbs = dataToSign({
      fabricBindingVersion,
      clientChallenge,
      attChallenge,
      fabricIndex,
      fabric: {
        rootPublicKey,
        fabricId,
        rootVendorId,
        vidVerificationStatement
      }
    }).signatureData;
    try {
      await crypto.verifyEcdsa(PublicKey(rootPublicKey), tbs, new EcdsaSignature(signature));
      const rootCert = Rcac.fromTlv(rcac);
      const nocCert = Noc.fromTlv(noc);
      const icaCert = icac ? Icac.fromTlv(icac) : void 0;
      if (icaCert !== void 0) {
        await icaCert.verify(crypto, rootCert);
      }
      await nocCert.verify(crypto, rootCert, icaCert);
    } catch (error) {
      CryptoError.accept(error);
      logger.warn("Could not verify VendorId: certificate chain verification failed", error);
      return false;
    }
    if (vidVerificationStatement) {
      const {
        signerSkid,
        version: vidStatementVersion,
        signature: vidStatementSignature
      } = parseStatement(vidVerificationStatement);
      if (vidStatementSignature === void 0) {
        throw new UnexpectedDataError("VID Verification Statement is missing signature");
      }
      let vvscCert;
      if (vvsc !== void 0) {
        vvscCert = Vvsc.fromTlv(vvsc);
      } else {
        return true;
      }
      const {
        extensions: { subjectKeyIdentifier },
        ellipticCurvePublicKey
      } = vvscCert.cert;
      if (!Bytes.areEqual(subjectKeyIdentifier, signerSkid)) {
        throw new UnexpectedDataError(
          `VVSC SubjectKeyIdentifier does not match signerSkid in VID Verification Statement`
        );
      }
      await vvscCert.verify(crypto);
      const ourStatement = createStatementBytes({
        version: vidStatementVersion,
        fabricBindingMessage: tbs,
        signerSkid
      });
      await crypto.verifyEcdsa(
        PublicKey(ellipticCurvePublicKey),
        ourStatement,
        new EcdsaSignature(vidStatementSignature)
      );
    } else {
    }
    return true;
  }
  VendorIdVerification2.verifyData = verifyData;
  function createStatementBytes(options) {
    const { version = VERIFICATION_STATEMENT_VERSION, fabricBindingMessage, signerSkid } = options;
    const writer = new DataWriter();
    writer.writeUInt8(version);
    writer.writeByteArray(fabricBindingMessage);
    writer.writeByteArray(signerSkid);
    return writer.toByteArray();
  }
  VendorIdVerification2.createStatementBytes = createStatementBytes;
  function parseStatement(statement) {
    const reader = new DataReader(statement);
    const version = reader.readUInt8();
    if (version !== VERIFICATION_STATEMENT_VERSION) {
      throw new UnexpectedDataError(`Unsupported VID Verification Statement version ${version}`);
    }
    const fabricBindingMessage = reader.readByteArray(FABRIC_BINDING_MESSAGE_SIZE);
    const signerSkid = reader.readByteArray(20);
    const signature = reader.remainingBytesCount > 0 ? reader.remainingBytes : void 0;
    return {
      version,
      fabricBindingMessage,
      signerSkid,
      signature
    };
  }
  VendorIdVerification2.parseStatement = parseStatement;
})(VendorIdVerification || (VendorIdVerification = {}));
export {
  VERIFICATION_STATEMENT_SIZE,
  VendorIdVerification
};
//# sourceMappingURL=VendorIdVerification.js.map
