{"version":3,"file":"index.cjs","names":["AES","AES","CryptoJS","CryptoJS"],"sources":["../../../../../../encryption/src/errors.ts","../../../../../../encryption/src/crypto-runtime.ts","../../../../../../encryption/src/envelope.ts","../../../../../../encryption/src/configurations.ts","../../../../../../encryption/src/key-derivation.ts","../../../../../../encryption/src/legacy.ts","../../../../../../encryption/src/encryption.ts"],"sourcesContent":["/**\n * Base class for every error thrown by this package.\n *\n * Consumers can catch `EncryptionError` to handle any crypto failure, or the\n * narrower subclasses below when the distinction matters.\n */\nexport class EncryptionError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = \"EncryptionError\";\n    // Keeps `instanceof` working when the package is transpiled down to ES5.\n    Object.setPrototypeOf(this, new.target.prototype);\n  }\n}\n\n/**\n * Thrown when no encryption key was supplied, per call or in configurations.\n */\nexport class MissingEncryptionKeyError extends EncryptionError {\n  constructor(\n    message = \"Missing Encryption key, please define it or set it in encryption configurations\"\n  ) {\n    super(message);\n    this.name = \"MissingEncryptionKeyError\";\n  }\n}\n\n/**\n * Thrown when the runtime does not expose a usable WebCrypto implementation.\n *\n * There is deliberately no fallback: silently downgrading to a non-CSPRNG or a\n * hand-rolled cipher would defeat the point of the AEAD migration.\n */\nexport class UnsupportedRuntimeError extends EncryptionError {\n  constructor(message: string) {\n    super(message);\n    this.name = \"UnsupportedRuntimeError\";\n  }\n}\n\n/**\n * Thrown when a ciphertext cannot be decrypted.\n *\n * The message intentionally does NOT distinguish \"wrong key\" from \"tampered\n * ciphertext\" for the AES-GCM path — the two are indistinguishable to the\n * cipher itself, and keeping them indistinguishable to the caller avoids\n * handing an attacker a decryption oracle.\n */\nexport class DecryptionError extends EncryptionError {\n  constructor(message: string) {\n    super(message);\n    this.name = \"DecryptionError\";\n  }\n}\n","import { UnsupportedRuntimeError } from \"./errors\";\n\n/**\n * Minimal structural views over the WebCrypto surface this package touches.\n *\n * They exist so the package type-checks without requiring `lib.dom` (browser)\n * or `@types/node` (server) in the consumer's tsconfig — the real `CryptoKey`\n * and `SubtleCrypto` are structurally assignable to these.\n */\nexport type CryptoKeyLike = {\n  readonly type: string;\n  readonly extractable: boolean;\n};\n\nexport type SubtleCryptoLike = {\n  importKey(\n    format: \"raw\",\n    keyData: Uint8Array,\n    algorithm: any,\n    extractable: boolean,\n    usages: string[]\n  ): Promise<any>;\n  deriveKey(\n    algorithm: any,\n    baseKey: any,\n    derivedKeyAlgorithm: any,\n    extractable: boolean,\n    usages: string[]\n  ): Promise<any>;\n  encrypt(algorithm: any, key: any, data: Uint8Array): Promise<ArrayBuffer>;\n  decrypt(algorithm: any, key: any, data: Uint8Array): Promise<ArrayBuffer>;\n};\n\ntype CryptoLike = {\n  subtle?: SubtleCryptoLike;\n  getRandomValues?<T extends Uint8Array>(array: T): T;\n};\n\nconst RUNTIME_HINT =\n  \"AES-GCM requires WebCrypto (globalThis.crypto.subtle), available in browsers over HTTPS/localhost and in Node.js 18+.\";\n\nfunction getCrypto(): CryptoLike {\n  const runtimeCrypto = (globalThis as any)?.crypto as CryptoLike | undefined;\n\n  if (!runtimeCrypto) {\n    throw new UnsupportedRuntimeError(\n      `No Web Crypto API found in this runtime. ${RUNTIME_HINT}`\n    );\n  }\n\n  return runtimeCrypto;\n}\n\n/**\n * Get the runtime's `crypto.subtle`, or throw a clear error.\n *\n * Browsers only expose `subtle` in a secure context, which is why the error\n * mentions HTTPS/localhost — that is by far the most common cause.\n */\nexport function getSubtle(): SubtleCryptoLike {\n  const { subtle } = getCrypto();\n\n  if (!subtle) {\n    throw new UnsupportedRuntimeError(\n      `crypto.subtle is not available in this runtime (an insecure browser context, or a runtime older than Node.js 18). ${RUNTIME_HINT}`\n    );\n  }\n\n  return subtle;\n}\n\n/**\n * Fill a buffer of the given length from the platform CSPRNG.\n *\n * Never falls back to `Math.random` — if there is no CSPRNG we refuse to\n * produce a salt or a nonce at all.\n */\nexport function randomBytes(length: number): Uint8Array {\n  const runtimeCrypto = getCrypto();\n\n  if (typeof runtimeCrypto.getRandomValues !== \"function\") {\n    throw new UnsupportedRuntimeError(\n      `crypto.getRandomValues is not available in this runtime; refusing to generate a salt/nonce from a non-cryptographic source. ${RUNTIME_HINT}`\n    );\n  }\n\n  return runtimeCrypto.getRandomValues(new Uint8Array(length));\n}\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder(\"utf-8\", { fatal: true });\n\nexport function utf8ToBytes(text: string): Uint8Array {\n  return textEncoder.encode(text);\n}\n\nexport function bytesToUtf8(bytes: Uint8Array): string {\n  return textDecoder.decode(bytes);\n}\n\nexport function concatBytes(...chunks: Uint8Array[]): Uint8Array {\n  const total = chunks.reduce((length, chunk) => length + chunk.length, 0);\n  const output = new Uint8Array(total);\n\n  let offset = 0;\n  for (const chunk of chunks) {\n    output.set(chunk, offset);\n    offset += chunk.length;\n  }\n\n  return output;\n}\n\n// `btoa` chokes on very large argument lists, so the binary string is built in\n// chunks — a 10k-character payload is a realistic input for this package.\nconst BASE64_CHUNK_SIZE = 0x8000;\n\nexport function toBase64(bytes: Uint8Array): string {\n  let binary = \"\";\n\n  for (let index = 0; index < bytes.length; index += BASE64_CHUNK_SIZE) {\n    binary += String.fromCharCode(\n      ...bytes.subarray(index, index + BASE64_CHUNK_SIZE)\n    );\n  }\n\n  return btoa(binary);\n}\n\n/**\n * Decode base64 into bytes, returning `null` instead of throwing when the\n * input is not valid base64 — callers treat \"not decodable\" as \"not one of\n * our envelopes\" rather than as a hard failure.\n */\nexport function tryFromBase64(value: string): Uint8Array | null {\n  if (typeof value !== \"string\" || value.length === 0) return null;\n\n  // `atob` tolerates some whitespace but not arbitrary characters; reject\n  // anything outside the base64 alphabet up front so the shape check is ours.\n  if (!/^[A-Za-z0-9+/]+={0,2}$/.test(value)) return null;\n\n  let binary: string;\n\n  try {\n    binary = atob(value);\n  } catch {\n    return null;\n  }\n\n  const bytes = new Uint8Array(binary.length);\n\n  for (let index = 0; index < binary.length; index++) {\n    bytes[index] = binary.charCodeAt(index);\n  }\n\n  return bytes;\n}\n","import { concatBytes, toBase64, tryFromBase64 } from \"./crypto-runtime\";\nimport { DecryptionError } from \"./errors\";\n\n/**\n * Envelope version 1 — the only format this package writes.\n *\n * A leading version byte makes the format self-describing: future algorithm\n * changes bump the version (or the suite) and `decrypt` can still recognise,\n * and refuse or handle, every generation of ciphertext it is handed.\n */\nexport const ENVELOPE_VERSION_1 = 0x01;\n\n/**\n * Cipher suite 1 — PBKDF2-HMAC-SHA256 → AES-256-GCM with a 128-bit tag.\n */\nexport const SUITE_PBKDF2_SHA256_AES_256_GCM = 0x01;\n\n/** Random per-message PBKDF2 salt, in bytes. */\nexport const SALT_LENGTH = 16;\n\n/** Random per-message GCM nonce, in bytes. 96 bits is the GCM-native size. */\nexport const IV_LENGTH = 12;\n\n/** GCM authentication tag, in bytes (128 bits). */\nexport const AUTH_TAG_LENGTH = 16;\n\n/** GCM authentication tag, in bits — what WebCrypto's `tagLength` wants. */\nexport const AUTH_TAG_LENGTH_BITS = AUTH_TAG_LENGTH * 8;\n\n/**\n * version(1) + suite(1) + iterations(4, uint32 BE) + salt(16) + iv(12).\n *\n * The whole header is fed to AES-GCM as additional authenticated data, so the\n * declared iteration count, salt and nonce are covered by the auth tag and\n * cannot be edited without the tag check failing.\n */\nexport const HEADER_LENGTH = 2 + 4 + SALT_LENGTH + IV_LENGTH;\n\n/** OWASP-aligned default work factor for PBKDF2-HMAC-SHA256. */\nexport const DEFAULT_ITERATIONS = 210_000;\n\n/** Floor enforced when encrypting. Below this the KDF is not worth its name. */\nexport const MIN_ITERATIONS = 100_000;\n\n/**\n * Ceiling accepted when decrypting.\n *\n * The iteration count is read out of attacker-reachable ciphertext, so an\n * unbounded value would be a trivial CPU-exhaustion vector: a forged envelope\n * claiming 4 billion iterations would pin a core for minutes before the tag\n * check ever ran. Anything above this is rejected before key derivation.\n */\nexport const MAX_ITERATIONS = 5_000_000;\n\nexport type EncryptionEnvelope = {\n  version: number;\n  suite: number;\n  iterations: number;\n  salt: Uint8Array;\n  iv: Uint8Array;\n  /** The raw header bytes, reused verbatim as GCM additional authenticated data. */\n  header: Uint8Array;\n  /** Ciphertext with the GCM tag appended, exactly as WebCrypto returns it. */\n  payload: Uint8Array;\n};\n\n/**\n * Build the 34-byte version-1 header.\n */\nexport function buildHeader(\n  iterations: number,\n  salt: Uint8Array,\n  iv: Uint8Array\n): Uint8Array {\n  const header = new Uint8Array(HEADER_LENGTH);\n\n  header[0] = ENVELOPE_VERSION_1;\n  header[1] = SUITE_PBKDF2_SHA256_AES_256_GCM;\n\n  new DataView(header.buffer).setUint32(2, iterations, false);\n\n  header.set(salt, 6);\n  header.set(iv, 6 + SALT_LENGTH);\n\n  return header;\n}\n\n/**\n * Serialise header + payload into the base64 string handed back to callers.\n */\nexport function encodeEnvelope(\n  header: Uint8Array,\n  payload: Uint8Array\n): string {\n  return toBase64(concatBytes(header, payload));\n}\n\n/**\n * Parse a ciphertext string as a version-1 envelope.\n *\n * Returns `null` when the input is not a version-1 envelope at all (bad\n * base64, or a different leading version byte) — that is the signal to try the\n * legacy path. Throws when the input *claims* to be a version-1 envelope but\n * is truncated, uses an unknown suite, or declares an implausible work factor;\n * those are corrupt/hostile inputs, not old data.\n */\nexport function parseEnvelope(cipher: string): EncryptionEnvelope | null {\n  const bytes = tryFromBase64(cipher);\n\n  if (!bytes || bytes.length === 0) return null;\n  if (bytes[0] !== ENVELOPE_VERSION_1) return null;\n\n  if (bytes.length < HEADER_LENGTH + AUTH_TAG_LENGTH) {\n    throw new DecryptionError(\n      \"Malformed ciphertext: the envelope is shorter than its own header and authentication tag.\"\n    );\n  }\n\n  const suite = bytes[1];\n\n  if (suite !== SUITE_PBKDF2_SHA256_AES_256_GCM) {\n    throw new DecryptionError(\n      `Unsupported cipher suite 0x${suite\n        .toString(16)\n        .padStart(2, \"0\")} in a version 1 envelope; this ciphertext was produced by a newer version of @mongez/encryption.`\n    );\n  }\n\n  const iterations = new DataView(\n    bytes.buffer,\n    bytes.byteOffset,\n    bytes.byteLength\n  ).getUint32(2, false);\n\n  if (iterations < 1 || iterations > MAX_ITERATIONS) {\n    throw new DecryptionError(\n      `Refusing to decrypt: the envelope declares ${iterations} PBKDF2 iterations, outside the accepted range of 1..${MAX_ITERATIONS}.`\n    );\n  }\n\n  return {\n    version: ENVELOPE_VERSION_1,\n    suite,\n    iterations,\n    salt: bytes.subarray(6, 6 + SALT_LENGTH),\n    iv: bytes.subarray(6 + SALT_LENGTH, HEADER_LENGTH),\n    header: bytes.subarray(0, HEADER_LENGTH),\n    payload: bytes.subarray(HEADER_LENGTH),\n  };\n}\n\n/**\n * Whether the given string looks like a version-1 AES-GCM envelope.\n *\n * Useful for migration scripts that walk a store and re-encrypt whatever is\n * still in the legacy format.\n */\nexport function isEncryptionEnvelope(cipher: string): boolean {\n  const bytes = tryFromBase64(cipher);\n\n  return (\n    !!bytes &&\n    bytes.length >= HEADER_LENGTH + AUTH_TAG_LENGTH &&\n    bytes[0] === ENVELOPE_VERSION_1 &&\n    bytes[1] === SUITE_PBKDF2_SHA256_AES_256_GCM\n  );\n}\n","import AES from \"crypto-js/aes\";\nimport { DEFAULT_ITERATIONS, MAX_ITERATIONS, MIN_ITERATIONS } from \"./envelope\";\nimport { EncryptionError } from \"./errors\";\nimport { EncryptionConfigurations } from \"./types\";\n\nconst defaultConfigurations: EncryptionConfigurations = {\n  key: null as any,\n  iterations: DEFAULT_ITERATIONS,\n  legacyDecryption: false,\n  legacyDriver: AES,\n};\n\nlet configurations: EncryptionConfigurations = { ...defaultConfigurations };\n\nlet deprecatedDriverWarned = false;\n\nexport function setEncryptionConfigurations(\n  newConfigurations: EncryptionConfigurations\n) {\n  if (newConfigurations.iterations !== undefined) {\n    assertIterations(newConfigurations.iterations);\n  }\n\n  // v1.x callers configured a pluggable cipher. It can no longer change how we\n  // encrypt — AES-256-GCM is not negotiable — so it is re-pointed at the\n  // legacy decrypt path and the caller is told once.\n  if (newConfigurations.driver !== undefined) {\n    if (!deprecatedDriverWarned) {\n      deprecatedDriverWarned = true;\n      console.warn(\n        \"[@mongez/encryption] `driver` is deprecated: encryption is always AES-256-GCM. The driver you passed will only be used to read legacy (v1.x) ciphertexts.\"\n      );\n    }\n\n    newConfigurations = {\n      legacyDriver: newConfigurations.driver,\n      ...newConfigurations,\n    };\n  }\n\n  configurations = { ...configurations, ...newConfigurations };\n}\n\nexport function getEncryptionConfig(key: keyof EncryptionConfigurations): any {\n  return configurations[key];\n}\n\n/**\n * Reset every configuration value back to its import-time default.\n */\nexport function resetEncryptionConfigurations() {\n  configurations = { ...defaultConfigurations };\n}\n\n/**\n * Reject work factors that would make PBKDF2 decorative.\n */\nexport function assertIterations(iterations: number) {\n  if (\n    !Number.isInteger(iterations) ||\n    iterations < MIN_ITERATIONS ||\n    iterations > MAX_ITERATIONS\n  ) {\n    throw new EncryptionError(\n      `Invalid PBKDF2 iterations: ${iterations}. Expected an integer between ${MIN_ITERATIONS} and ${MAX_ITERATIONS}.`\n    );\n  }\n}\n","import {\n  getSubtle,\n  toBase64,\n  utf8ToBytes,\n  type CryptoKeyLike,\n} from \"./crypto-runtime\";\n\n/**\n * Derived keys are cached in memory, keyed by the exact\n * (password, salt, iterations) triple that produced them.\n *\n * PBKDF2 at the default work factor costs ~100ms per call, and a browser app\n * that reads a dozen encrypted values from storage on boot would otherwise pay\n * it a dozen times. The cache never widens who can decrypt what: a hit\n * requires the same password AND the same salt, and the cached `CryptoKey` is\n * non-extractable, so it is not a more useful thing to hold than the password\n * the caller already keeps in memory.\n */\nconst MAX_CACHED_KEYS = 64;\n\nconst keyCache = new Map<string, Promise<CryptoKeyLike>>();\n\n/**\n * Drop every cached derived key. Call it on logout, or between tests.\n */\nexport function clearKeyCache(): void {\n  keyCache.clear();\n}\n\nfunction cacheKeyFor(\n  password: string,\n  salt: Uint8Array,\n  iterations: number\n): string {\n  return `${iterations}:${toBase64(salt)}:${password}`;\n}\n\n/**\n * Derive a 256-bit AES-GCM key from a passphrase with PBKDF2-HMAC-SHA256.\n *\n * The derived key is non-extractable: it can encrypt and decrypt through\n * WebCrypto but its bytes cannot be read back out by application code.\n */\nexport async function deriveKey(\n  password: string,\n  salt: Uint8Array,\n  iterations: number\n): Promise<CryptoKeyLike> {\n  const cacheKey = cacheKeyFor(password, salt, iterations);\n  const cached = keyCache.get(cacheKey);\n\n  if (cached) return cached;\n\n  const subtle = getSubtle();\n\n  const derivation = (async () => {\n    const baseKey = await subtle.importKey(\n      \"raw\",\n      utf8ToBytes(password),\n      { name: \"PBKDF2\" },\n      false,\n      [\"deriveKey\"]\n    );\n\n    return subtle.deriveKey(\n      { name: \"PBKDF2\", salt, iterations, hash: \"SHA-256\" },\n      baseKey,\n      { name: \"AES-GCM\", length: 256 },\n      false,\n      [\"encrypt\", \"decrypt\"]\n    );\n  })();\n\n  // A failed derivation must not be memoised, otherwise a transient runtime\n  // error would be replayed to every later caller with the same inputs.\n  derivation.catch(() => keyCache.delete(cacheKey));\n\n  if (keyCache.size >= MAX_CACHED_KEYS) {\n    const oldest = keyCache.keys().next();\n    if (!oldest.done) keyCache.delete(oldest.value);\n  }\n\n  keyCache.set(cacheKey, derivation);\n\n  return derivation;\n}\n","import CryptoJS from \"crypto-js\";\nimport AES from \"crypto-js/aes\";\nimport { DecryptionError } from \"./errors\";\nimport { type LegacyCipherDriver } from \"./types\";\n\n/**\n * Base64 prefix of every ciphertext produced by @mongez/encryption v1.x.\n *\n * v1 always called crypto-js in passphrase mode, which emits\n * `\"Salted__\" + 8-byte salt + AES-CBC ciphertext`; the ASCII `Salted__` header\n * base64-encodes to this fixed 10-character prefix. A version-1 AES-GCM\n * envelope starts with byte 0x01 instead, so the two formats can never be\n * confused for one another.\n */\nexport const LEGACY_CIPHER_PREFIX = \"U2FsdGVkX1\";\n\n/**\n * Whether the given string is a v1.x (AES-CBC, MD5-KDF) ciphertext.\n *\n * Exposed so consumers can walk a store and re-encrypt legacy values, which is\n * the only way to get integrity protection over data written by v1.x.\n */\nexport function isLegacyCipher(cipher: string): boolean {\n  return typeof cipher === \"string\" && cipher.startsWith(LEGACY_CIPHER_PREFIX);\n}\n\n/**\n * Whether the given value can act as a crypto-js style cipher driver.\n *\n * Used both to accept a legacy driver in `decrypt`'s third positional slot and\n * to reject one in `encrypt`'s, where v1.x callers used to pass `AES`.\n */\nexport function isLegacyDriver(value: any): value is LegacyCipherDriver {\n  return (\n    !!value &&\n    typeof value === \"object\" &&\n    typeof value.encrypt === \"function\" &&\n    typeof value.decrypt === \"function\"\n  );\n}\n\n/**\n * Decrypt a v1.x ciphertext. **Decrypt only** — nothing in this package writes\n * this format any more.\n *\n * The value returned here is NOT authenticated: v1.x used AES-CBC with no MAC,\n * so a v1.x ciphertext an attacker can write to may have been tampered with\n * undetectably. That is exactly why this path is opt-in.\n */\nexport function legacyDecrypt(\n  cipher: string,\n  key: string,\n  driver: LegacyCipherDriver = AES\n): any {\n  let plainText: string;\n\n  try {\n    plainText = driver.decrypt(cipher, key).toString(CryptoJS.enc.Utf8);\n  } catch {\n    throw new DecryptionError(\n      \"Unable to decrypt the given legacy (v1.x) ciphertext: wrong key, wrong driver, or corrupt data.\"\n    );\n  }\n\n  if (!plainText) {\n    throw new DecryptionError(\n      \"Unable to decrypt the given legacy (v1.x) ciphertext: wrong key, wrong driver, or corrupt data.\"\n    );\n  }\n\n  try {\n    return JSON.parse(plainText).data;\n  } catch {\n    throw new DecryptionError(\n      \"Legacy (v1.x) ciphertext decrypted to something that is not a @mongez/encryption payload.\"\n    );\n  }\n}\n","import CryptoJS from \"crypto-js\";\nimport { assertIterations, getEncryptionConfig } from \"./configurations\";\nimport {\n  bytesToUtf8,\n  getSubtle,\n  randomBytes,\n  utf8ToBytes,\n} from \"./crypto-runtime\";\nimport {\n  AUTH_TAG_LENGTH_BITS,\n  buildHeader,\n  encodeEnvelope,\n  IV_LENGTH,\n  parseEnvelope,\n  SALT_LENGTH,\n} from \"./envelope\";\nimport {\n  DecryptionError,\n  EncryptionError,\n  MissingEncryptionKeyError,\n} from \"./errors\";\nimport { deriveKey } from \"./key-derivation\";\nimport { isLegacyCipher, isLegacyDriver, legacyDecrypt } from \"./legacy\";\nimport { DecryptOptions, EncryptOptions, LegacyCipherDriver } from \"./types\";\n\n/**\n * Return md5 hashed string\n *\n * @deprecated MD5 is collision-broken. Never use it for passwords, signatures\n * or integrity checks — it is exported for legacy interop (cache keys,\n * gravatar-style identifiers) only.\n *\n * @param {string} text\n * @returns {string}\n */\nexport function md5(text: string): string {\n  return CryptoJS.MD5(text).toString();\n}\n\n/**\n * Return sha1 hashed string\n *\n * @deprecated SHA-1 is collision-broken. Never use it for passwords,\n * signatures or integrity checks — it is exported for legacy interop only.\n *\n * @param {string} text\n * @returns {string}\n */\nexport function sha1(text: string): string {\n  return CryptoJS.SHA1(text).toString();\n}\n\n/**\n * Return sha256 hashed string\n *\n * @param {string} text\n * @returns {string}\n */\nexport function sha256(text: string): string {\n  return CryptoJS.SHA256(text).toString();\n}\n\n/**\n * Return sha512 hashed string\n *\n * @param {string} text\n * @returns {string}\n */\nexport function sha512(text: string): string {\n  return CryptoJS.SHA512(text).toString();\n}\n\nfunction assertKey(key: string): asserts key is string {\n  if (!key) {\n    throw new MissingEncryptionKeyError();\n  }\n\n  if (typeof key !== \"string\") {\n    throw new EncryptionError(\n      `The encryption key must be a string, ${typeof key} given.`\n    );\n  }\n}\n\n/**\n * Encrypt the given value with AES-256-GCM.\n *\n * The key is stretched with PBKDF2-HMAC-SHA256 over a fresh random salt, and\n * the payload is sealed under a fresh random 96-bit nonce; both are stored in\n * the returned envelope. Two calls with the same value and key therefore never\n * produce the same string, and any edit to the returned string makes\n * {@link decrypt} reject rather than return altered data.\n *\n * @breaking-change v2 — this is `async`. v1.x returned the ciphertext directly.\n *\n * @param {any} value any JSON-encodable value\n * @param {string} key\n * @param {EncryptOptions} options\n * @returns {Promise<string>} base64 envelope\n */\nexport async function encrypt(\n  value: any,\n  key: string = getEncryptionConfig(\"key\"),\n  options: EncryptOptions = {}\n): Promise<string> {\n  assertKey(key);\n\n  if (isLegacyDriver(options)) {\n    throw new EncryptionError(\n      \"encrypt() no longer takes a cipher driver: v2 always encrypts with AES-256-GCM. Drop the third argument; pass a legacy driver to decrypt() instead if you still need to read v1.x ciphertexts.\"\n    );\n  }\n\n  const iterations: number =\n    options?.iterations ?? getEncryptionConfig(\"iterations\");\n\n  assertIterations(iterations);\n\n  // Serialised before any randomness is drawn, so a circular value throws the\n  // same synchronous-shaped TypeError v1.x threw (surfaced as a rejection).\n  const data = utf8ToBytes(\n    JSON.stringify({\n      data: value,\n    })\n  );\n\n  const subtle = getSubtle();\n  const salt = randomBytes(SALT_LENGTH);\n  const iv = randomBytes(IV_LENGTH);\n  const header = buildHeader(iterations, salt, iv);\n\n  const cryptoKey = await deriveKey(key, salt, iterations);\n\n  const payload = await subtle.encrypt(\n    {\n      name: \"AES-GCM\",\n      iv,\n      // Binds version, suite, iteration count, salt and nonce to the tag —\n      // none of them can be edited in transit without the tag check failing.\n      additionalData: header,\n      tagLength: AUTH_TAG_LENGTH_BITS,\n    },\n    cryptoKey,\n    data\n  );\n\n  return encodeEnvelope(header, new Uint8Array(payload));\n}\n\n/**\n * Decrypt the given ciphertext and return its original value.\n *\n * @breaking-change v2 — this is `async`, and it **throws** a\n * {@link DecryptionError} on a wrong key, a tampered envelope or a malformed\n * input, where v1.x returned `null`. Failing loudly is the entire point of\n * moving to an authenticated cipher: a returned `null` cannot be distinguished\n * from a legitimately encrypted `null`. Use {@link tryDecrypt} for the old\n * null-on-failure shape.\n *\n * @param {string} cypher\n * @param {string} key\n * @param {DecryptOptions|LegacyCipherDriver} options a v1.x cipher driver is\n *        accepted here and read as `{ legacyDriver, legacyDecryption: true }`.\n * @returns {Promise<any>}\n */\nexport async function decrypt(\n  cypher: string,\n  key: string = getEncryptionConfig(\"key\"),\n  options: DecryptOptions | LegacyCipherDriver = {}\n): Promise<any> {\n  assertKey(key);\n\n  const { legacyDecryption, legacyDriver } = normalizeDecryptOptions(options);\n\n  if (typeof cypher !== \"string\" || cypher.length === 0) {\n    throw new DecryptionError(\n      \"Unable to decrypt: the given ciphertext is empty or not a string.\"\n    );\n  }\n\n  const envelope = parseEnvelope(cypher);\n\n  if (!envelope) {\n    return decryptLegacy(cypher, key, legacyDecryption, legacyDriver);\n  }\n\n  const cryptoKey = await deriveKey(key, envelope.salt, envelope.iterations);\n\n  let plainBytes: ArrayBuffer;\n\n  try {\n    plainBytes = await getSubtle().decrypt(\n      {\n        name: \"AES-GCM\",\n        iv: envelope.iv,\n        additionalData: envelope.header,\n        tagLength: AUTH_TAG_LENGTH_BITS,\n      },\n      cryptoKey,\n      envelope.payload\n    );\n  } catch {\n    // Deliberately one message for both \"wrong key\" and \"tampered\": the two\n    // are indistinguishable to GCM, and keeping them indistinguishable here\n    // denies an attacker an oracle.\n    throw new DecryptionError(\n      \"Authentication failed: the ciphertext was modified, or the key is wrong.\"\n    );\n  }\n\n  try {\n    return JSON.parse(bytesToUtf8(new Uint8Array(plainBytes))).data;\n  } catch {\n    // Authenticated, so this is our own data — just not a payload this\n    // version knows how to read.\n    throw new DecryptionError(\n      \"Decryption succeeded but the payload is not a @mongez/encryption envelope body.\"\n    );\n  }\n}\n\n/**\n * Decrypt, returning `null` instead of throwing when the ciphertext cannot be\n * authenticated.\n *\n * This is the v1.x failure shape, for callers that genuinely do not care why a\n * value failed to decrypt. Note the ambiguity it carries: `encrypt(null)`\n * round-trips to `null` too, so `null` here means \"no usable value\", not\n * \"failure\". Prefer {@link decrypt}.\n *\n * @param {string} cypher\n * @param {string} key\n * @param {DecryptOptions|LegacyCipherDriver} options\n * @returns {Promise<any|null>}\n */\nexport async function tryDecrypt(\n  cypher: string,\n  key: string = getEncryptionConfig(\"key\"),\n  options: DecryptOptions | LegacyCipherDriver = {}\n): Promise<any> {\n  try {\n    return await decrypt(cypher, key, options);\n  } catch (error) {\n    // A missing key or an unusable runtime is a programming/deployment fault,\n    // not a bad ciphertext — those still throw.\n    if (error instanceof DecryptionError) return null;\n    throw error;\n  }\n}\n\nfunction normalizeDecryptOptions(options: DecryptOptions | LegacyCipherDriver) {\n  if (isLegacyDriver(options)) {\n    return { legacyDecryption: true, legacyDriver: options };\n  }\n\n  const { legacyDecryption, legacyDriver } = (options ??\n    {}) as DecryptOptions;\n\n  return {\n    legacyDecryption: legacyDecryption ?? !!legacyDriver,\n    legacyDriver,\n  };\n}\n\nfunction decryptLegacy(\n  cypher: string,\n  key: string,\n  legacyDecryption: boolean,\n  legacyDriver?: LegacyCipherDriver\n): any {\n  if (!isLegacyCipher(cypher)) {\n    throw new DecryptionError(\n      \"Unrecognised ciphertext: not an AES-GCM envelope, and not a legacy (v1.x) ciphertext either.\"\n    );\n  }\n\n  const enabled =\n    legacyDecryption || getEncryptionConfig(\"legacyDecryption\") === true;\n\n  if (!enabled) {\n    throw new DecryptionError(\n      \"This is a legacy (v1.x) ciphertext, which is unauthenticated and therefore rejected by default. Enable it while migrating with setEncryptionConfigurations({ legacyDecryption: true }), then re-encrypt the value.\"\n    );\n  }\n\n  return legacyDecrypt(\n    cypher,\n    key,\n    legacyDriver ?? getEncryptionConfig(\"legacyDriver\")\n  );\n}\n"],"mappings":"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"}