import {
  DEFAULT_ARGON2ID_PARAMS,
  DEFAULT_SCRYPT_PARAMS,
  KDF_SALT_LENGTH
} from "./chunk-FP2373TW.js";

// src/secure-store/metadata.ts
var METADATA_FORMAT = "remnic.secure-store.metadata";
var METADATA_FORMAT_VERSION = 1;
function bytesToHex(bytes) {
  return Buffer.from(bytes).toString("hex");
}
function hexToBytes(hex, expectedLength) {
  if (typeof hex !== "string") {
    throw new Error("hex field must be a string");
  }
  if (!/^[0-9a-f]*$/i.test(hex)) {
    throw new Error("hex field must contain only hexadecimal characters");
  }
  if (hex.length % 2 !== 0) {
    throw new Error("hex field must have even length");
  }
  const buf = Buffer.from(hex, "hex");
  if (buf.length !== expectedLength) {
    throw new Error(
      `hex field decoded to ${buf.length} bytes, expected ${expectedLength}`
    );
  }
  return buf;
}
function buildMetadata(options) {
  const { algorithm, salt } = options;
  if (!(salt instanceof Uint8Array) || salt.length !== KDF_SALT_LENGTH) {
    throw new Error(
      `salt must be ${KDF_SALT_LENGTH} bytes, got ${salt?.length ?? "non-buffer"}`
    );
  }
  const createdAt = options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
  let kdf;
  if (algorithm === "scrypt") {
    const params = options.params ?? {
      ...DEFAULT_SCRYPT_PARAMS
    };
    kdf = { algorithm: "scrypt", params, salt: bytesToHex(salt) };
  } else if (algorithm === "argon2id") {
    const params = options.params ?? {
      ...DEFAULT_ARGON2ID_PARAMS
    };
    kdf = { algorithm: "argon2id", params, salt: bytesToHex(salt) };
  } else {
    throw new Error(`unknown KDF algorithm: ${algorithm}`);
  }
  const meta = {
    format: METADATA_FORMAT,
    formatVersion: METADATA_FORMAT_VERSION,
    kdf,
    createdAt
  };
  if (options.note !== void 0) {
    meta.note = options.note;
  }
  return meta;
}
function serializeMetadata(meta) {
  validateMetadata(meta);
  const ordered = {
    format: meta.format,
    formatVersion: meta.formatVersion,
    kdf: orderKdf(meta.kdf),
    createdAt: meta.createdAt
  };
  if (meta.note !== void 0) {
    ordered.note = meta.note;
  }
  return JSON.stringify(ordered, null, 2);
}
function orderKdf(kdf) {
  if (kdf.algorithm === "scrypt") {
    return {
      algorithm: kdf.algorithm,
      params: {
        N: kdf.params.N,
        r: kdf.params.r,
        p: kdf.params.p,
        keyLength: kdf.params.keyLength,
        maxmem: kdf.params.maxmem
      },
      salt: kdf.salt
    };
  }
  return {
    algorithm: kdf.algorithm,
    params: {
      memoryKiB: kdf.params.memoryKiB,
      iterations: kdf.params.iterations,
      parallelism: kdf.params.parallelism,
      keyLength: kdf.params.keyLength
    },
    salt: kdf.salt
  };
}
function parseMetadata(json) {
  if (typeof json !== "string") {
    throw new Error("metadata 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(`metadata is not valid JSON: ${msg}`);
  }
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
    throw new Error("metadata must be a JSON object");
  }
  const obj = parsed;
  if (obj.format !== METADATA_FORMAT) {
    throw new Error(
      `unexpected metadata format: ${String(obj.format)} (expected ${METADATA_FORMAT})`
    );
  }
  if (obj.formatVersion !== METADATA_FORMAT_VERSION) {
    throw new Error(
      `unsupported metadata formatVersion: ${String(obj.formatVersion)} (this build supports ${METADATA_FORMAT_VERSION})`
    );
  }
  if (typeof obj.createdAt !== "string" || obj.createdAt.length === 0) {
    throw new Error("metadata.createdAt must be a non-empty string");
  }
  if (obj.note !== void 0 && typeof obj.note !== "string") {
    throw new Error("metadata.note must be a string when present");
  }
  const kdf = parseKdf(obj.kdf);
  const meta = {
    format: METADATA_FORMAT,
    formatVersion: METADATA_FORMAT_VERSION,
    kdf,
    createdAt: obj.createdAt
  };
  if (typeof obj.note === "string") {
    meta.note = obj.note;
  }
  validateMetadata(meta);
  return meta;
}
function parseKdf(value) {
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
    throw new Error("metadata.kdf must be an object");
  }
  const k = value;
  if (k.algorithm !== "scrypt" && k.algorithm !== "argon2id") {
    throw new Error(`metadata.kdf.algorithm must be 'scrypt' or 'argon2id', got ${String(k.algorithm)}`);
  }
  if (typeof k.salt !== "string") {
    throw new Error("metadata.kdf.salt must be a hex string");
  }
  hexToBytes(k.salt, KDF_SALT_LENGTH);
  if (typeof k.params !== "object" || k.params === null || Array.isArray(k.params)) {
    throw new Error("metadata.kdf.params must be an object");
  }
  const params = k.params;
  if (k.algorithm === "scrypt") {
    const required = ["N", "r", "p", "keyLength", "maxmem"];
    for (const key of required) {
      if (typeof params[key] !== "number" || !Number.isFinite(params[key])) {
        throw new Error(`metadata.kdf.params.${key} must be a finite number`);
      }
    }
    return {
      algorithm: "scrypt",
      params: {
        N: params.N,
        r: params.r,
        p: params.p,
        keyLength: params.keyLength,
        maxmem: params.maxmem
      },
      salt: k.salt
    };
  }
  const required2 = [
    "memoryKiB",
    "iterations",
    "parallelism",
    "keyLength"
  ];
  for (const key of required2) {
    if (typeof params[key] !== "number" || !Number.isFinite(params[key])) {
      throw new Error(`metadata.kdf.params.${key} must be a finite number`);
    }
  }
  return {
    algorithm: "argon2id",
    params: {
      memoryKiB: params.memoryKiB,
      iterations: params.iterations,
      parallelism: params.parallelism,
      keyLength: params.keyLength
    },
    salt: k.salt
  };
}
function validateMetadata(meta) {
  if (meta.format !== METADATA_FORMAT) {
    throw new Error(`metadata.format must be ${METADATA_FORMAT}`);
  }
  if (meta.formatVersion !== METADATA_FORMAT_VERSION) {
    throw new Error(`metadata.formatVersion must be ${METADATA_FORMAT_VERSION}`);
  }
  if (typeof meta.createdAt !== "string" || meta.createdAt.length === 0) {
    throw new Error("metadata.createdAt must be a non-empty ISO-8601 string");
  }
  hexToBytes(meta.kdf.salt, KDF_SALT_LENGTH);
  if (meta.kdf.algorithm === "scrypt") {
    const { N, r, p, keyLength, maxmem } = meta.kdf.params;
    if (!Number.isInteger(N) || N < 2 || N > 2 ** 30 || !Number.isInteger(Math.log2(N))) {
      throw new Error("metadata.kdf.params.N must be a power of 2 in [2, 2^30]");
    }
    if (!Number.isInteger(r) || r < 1) {
      throw new Error("metadata.kdf.params.r must be a positive integer");
    }
    if (!Number.isInteger(p) || p < 1) {
      throw new Error("metadata.kdf.params.p must be a positive integer");
    }
    if (!Number.isInteger(keyLength) || keyLength !== 32) {
      throw new Error("metadata.kdf.params.keyLength must be 32 (AES-256 requires a 32-byte key)");
    }
    if (!Number.isInteger(maxmem) || maxmem < 1024) {
      throw new Error("metadata.kdf.params.maxmem must be a positive integer");
    }
  } else if (meta.kdf.algorithm === "argon2id") {
    const { memoryKiB, iterations, parallelism, keyLength } = meta.kdf.params;
    if (!Number.isInteger(memoryKiB) || memoryKiB < 8) {
      throw new Error("metadata.kdf.params.memoryKiB must be \u2265 8");
    }
    if (!Number.isInteger(iterations) || iterations < 1) {
      throw new Error("metadata.kdf.params.iterations must be a positive integer");
    }
    if (!Number.isInteger(parallelism) || parallelism < 1 || parallelism > 255) {
      throw new Error("metadata.kdf.params.parallelism must be an integer in [1, 255]");
    }
    if (!Number.isInteger(keyLength) || keyLength !== 32) {
      throw new Error("metadata.kdf.params.keyLength must be 32 (AES-256 requires a 32-byte key)");
    }
  }
}
function decodeMetadataSalt(meta) {
  return hexToBytes(meta.kdf.salt, KDF_SALT_LENGTH);
}

export {
  METADATA_FORMAT,
  METADATA_FORMAT_VERSION,
  buildMetadata,
  serializeMetadata,
  parseMetadata,
  validateMetadata,
  decodeMetadataSalt
};
//# sourceMappingURL=chunk-UKJAGEXH.js.map