import {
  buildMetadata,
  decodeMetadataSalt,
  parseMetadata,
  serializeMetadata
} from "./chunk-UKJAGEXH.js";
import {
  KDF_KEY_LENGTH,
  KDF_SALT_LENGTH,
  deriveKey
} from "./chunk-FP2373TW.js";
import {
  open,
  seal
} from "./chunk-A6XUJE5D.js";
import {
  __export
} from "./chunk-PZ5AY32C.js";

// src/secure-store/header.ts
import { mkdir, readFile, writeFile } from "fs/promises";
import path from "path";
var SECURE_STORE_DIR_NAME = ".secure-store";
var HEADER_FILENAME = "header.json";
var HEADER_FORMAT = "remnic.secure-store.header";
var HEADER_FORMAT_VERSION = 1;
var VERIFIER_PLAINTEXT = Buffer.from("remnic-secure-store-v1", "utf8");
var VERIFIER_AAD = Buffer.from("remnic-secure-store/verifier", "utf8");
function secureStoreDir(memoryDir) {
  return path.join(memoryDir, SECURE_STORE_DIR_NAME);
}
function headerPath(memoryDir) {
  return path.join(secureStoreDir(memoryDir), HEADER_FILENAME);
}
function buildHeader(options) {
  const { metadata, derivedKey } = options;
  if (!Buffer.isBuffer(derivedKey) || derivedKey.length !== KDF_KEY_LENGTH) {
    throw new Error(
      `derivedKey must be a ${KDF_KEY_LENGTH}-byte Buffer, got length=${derivedKey?.length ?? "non-buffer"}`
    );
  }
  const salt = decodeMetadataSalt(metadata);
  if (salt.length !== KDF_SALT_LENGTH) {
    throw new Error(`metadata salt is ${salt.length} bytes, expected ${KDF_SALT_LENGTH}`);
  }
  const envelope = seal(derivedKey, salt, VERIFIER_PLAINTEXT, { aad: VERIFIER_AAD });
  return {
    format: HEADER_FORMAT,
    formatVersion: HEADER_FORMAT_VERSION,
    metadata,
    verifier: envelope.toString("hex"),
    createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
  };
}
function serializeHeader(header) {
  validateHeader(header);
  const metadataString = serializeMetadata(header.metadata);
  const metadataObject = JSON.parse(metadataString);
  const ordered = {
    format: header.format,
    formatVersion: header.formatVersion,
    metadata: metadataObject,
    verifier: header.verifier,
    createdAt: header.createdAt
  };
  return JSON.stringify(ordered, null, 2);
}
function parseHeader(json) {
  if (typeof json !== "string") {
    throw new Error("header input must be a string");
  }
  let parsed;
  try {
    parsed = JSON.parse(json);
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    throw new Error(`header is not valid JSON: ${msg}`);
  }
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
    throw new Error("header must be a JSON object");
  }
  const obj = parsed;
  if (obj.format !== HEADER_FORMAT) {
    throw new Error(
      `unexpected header format: ${String(obj.format)} (expected ${HEADER_FORMAT})`
    );
  }
  if (obj.formatVersion !== HEADER_FORMAT_VERSION) {
    throw new Error(
      `unsupported header formatVersion: ${String(obj.formatVersion)} (this build supports ${HEADER_FORMAT_VERSION})`
    );
  }
  if (typeof obj.verifier !== "string" || obj.verifier.length === 0) {
    throw new Error("header.verifier must be a non-empty hex string");
  }
  if (!/^[0-9a-fA-F]+$/.test(obj.verifier)) {
    throw new Error("header.verifier must be a hex-encoded string");
  }
  if (obj.verifier.length % 2 !== 0) {
    throw new Error(
      "header.verifier hex string must have even length (each byte encodes as two hex digits)"
    );
  }
  if (typeof obj.createdAt !== "string" || obj.createdAt.length === 0) {
    throw new Error("header.createdAt must be a non-empty string");
  }
  if (typeof obj.metadata !== "object" || obj.metadata === null) {
    throw new Error("header.metadata must be an object");
  }
  const metadata = parseMetadata(JSON.stringify(obj.metadata));
  const header = {
    format: HEADER_FORMAT,
    formatVersion: HEADER_FORMAT_VERSION,
    metadata,
    verifier: obj.verifier,
    createdAt: obj.createdAt
  };
  validateHeader(header);
  return header;
}
function validateHeader(header) {
  if (header.format !== HEADER_FORMAT) {
    throw new Error(`header.format must be ${HEADER_FORMAT}`);
  }
  if (header.formatVersion !== HEADER_FORMAT_VERSION) {
    throw new Error(`header.formatVersion must be ${HEADER_FORMAT_VERSION}`);
  }
  if (typeof header.createdAt !== "string" || header.createdAt.length === 0) {
    throw new Error("header.createdAt must be a non-empty ISO-8601 string");
  }
  if (typeof header.verifier !== "string" || header.verifier.length === 0) {
    throw new Error("header.verifier must be a non-empty hex string");
  }
  if (!/^[0-9a-fA-F]+$/.test(header.verifier)) {
    throw new Error("header.verifier must be a hex-encoded string");
  }
  if (header.verifier.length % 2 !== 0) {
    throw new Error(
      "header.verifier hex string must have even length (each byte encodes as two hex digits)"
    );
  }
  if (header.metadata.format !== "remnic.secure-store.metadata") {
    throw new Error("header.metadata.format must be remnic.secure-store.metadata");
  }
}
function verifyKey(header, candidateKey) {
  if (!Buffer.isBuffer(candidateKey) || candidateKey.length !== KDF_KEY_LENGTH) {
    return false;
  }
  const envelope = Buffer.from(header.verifier, "hex");
  try {
    const plaintext = open(candidateKey, envelope, { aad: VERIFIER_AAD });
    return plaintext.equals(VERIFIER_PLAINTEXT);
  } catch {
    return false;
  }
}
function deriveKeyFromHeader(header, passphrase) {
  const salt = decodeMetadataSalt(header.metadata);
  const params = header.metadata.kdf.params;
  return deriveKey(header.metadata.kdf.algorithm, passphrase, salt, params);
}
async function readHeader(memoryDir) {
  const target = headerPath(memoryDir);
  let raw;
  try {
    raw = await readFile(target, "utf8");
  } catch (e) {
    if (e.code === "ENOENT") {
      return null;
    }
    throw e;
  }
  return parseHeader(raw);
}
async function writeHeader(memoryDir, header) {
  validateHeader(header);
  const dir = secureStoreDir(memoryDir);
  await mkdir(dir, { recursive: true });
  const target = headerPath(memoryDir);
  try {
    await writeFile(target, serializeHeader(header), {
      encoding: "utf8",
      mode: 384,
      flag: "wx"
    });
  } catch (e) {
    if (e.code === "EEXIST") {
      throw new Error(
        `secure-store header already exists at ${target}. Refusing to overwrite \u2014 initialize a fresh store or remove the existing header explicitly.`
      );
    }
    throw e;
  }
  return target;
}
function buildHeaderFromPassphrase(options) {
  const { passphrase, salt } = options;
  const algorithm = options.algorithm ?? "argon2id";
  const metadataOpts = { algorithm, salt };
  if (options.params !== void 0) metadataOpts.params = options.params;
  if (options.createdAt !== void 0) metadataOpts.createdAt = options.createdAt;
  if (options.note !== void 0) metadataOpts.note = options.note;
  const metadata = buildMetadata(metadataOpts);
  const params = metadata.kdf.params;
  const derivedKey = deriveKey(algorithm, passphrase, salt, params);
  const headerOpts = {
    metadata,
    derivedKey
  };
  if (options.createdAt !== void 0) headerOpts.createdAt = options.createdAt;
  const header = buildHeader(headerOpts);
  return { header, derivedKey };
}

