"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 Ccm_exports = {};
__export(Ccm_exports, {
  BYTES_IN_LENGTH: () => BYTES_IN_LENGTH,
  Ccm: () => Ccm,
  MAX_CIPHERTEXT_LENGTH: () => MAX_CIPHERTEXT_LENGTH,
  MAX_PLAINTEXT_LENGTH: () => MAX_PLAINTEXT_LENGTH
});
module.exports = __toCommonJS(Ccm_exports);
var import_CryptoConstants = require("#crypto/CryptoConstants.js");
var import_CryptoError = require("#crypto/CryptoError.js");
var import_Aes = require("./Aes.js");
var import_WordArray = require("./WordArray.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
function Ccm(key) {
  const aes = (0, import_Aes.Aes)(key);
  return {
    encrypt(input) {
      validateNonceAndAdata(input);
      const ptLength = input.pt.length;
      if (ptLength > MAX_PLAINTEXT_LENGTH) {
        throw new import_CryptoError.CryptoInputError(
          `Cannot encrypt plaintext exceeding maximum length of ${MAX_PLAINTEXT_LENGTH}`
        );
      }
      const ptView = new DataView(input.pt.buffer, input.pt.byteOffset, input.pt.byteLength);
      const ct = new Uint8Array(ptLength + import_CryptoConstants.CRYPTO_AEAD_MIC_LENGTH_BYTES);
      const ctView = new DataView(ct.buffer);
      cbcMac(input, ptView, ptLength);
      ctr(input, ptView, ctView, ptLength, computedMic);
      for (let i = 0; i < computedMic.words.length; i++) {
        ctView.setInt32(input.pt.length + i * 4, computedMic.words[i]);
      }
      return ct;
    },
    decrypt(input) {
      validateNonceAndAdata(input);
      if (input.ct.length > MAX_CIPHERTEXT_LENGTH) {
        throw new import_CryptoError.CryptoInputError(
          `Cannot decrypt ciphertext longer than maximum length of ${MAX_CIPHERTEXT_LENGTH}`
        );
      }
      const ptLength = input.ct.length - import_CryptoConstants.CRYPTO_AEAD_MIC_LENGTH_BYTES;
      if (ptLength < 0) {
        throw new import_CryptoError.CryptoInputError(
          `Cannot decrypt ciphertext shorter than minimum length of ${import_CryptoConstants.CRYPTO_AEAD_MIC_LENGTH_BYTES}`
        );
      }
      const ctView = new DataView(input.ct.buffer, input.ct.byteOffset, ptLength);
      import_WordArray.WordArray.bytesToBlock(
        new DataView(input.ct.buffer, input.ct.byteOffset, input.ct.byteLength),
        inputMic.words,
        ptLength
      );
      const pt = new Uint8Array(ptLength);
      const ptView = new DataView(pt.buffer);
      ctr(input, ctView, ptView, ptLength, inputMic);
      cbcMac(input, ptView, ptLength);
      let micDiff = 0;
      for (let i = 0; i < computedMic.words.length; i++) {
        micDiff |= inputMic.words[i] ^ computedMic.words[i];
      }
      if (micDiff !== 0) {
        throw new import_CryptoError.CryptoDecryptError("Message authentication failed: tag mismatch");
      }
      return pt;
    }
  };
  function cbcMac(input, pt, ptLength) {
    const adataLength = input.adata?.length;
    computedMic.bytes[0] = (adataLength ? 1 << 6 : 0) | import_CryptoConstants.CRYPTO_AEAD_MIC_LENGTH_BYTES - 2 << 2 | BYTES_IN_LENGTH - 1;
    computedMic.bytes.set(input.nonce, 1);
    import_WordArray.WordArray.bytesToBlock(computedMic.view, computedMic.words);
    computedMic.words[3] = computedMic.words[3] & 4294901760 | ptLength;
    aes.encrypt(computedMic.words);
    if (adataLength) {
      tempBlock1.view.setInt16(0, input.adata.length);
      for (let i = 0; i < 14; i++) {
        tempBlock1.bytes[i + 2] = i < adataLength ? input.adata[i] : 0;
      }
      import_WordArray.WordArray.bytesToBlock(tempBlock1.view, tempBlock1.words);
      add();
      if (adataLength > 14) {
        const adataView = new DataView(input.adata.buffer, input.adata.byteOffset, input.adata.byteLength);
        for (let i = 14; i < adataLength; i += 16) {
          import_WordArray.WordArray.bytesToBlock(adataView, tempBlock1.words, i);
          add();
        }
      }
    }
    if (ptLength) {
      for (let i = 0; i < ptLength; i += 16) {
        import_WordArray.WordArray.bytesToBlock(pt, tempBlock1.words, i);
        add();
      }
    }
    function add() {
      computedMic.words[0] ^= tempBlock1.words[0];
      computedMic.words[1] ^= tempBlock1.words[1];
      computedMic.words[2] ^= tempBlock1.words[2];
      computedMic.words[3] ^= tempBlock1.words[3];
      aes.encrypt(computedMic.words);
    }
  }
  function ctr(input, from, to, ptLength, mic) {
    tempBlock1.bytes[0] = BYTES_IN_LENGTH - 1;
    tempBlock1.bytes.set(input.nonce, 1);
    tempBlock1.bytes[14] = 0;
    tempBlock1.bytes[15] = 0;
    import_WordArray.WordArray.bytesToBlock(tempBlock1.view, ctrBlock.words);
    aes.encrypt(ctrBlock.words, tempBlock1.words);
    mic.words[0] ^= tempBlock1.words[0];
    mic.words[1] ^= tempBlock1.words[1];
    mic.words[2] ^= tempBlock1.words[2];
    mic.words[3] ^= tempBlock1.words[3];
    for (let i = 0; i < ptLength; ) {
      ctrBlock.words[3]++;
      import_WordArray.WordArray.bytesToBlock(from, tempBlock1.words, i);
      aes.encrypt(ctrBlock.words, tempBlock2.words);
      for (let j = 0; j < 4 && i < ptLength; j++, i += 4) {
        const tempWord = tempBlock2.words[j];
        if (i + 4 < ptLength) {
          to.setInt32(i, from.getInt32(i) ^ tempWord);
        } else {
          const partial = import_WordArray.WordArray.readPartialWord(from, i, ptLength - i) ^ tempWord;
          import_WordArray.WordArray.writePartialWord(partial, to, i, ptLength - i);
        }
      }
    }
  }
}
const BYTES_IN_LENGTH = 2;
const MAX_CIPHERTEXT_LENGTH = Math.pow(2, BYTES_IN_LENGTH * 8);
const MAX_PLAINTEXT_LENGTH = MAX_CIPHERTEXT_LENGTH - import_CryptoConstants.CRYPTO_AEAD_MIC_LENGTH_BYTES;
class SingletonBuffer {
  #words;
  #bytes;
  #view;
  get words() {
    if (this.#words === void 0) {
      this.#words = new Int32Array(4);
    }
    return this.#words;
  }
  get bytes() {
    if (this.#bytes === void 0) {
      this.#bytes = new Uint8Array(this.words.buffer);
    }
    return this.#bytes;
  }
  /**
   * The word and byte views of the buffer above are insufficient because we must account for platform endianness.  So
   * we also make a DataView available.
   */
  get view() {
    if (this.#view === void 0) {
      this.#view = new DataView(this.words.buffer);
    }
    return this.#view;
  }
}
const computedMic = new SingletonBuffer();
const inputMic = new SingletonBuffer();
const ctrBlock = new SingletonBuffer();
const tempBlock1 = new SingletonBuffer();
const tempBlock2 = new SingletonBuffer();
function validateNonceAndAdata(input) {
  if (input.nonce.length !== import_CryptoConstants.CRYPTO_AEAD_NONCE_LENGTH_BYTES) {
    throw new import_CryptoError.CryptoInputError("Nonce must be 13 bytes");
  }
  if (input.adata && input.adata.length > 65535) {
    throw new import_CryptoError.CryptoInputError(`Associated adata exceeds maximum length of ${MAX_PLAINTEXT_LENGTH}`);
  }
}
//# sourceMappingURL=Ccm.js.map
