"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 CaseClient_exports = {};
__export(CaseClient_exports, {
  CaseClient: () => CaseClient
});
module.exports = __toCommonJS(CaseClient_exports);
var import_Icac = require("#certificate/kinds/Icac.js");
var import_Noc = require("#certificate/kinds/Noc.js");
var import_general = require("#general");
var import_errors = require("#protocol/errors.js");
var import_SecureChannelMessenger = require("#securechannel/SecureChannelMessenger.js");
var import_NodeSession = require("#session/NodeSession.js");
var import_types = require("#types");
var import_CaseMessages = require("./CaseMessages.js");
var import_CaseMessenger = require("./CaseMessenger.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("CaseClient");
class CaseClient {
  #sessions;
  constructor(sessions) {
    this.#sessions = sessions;
  }
  async pair(exchange, fabric, peerNodeId, options) {
    const { expectedProcessingTime, caseAuthenticatedTags } = options ?? {};
    const messenger = new import_CaseMessenger.CaseClientMessenger(exchange, expectedProcessingTime);
    try {
      return await this.#doPair(messenger, exchange, fabric, peerNodeId, caseAuthenticatedTags);
    } catch (error) {
      if (!(error instanceof import_SecureChannelMessenger.ChannelStatusResponseError || error instanceof import_errors.RetransmissionLimitReachedError || error instanceof import_general.AbortedError || error instanceof import_general.NetworkError)) {
        await messenger.sendError(import_types.SecureChannelStatusCode.InvalidParam);
      }
      throw error;
    }
  }
  async #doPair(messenger, exchange, fabric, peerNodeId, caseAuthenticatedTags) {
    const { crypto } = fabric;
    const initiatorRandom = crypto.randomBytes(32);
    const initiatorSessionId = await this.#sessions.getNextAvailableSessionId();
    const { operationalIdentityProtectionKey, operationalCert: localNoc, intermediateCACert: localIcac } = fabric;
    const localKey = await crypto.createKeyPair();
    let sigma1Bytes;
    let resumed = false;
    let resumptionRecord = this.#sessions.findResumptionRecordByAddress(fabric.addressOf(peerNodeId));
    if (resumptionRecord !== void 0) {
      const { sharedSecret, resumptionId } = resumptionRecord;
      const resumeKey = await crypto.createHkdfKey(
        sharedSecret,
        import_general.Bytes.concat(initiatorRandom, resumptionId),
        import_CaseMessages.KDFSR1_KEY_INFO
      );
      const initiatorResumeMic = crypto.encrypt(resumeKey, new Uint8Array(0), import_CaseMessages.RESUME1_MIC_NONCE);
      sigma1Bytes = await messenger.sendSigma1({
        initiatorSessionId,
        destinationId: await fabric.currentDestinationIdFor(peerNodeId, initiatorRandom),
        initiatorEcdhPublicKey: localKey.publicBits,
        initiatorRandom,
        resumptionId,
        initiatorResumeMic,
        initiatorSessionParams: this.#sessions.sessionParameters
      });
    } else {
      sigma1Bytes = await messenger.sendSigma1({
        initiatorSessionId,
        destinationId: await fabric.currentDestinationIdFor(peerNodeId, initiatorRandom),
        initiatorEcdhPublicKey: localKey.publicBits,
        initiatorRandom,
        initiatorSessionParams: this.#sessions.sessionParameters
      });
    }
    let secureSession;
    const { sigma2Bytes, sigma2, sigma2Resume } = await messenger.readSigma2();
    if (sigma2Resume !== void 0) {
      if (resumptionRecord === void 0) throw new import_general.UnexpectedDataError("Received an unexpected sigma2Resume.");
      const {
        sharedSecret,
        fabric: fabric2,
        sessionParameters: resumptionSessionParams,
        caseAuthenticatedTags: caseAuthenticatedTags2
      } = resumptionRecord;
      const { responderSessionId: peerSessionId, resumptionId, resumeMic } = sigma2Resume;
      const sessionParameters = {
        ...exchange.session.parameters,
        ...resumptionSessionParams ?? {}
      };
      const resumeSalt = import_general.Bytes.concat(initiatorRandom, resumptionId);
      const resumeKey = await crypto.createHkdfKey(sharedSecret, resumeSalt, import_CaseMessages.KDFSR2_KEY_INFO);
      crypto.decrypt(resumeKey, resumeMic, import_CaseMessages.RESUME2_MIC_NONCE);
      const secureSessionSalt = import_general.Bytes.concat(initiatorRandom, resumptionRecord.resumptionId);
      secureSession = await this.#sessions.createSecureSession({
        channel: exchange.channel.channel,
        id: initiatorSessionId,
        fabric: fabric2,
        peerNodeId,
        peerSessionId,
        sharedSecret,
        salt: secureSessionSalt,
        isInitiator: true,
        isResumption: true,
        peerSessionParameters: sessionParameters,
        caseAuthenticatedTags: caseAuthenticatedTags2
      });
      import_NodeSession.NodeSession.logNew(logger, "Resumed", secureSession, messenger, fabric2, peerNodeId);
      resumptionRecord.resumptionId = resumptionId;
      resumptionRecord.sessionParameters = secureSession.parameters;
      resumed = true;
      try {
        await messenger.sendSuccess();
      } catch (error) {
        import_general.MatterError.accept(error);
        logger.info(
          messenger.exchange.via,
          "Error sending Sigma2Resume-Success, assume session still valid:",
          error
        );
      }
    } else {
      const {
        responderEcdhPublicKey: peerKey,
        encrypted: peerEncrypted,
        responderRandom,
        responderSessionId: peerSessionId,
        responderSessionParams
      } = sigma2;
      if (responderSessionParams !== void 0) {
        exchange.session.timingParameters = responderSessionParams;
      }
      const peerSessionParameters = {
        ...exchange.session.parameters,
        ...responderSessionParams ?? {}
      };
      const sharedSecret = await crypto.generateDhSecret(localKey, (0, import_general.PublicKey)(peerKey));
      const sigma2Salt = import_general.Bytes.concat(
        operationalIdentityProtectionKey,
        responderRandom,
        peerKey,
        await crypto.computeHash(sigma1Bytes)
      );
      const sigma2Key = await crypto.createHkdfKey(sharedSecret, sigma2Salt, import_CaseMessages.KDFSR2_INFO);
      const peerEncryptedData = crypto.decrypt(sigma2Key, peerEncrypted, import_CaseMessages.TBE_DATA2_NONCE);
      const {
        responderNoc: peerNoc,
        responderIcac: peerIcac,
        signature: peerSignature,
        resumptionId: peerResumptionId
      } = import_CaseMessages.TlvEncryptedDataSigma2.decode(peerEncryptedData);
      const peerSignatureData = import_CaseMessages.TlvSignedData.encode({
        responderNoc: peerNoc,
        responderIcac: peerIcac,
        responderPublicKey: peerKey,
        initiatorPublicKey: localKey.publicBits
      });
      const {
        ellipticCurvePublicKey: peerPublicKey,
        subject: { fabricId: peerFabricIdNOCert, nodeId: peerNodeIdNOCert }
      } = import_Noc.Noc.fromTlv(peerNoc).cert;
      await crypto.verifyEcdsa((0, import_general.PublicKey)(peerPublicKey), peerSignatureData, new import_general.EcdsaSignature(peerSignature));
      if (peerNodeIdNOCert !== peerNodeId) {
        throw new import_general.UnexpectedDataError(
          `The node ID in the peer certificate ${peerNodeIdNOCert} doesn't match the expected peer node ID ${peerNodeId}`
        );
      }
      if (peerFabricIdNOCert !== fabric.fabricId) {
        throw new import_general.UnexpectedDataError(
          `The fabric ID in the peer certificate ${peerFabricIdNOCert} doesn't match the expected fabric ID ${fabric.fabricId}`
        );
      }
      if (peerIcac !== void 0) {
        const {
          subject: { fabricId: peerFabricIdIcaCert }
        } = import_Icac.Icac.fromTlv(peerIcac).cert;
        if (peerFabricIdIcaCert !== void 0 && peerFabricIdIcaCert !== fabric.fabricId) {
          throw new import_general.UnexpectedDataError(
            `The fabric ID in the peer intermediate CA certificate ${peerFabricIdIcaCert} doesn't match the expected fabric ID ${fabric.fabricId}`
          );
        }
      }
      await fabric.verifyCredentials(peerNoc, peerIcac);
      const sigma3Salt = import_general.Bytes.concat(
        operationalIdentityProtectionKey,
        await crypto.computeHash([sigma1Bytes, sigma2Bytes])
      );
      const sigma3Key = await crypto.createHkdfKey(sharedSecret, sigma3Salt, import_CaseMessages.KDFSR3_INFO);
      const signatureData = import_CaseMessages.TlvSignedData.encode({
        responderNoc: localNoc,
        responderIcac: localIcac,
        responderPublicKey: localKey.publicBits,
        initiatorPublicKey: peerKey
      });
      const signature = await fabric.sign(signatureData);
      const encryptedData = import_CaseMessages.TlvEncryptedDataSigma3.encode({
        responderNoc: localNoc,
        responderIcac: localIcac,
        signature: signature.bytes
      });
      const encrypted = crypto.encrypt(sigma3Key, encryptedData, import_CaseMessages.TBE_DATA3_NONCE);
      const sigma3Bytes = await messenger.sendSigma3({ encrypted });
      await messenger.waitForSuccess("Sigma3-Success");
      const sessionCaseAuthenticatedTags = caseAuthenticatedTags ?? resumptionRecord?.caseAuthenticatedTags;
      const secureSessionSalt = import_general.Bytes.concat(
        operationalIdentityProtectionKey,
        await crypto.computeHash([sigma1Bytes, sigma2Bytes, sigma3Bytes])
      );
      secureSession = await this.#sessions.createSecureSession({
        channel: exchange.channel.channel,
        id: initiatorSessionId,
        fabric,
        peerNodeId,
        peerSessionId,
        sharedSecret,
        salt: secureSessionSalt,
        isInitiator: true,
        isResumption: false,
        peerSessionParameters,
        caseAuthenticatedTags: sessionCaseAuthenticatedTags
      });
      import_NodeSession.NodeSession.logNew(logger, "New", secureSession, messenger, fabric, peerNodeId);
      resumptionRecord = {
        fabric,
        peerNodeId,
        sharedSecret,
        resumptionId: peerResumptionId,
        sessionParameters: secureSession.parameters,
        caseAuthenticatedTags: sessionCaseAuthenticatedTags
      };
    }
    await messenger.close();
    await this.#sessions.saveResumptionRecord(resumptionRecord);
    return { session: secureSession, resumed };
  }
}
//# sourceMappingURL=CaseClient.js.map