// src/secure-store/keyring.ts
var keyring_exports = {};
__export(keyring_exports, {
  getKey: () => getKey,
  lock: () => lock,
  lockAll: () => lockAll,
  size: () => size,
  status: () => status,
  unlock: () => unlock
});
var ENTRIES = /* @__PURE__ */ new Map();
function unlock(id, key, now = () => /* @__PURE__ */ new Date()) {
  if (typeof id !== "string" || id.length === 0) {
    throw new Error("keyring id must be a non-empty string");
  }
  if (!Buffer.isBuffer(key) || key.length !== 32) {
    throw new Error(`keyring key must be a 32-byte Buffer, got length=${key?.length ?? "non-buffer"}`);
  }
  const existing = ENTRIES.get(id);
  if (existing) {
    existing.key.fill(0);
  }
  ENTRIES.set(id, { key, unlockedAt: now().toISOString() });
}
function getKey(id) {
  const entry = ENTRIES.get(id);
  return entry ? entry.key : null;
}
function lock(id) {
  const entry = ENTRIES.get(id);
  if (!entry) return false;
  entry.key.fill(0);
  ENTRIES.delete(id);
  return true;
}
function lockAll() {
  for (const entry of ENTRIES.values()) {
    entry.key.fill(0);
  }
  ENTRIES.clear();
}
function status(id) {
  const entry = ENTRIES.get(id);
  if (!entry) {
    return { unlocked: false, unlockedAt: null };
  }
  return { unlocked: true, unlockedAt: entry.unlockedAt };
}
function size() {
  return ENTRIES.size;
}

export {
  SECURE_STORE_DIR_NAME,
  HEADER_FILENAME,
  HEADER_FORMAT,
  HEADER_FORMAT_VERSION,
  secureStoreDir,
  headerPath,
  buildHeader,
  serializeHeader,
  parseHeader,
  validateHeader,
  verifyKey,
  deriveKeyFromHeader,
  readHeader,
  writeHeader,
  buildHeaderFromPassphrase,
  unlock,
  getKey,
  lock,
  status,
  keyring_exports
};
//# sourceMappingURL=chunk-BJMBJZ2Y.js.map