/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { Mark } from "#common/Mark.js";
import { Bytes, ec, Logger, Spake2p, UnexpectedDataError } from "#general";
import { CommissioningOptions, NodeId, SecureChannelStatusCode } from "#types";
import { DEFAULT_PASSCODE_ID, PaseClientMessenger, SPAKE_CONTEXT } from "./PaseMessenger.js";
const { numberToBytesBE } = ec;
const logger = Logger.get("PaseClient");
class PaseClient {
  #sessions;
  constructor(sessions) {
    this.#sessions = sessions;
  }
  static async generatePakePasscodeVerifier(crypto, setupPinCode, pbkdfParameters) {
    const { w0, L } = await Spake2p.computeW0L(crypto, pbkdfParameters, setupPinCode);
    return Bytes.concat(numberToBytesBE(w0, 32), L);
  }
  static generateRandomPasscode(crypto) {
    let passcode;
    passcode = crypto.randomUint32 % 99999998 + 1;
    if (CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode)) {
      passcode += 1;
    }
    return passcode;
  }
  static generateRandomDiscriminator(crypto) {
    return crypto.randomUint16 % 4096;
  }
  async pair(initiatorSessionParams, exchange, channel, setupPin) {
    const messenger = new PaseClientMessenger(exchange);
    const { crypto } = this.#sessions;
    const initiatorRandom = crypto.randomBytes(32);
    const initiatorSessionId = await this.#sessions.getNextAvailableSessionId();
    const requestPayload = await messenger.sendPbkdfParamRequest({
      initiatorRandom,
      initiatorSessionId,
      passcodeId: DEFAULT_PASSCODE_ID,
      hasPbkdfParameters: false,
      initiatorSessionParams
    });
    const {
      responsePayload,
      response: { pbkdfParameters, responderSessionId, responderSessionParams }
    } = await messenger.readPbkdfParamResponse();
    if (pbkdfParameters === void 0) {
      await messenger.sendError(SecureChannelStatusCode.InvalidParam);
      throw new UnexpectedDataError("Missing requested PbkdfParameters in the response. Commissioning failed.");
    }
    if (responderSessionParams !== void 0) {
      exchange.session.timingParameters = responderSessionParams;
    }
    const peerSessionParameters = {
      ...exchange.session.parameters,
      ...responderSessionParams ?? {}
    };
    const { w0, w1 } = await Spake2p.computeW0W1(crypto, pbkdfParameters, setupPin);
    const spake2p = Spake2p.create(
      crypto,
      await crypto.computeHash([SPAKE_CONTEXT, requestPayload, responsePayload]),
      w0
    );
    const X = spake2p.computeX();
    await messenger.sendPasePake1({ x: X });
    const { y: Y, verifier } = await messenger.readPasePake2();
    const { Ke, hAY, hBX } = await spake2p.computeSecretAndVerifiersFromY(w1, X, Y);
    if (!Bytes.areEqual(verifier, hBX)) {
      await messenger.sendError(SecureChannelStatusCode.InvalidParam);
      throw new UnexpectedDataError(
        "Received incorrect key confirmation from the receiver. Commissioning failed."
      );
    }
    await messenger.sendPasePake3({ verifier: hAY });
    await messenger.waitForSuccess("PasePake3-Success");
    const secureSession = await this.#sessions.createSecureSession({
      channel,
      id: initiatorSessionId,
      fabric: void 0,
      peerNodeId: NodeId.UNSPECIFIED_NODE_ID,
      peerSessionId: responderSessionId,
      sharedSecret: Ke,
      salt: new Uint8Array(0),
      isInitiator: true,
      isResumption: false,
      peerSessionParameters
    });
    await messenger.close();
    logger.info("Paired successfully", Mark.OUTBOUND, messenger.channelName, exchange.diagnostics);
    return secureSession;
  }
}
export {
  PaseClient
};
//# sourceMappingURL=PaseClient.js.map
