import {
  getKey,
  readHeader,
  secureStoreDir
} from "./chunk-BJMBJZ2Y.js";
import {
  open,
  seal
} from "./chunk-A6XUJE5D.js";

// src/transfer/capsule-crypto.ts
import { open as openFileHandle, readFile, writeFile } from "fs/promises";
import path from "path";
var MAGIC = Buffer.from("REMNIC-ENC\0", "ascii");
var FORMAT_VERSION = 2;
var MIN_ENC_SIZE_V1 = MAGIC.length + 1 + 45;
var MIN_ENC_SIZE = MAGIC.length + 1 + 2;
async function isEncryptedCapsuleFile(filePath) {
  if (!filePath.endsWith(".enc")) return false;
  let fh = null;
  try {
    fh = await openFileHandle(filePath, "r");
    const buf = Buffer.allocUnsafe(MAGIC.length);
    const { bytesRead } = await fh.read(buf, 0, MAGIC.length, 0);
    if (bytesRead < MAGIC.length) return false;
    return buf.equals(MAGIC);
  } catch {
    return false;
  } finally {
    if (fh !== null) {
      await fh.close().catch(() => void 0);
    }
  }
}
async function encryptCapsuleFile(opts) {
  const encPath = opts.outPath ?? `${opts.sourceGzPath}.enc`;
  const key = getKeyOrThrow(opts.memoryDir, "encrypt capsule");
  const plaintext = await readFile(opts.sourceGzPath);
  const basename = path.basename(encPath);
  const aad = Buffer.from(basename, "utf-8");
  const kdfSection = await loadKdfSection(opts.memoryDir);
  const envelope = seal(key, kdfSection.salt, plaintext, { aad });
  const versionBuf = Buffer.alloc(1);
  versionBuf.writeUInt8(FORMAT_VERSION, 0);
  const kdfJsonBuf = Buffer.from(kdfSection.json, "utf-8");
  const kdfLenBuf = Buffer.alloc(2);
  kdfLenBuf.writeUInt16LE(kdfJsonBuf.length, 0);
  const output = Buffer.concat([MAGIC, versionBuf, kdfLenBuf, kdfJsonBuf, envelope]);
  await writeFile(encPath, output);
  return { encPath };
}
async function decryptCapsuleFile(opts) {
  const gzPath = opts.outPath ?? opts.encPath.replace(/\.enc$/, "");
  const buf = await readFile(opts.encPath);
  if (buf.length < MIN_ENC_SIZE_V1) {
    throw new Error(
      `decryptCapsuleFile: file too short to be an encrypted capsule: ${opts.encPath}`
    );
  }
  if (!buf.subarray(0, MAGIC.length).equals(MAGIC)) {
    throw new Error(
      `decryptCapsuleFile: file does not start with REMNIC-ENC magic: ${opts.encPath}`
    );
  }
  const version = buf.readUInt8(MAGIC.length);
  if (version !== 1 && version !== 2) {
    throw new Error(
      `decryptCapsuleFile: unsupported encrypted-capsule format version ${version} (this build supports versions 1 and 2): ${opts.encPath}`
    );
  }
  const { key, envelopeOffset } = resolveKeyAndOffset(buf, version, opts.memoryDir, "decryptCapsuleFile", opts.encPath);
  const envelope = buf.subarray(envelopeOffset);
  const basename = path.basename(opts.encPath);
  const aad = Buffer.from(basename, "utf-8");
  let plaintext;
  try {
    plaintext = open(key, envelope, { aad });
  } catch (cause) {
    throw new Error(
      `decryptCapsuleFile: authentication failed \u2014 wrong passphrase, tampered archive, or key mismatch. Ensure the secure-store is unlocked with the correct passphrase and the archive has not been modified: ${opts.encPath}`,
      { cause }
    );
  }
  await writeFile(gzPath, plaintext);
  return { gzPath };
}
async function decryptCapsuleFileInMemory(encPath, memoryDir) {
  const buf = await readFile(encPath);
  if (buf.length < MIN_ENC_SIZE_V1) {
    throw new Error(
      `decryptCapsuleFileInMemory: file too short to be an encrypted capsule: ${encPath}`
    );
  }
  if (!buf.subarray(0, MAGIC.length).equals(MAGIC)) {
    throw new Error(
      `decryptCapsuleFileInMemory: file does not start with REMNIC-ENC magic: ${encPath}`
    );
  }
  const version = buf.readUInt8(MAGIC.length);
  if (version !== 1 && version !== 2) {
    throw new Error(
      `decryptCapsuleFileInMemory: unsupported encrypted-capsule format version ${version} (this build supports versions 1 and 2): ${encPath}`
    );
  }
  const { key, envelopeOffset } = resolveKeyAndOffset(buf, version, memoryDir, "decryptCapsuleFileInMemory", encPath);
  const envelope = buf.subarray(envelopeOffset);
  const basename = path.basename(encPath);
  const aad = Buffer.from(basename, "utf-8");
  try {
    return open(key, envelope, { aad });
  } catch (cause) {
    throw new Error(
      `decryptCapsuleFileInMemory: authentication failed \u2014 wrong passphrase, tampered archive, or key mismatch. Ensure the secure-store is unlocked with the correct passphrase and the archive has not been modified: ${encPath}`,
      { cause }
    );
  }
}
async function loadKdfSection(memoryDir) {
  try {
    const header = await readHeader(memoryDir);
    if (header !== null) {
      const { decodeMetadataSalt } = await import("./metadata-FC3XPDRQ.js");
      const salt2 = decodeMetadataSalt(header.metadata);
      const kdf = header.metadata.kdf;
      const json2 = JSON.stringify({
        algorithm: kdf.algorithm,
        params: kdf.params,
        salt: salt2.toString("hex")
      });
      return { json: json2, salt: salt2 };
    }
  } catch {
  }
  const { generateSalt } = await import("./cipher-GVE2GQ5H.js");
  const salt = generateSalt();
  const { DEFAULT_ARGON2ID_PARAMS } = await import("./kdf-7S6RWKLZ.js");
  const json = JSON.stringify({
    algorithm: "argon2id",
    params: DEFAULT_ARGON2ID_PARAMS,
    salt: salt.toString("hex")
  });
  return { json, salt };
}
function getKeyOrThrow(memoryDir, action) {
  const storeId = secureStoreDir(memoryDir);
  const key = getKey(storeId);
  if (key === null) {
    throw new Error(
      `Secure-store is locked or not initialized \u2014 cannot ${action}. Run \`remnic secure-store unlock\` first, or \`remnic secure-store init\` if the store has never been initialized.`
    );
  }
  return key;
}
function resolveKeyAndOffset(buf, version, memoryDir, caller, encPath) {
  if (version === 1) {
    const key2 = getKeyOrThrow(memoryDir, "decrypt capsule");
    return { key: key2, envelopeOffset: MAGIC.length + 1 };
  }
  const kdfLenOffset = MAGIC.length + 1;
  if (buf.length < kdfLenOffset + 2) {
    throw new Error(
      `${caller}: file too short for format v2 KDF length field: ${encPath}`
    );
  }
  const kdfLen = buf.readUInt16LE(kdfLenOffset);
  const kdfJsonOffset = kdfLenOffset + 2;
  if (buf.length < kdfJsonOffset + kdfLen) {
    throw new Error(
      `${caller}: file too short for format v2 KDF params section (expected ${kdfLen} bytes): ${encPath}`
    );
  }
  const envelopeOffset = kdfJsonOffset + kdfLen;
  const key = getKeyOrThrow(memoryDir, "decrypt capsule");
  return { key, envelopeOffset };
}

export {
  isEncryptedCapsuleFile,
  encryptCapsuleFile,
  decryptCapsuleFile,
  decryptCapsuleFileInMemory
};
//# sourceMappingURL=chunk-KNKUID7G.js.map