/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { ImplementationError } from "#MatterError.js";
import { Bytes } from "#util/Bytes.js";
import { asError } from "#util/Error.js";
import {
  Crypto,
  CRYPTO_AUTH_TAG_LENGTH,
  CRYPTO_EC_CURVE,
  CRYPTO_EC_KEY_BYTES,
  CRYPTO_ENCRYPT_ALGORITHM,
  CRYPTO_HASH_ALGORITHM,
  CRYPTO_SYMMETRIC_KEY_LENGTH
} from "./Crypto.js";
import { CryptoDecryptError, CryptoVerifyError } from "./CryptoError.js";
import { EcdsaSignature } from "./EcdsaSignature.js";
import { PrivateKey } from "./Key.js";
class NodeJsStyleCrypto extends Crypto {
  implementationName = "Node.js";
  #crypto;
  constructor(crypto) {
    super();
    this.#crypto = crypto;
  }
  encrypt(key, data, nonce, aad) {
    const cipher = this.#crypto.createCipheriv(CRYPTO_ENCRYPT_ALGORITHM, Bytes.of(key), Bytes.of(nonce), {
      authTagLength: CRYPTO_AUTH_TAG_LENGTH
    });
    if (aad !== void 0) {
      cipher.setAAD(Bytes.of(aad), { plaintextLength: data.byteLength });
    }
    const encrypted = cipher.update(Bytes.of(data));
    cipher.final();
    return Bytes.concat(Bytes.of(encrypted), Bytes.of(cipher.getAuthTag()));
  }
  decrypt(key, encrypted, nonce, aad) {
    const cipher = this.#crypto.createDecipheriv(CRYPTO_ENCRYPT_ALGORITHM, Bytes.of(key), Bytes.of(nonce), {
      authTagLength: CRYPTO_AUTH_TAG_LENGTH
    });
    const data = Bytes.of(encrypted);
    const plaintextLength = data.length - CRYPTO_AUTH_TAG_LENGTH;
    if (aad !== void 0) {
      cipher.setAAD(Bytes.of(aad), { plaintextLength });
    }
    cipher.setAuthTag(data.slice(plaintextLength));
    const result = cipher.update(data.slice(0, plaintextLength));
    try {
      cipher.final();
    } catch (e) {
      throw new CryptoDecryptError(`${CRYPTO_ENCRYPT_ALGORITHM} decryption failed: ${asError(e).message}`);
    }
    return Bytes.of(result);
  }
  randomBytes(length) {
    return Bytes.of(this.#crypto.randomBytes(length));
  }
  ecdhGeneratePublicKey() {
    const ecdh = this.#crypto.createECDH(CRYPTO_EC_CURVE);
    ecdh.generateKeys();
    return { publicKey: Bytes.of(ecdh.getPublicKey()), ecdh };
  }
  ecdhGeneratePublicKeyAndSecret(peerPublicKey) {
    const ecdh = this.#crypto.createECDH(CRYPTO_EC_CURVE);
    ecdh.generateKeys();
    return {
      publicKey: Bytes.of(ecdh.getPublicKey()),
      sharedSecret: Bytes.of(ecdh.computeSecret(Bytes.of(peerPublicKey)))
    };
  }
  async #hashAsyncIteratorData(hasher, iteratorFunc) {
    while (true) {
      const { value, done } = await iteratorFunc();
      if (value === void 0 || done) break;
      hasher.update(Bytes.of(value));
    }
  }
  computeHash(data, algorithm = "SHA-256") {
    const hasher = this.#crypto.createHash(algorithm);
    if (Array.isArray(data)) {
      data.forEach((chunk) => hasher.update(Bytes.of(chunk)));
    } else if (Bytes.isBytes(data)) {
      hasher.update(Bytes.of(data));
    } else {
      let iteratorFunc;
      if ("read" in data && typeof data.read === "function") {
        iteratorFunc = data.read.bind(data);
      } else if ("next" in data && typeof data.next === "function") {
        iteratorFunc = data.next.bind(data);
      } else {
        throw new ImplementationError(`Invalid data type for computeHash with algorithm ${algorithm}`);
      }
      return this.#hashAsyncIteratorData(hasher, iteratorFunc).then(() => Bytes.of(hasher.digest()));
    }
    return Bytes.of(hasher.digest());
  }
  createPbkdf2Key(secret, salt, iteration, keyLength) {
    return new Promise((resolver, rejecter) => {
      this.#crypto.pbkdf2(
        Bytes.of(secret),
        Bytes.of(salt),
        iteration,
        keyLength,
        CRYPTO_HASH_ALGORITHM,
        (error, key) => {
          if (error !== null) rejecter(error);
          resolver(Bytes.of(key));
        }
      );
    });
  }
  createHkdfKey(secret, salt, info, length = CRYPTO_SYMMETRIC_KEY_LENGTH) {
    return new Promise((resolver, rejecter) => {
      this.#crypto.hkdf(
        CRYPTO_HASH_ALGORITHM,
        Bytes.of(secret),
        Bytes.of(salt),
        Bytes.of(info),
        length,
        (error, key) => {
          if (error !== null) rejecter(error);
          resolver(Bytes.of(key));
        }
      );
    });
  }
  signHmac(key, data) {
    const hmac = this.#crypto.createHmac(CRYPTO_HASH_ALGORITHM, Bytes.of(key));
    hmac.update(Bytes.of(data));
    return Bytes.of(hmac.digest());
  }
  signEcdsa(privateKey, data) {
    const signer = this.#crypto.createSign(CRYPTO_HASH_ALGORITHM);
    if (Array.isArray(data)) {
      data.forEach((chunk) => signer.update(Bytes.of(chunk)));
    } else {
      signer.update(Bytes.of(data));
    }
    return new EcdsaSignature(
      Bytes.of(
        signer.sign({
          key: privateKey,
          format: "jwk",
          type: "pkcs8",
          dsaEncoding: "ieee-p1363"
        })
      )
    );
  }
  verifyEcdsa(publicKey, data, signature) {
    const verifier = this.#crypto.createVerify(CRYPTO_HASH_ALGORITHM);
    verifier.update(Bytes.of(data));
    const success = verifier.verify(
      {
        key: publicKey,
        format: "jwk",
        type: "spki",
        dsaEncoding: "ieee-p1363"
      },
      Bytes.of(signature.bytes)
    );
    if (!success) throw new CryptoVerifyError("Signature verification failed");
  }
  createKeyPair() {
    const ecdh = this.#crypto.createECDH(CRYPTO_EC_CURVE);
    ecdh.generateKeys();
    const privateKey = new Uint8Array(CRYPTO_EC_KEY_BYTES);
    const nodePrivateKey = ecdh.getPrivateKey();
    privateKey.set(nodePrivateKey, CRYPTO_EC_KEY_BYTES - nodePrivateKey.length);
    return PrivateKey(privateKey, { publicKey: Bytes.of(ecdh.getPublicKey()) });
  }
  generateDhSecret(key, peerKey) {
    const ecdh = this.#crypto.createECDH(CRYPTO_EC_CURVE);
    ecdh.setPrivateKey(Bytes.of(key.privateBits));
    return Bytes.of(ecdh.computeSecret(Bytes.of(peerKey.publicBits)));
  }
}
export {
  NodeJsStyleCrypto
};
//# sourceMappingURL=NodeJsStyleCrypto.js.map
