{"version":3,"file":"rn-helpers.mjs","names":["header: VaultHeader"],"sources":["../src/rn-helpers.ts"],"sourcesContent":["/**\n * React Native vault helpers (S3 suite, passphrase-based).\n *\n * Standalone counterpart to VaultEncryptionService.encryptWithPassphrase /\n * decryptWithPassphrase: produces the same wire format but does NOT depend\n * on tsyringe DI or the Credo agent. Suitable for direct call from a mobile\n * BackupService.\n *\n * The byte-equivalence with the server's VaultEncryptionService is verified\n * by tests/integration/cross-runtime.test.ts.\n */\n// Platform-conditional crypto entry: resolves to providers/node on Node,\n// providers/rn on React Native via package.json `exports` + the consumer\n// bundler's import conditions. Same surface either way, so this file is\n// safe to ship from both index.ts and index.rn.ts.\nimport {\n  aesGcmEncrypt,\n  aesGcmDecrypt,\n  argon2id,\n  canonicalAad,\n  fromBase64Url,\n  generateNonceAesGcm,\n  generateUuid,\n  keyCommitment,\n  randomBytes,\n  toBase64Url,\n  verifyKeyCommitment,\n  zeroize,\n} from './crypto'\nimport type { VaultDocument } from './models/VaultDocument'\nimport type { VaultHeader } from './models/VaultHeader'\n\nexport interface EncryptWithPassphraseRnOptions {\n  docId?: string\n  vaultId?: string\n  epoch?: number\n  /** Argon2id memory cost in KiB. Default 65536 (64 MiB). Matches server. */\n  memory?: number\n  /** Argon2id iterations. Default 3. Matches server. */\n  iterations?: number\n}\n\nexport async function encryptWithPassphraseRn(\n  plaintext: Uint8Array,\n  passphrase: string,\n  options: EncryptWithPassphraseRnOptions = {}\n): Promise<VaultDocument> {\n  const salt = randomBytes(32)\n  const argon2Params = {\n    memory: options.memory ?? 65536,\n    iterations: options.iterations ?? 3,\n    parallelism: 1,\n  }\n\n  const cek = await argon2id({\n    password: passphrase,\n    salt,\n    parallelism: argon2Params.parallelism,\n    iterations: argon2Params.iterations,\n    memorySize: argon2Params.memory,\n    hashLength: 32,\n    outputType: 'binary',\n  })\n\n  const nonce = generateNonceAesGcm()\n  const docId = options.docId ?? generateUuid()\n  const vaultId = options.vaultId ?? docId\n  const epoch = options.epoch ?? 1\n\n  const aadFields = {\n    v: 1,\n    suite: 'S3',\n    aead: 'AES-256-GCM',\n    docId,\n    vaultId,\n    epoch,\n    policy: { mode: 'passphrase' as const },\n  }\n  const aad = canonicalAad(aadFields)\n\n  const { ciphertext } = aesGcmEncrypt(cek, nonce, plaintext, aad)\n  const kcmp = keyCommitment(cek)\n\n  const header: VaultHeader = {\n    v: 1,\n    suite: 'S3',\n    aead: 'AES-256-GCM',\n    docId,\n    vaultId,\n    epoch,\n    nonce: toBase64Url(nonce),\n    kcmp: toBase64Url(kcmp),\n    salt: toBase64Url(salt),\n    argon2: argon2Params,\n    policy: { mode: 'passphrase' },\n  }\n\n  zeroize(cek)\n  return { header, ciphertext }\n}\n\nexport async function decryptWithPassphraseRn(\n  vault: VaultDocument,\n  passphrase: string\n): Promise<Uint8Array> {\n  const { header, ciphertext } = vault\n\n  if (header.suite !== 'S3' && header.suite !== 'S1') {\n    throw new Error(`Expected S1/S3 suite for passphrase vault, got ${header.suite}`)\n  }\n  if (!header.salt || !header.argon2) {\n    throw new Error('Missing salt or argon2 params')\n  }\n\n  const salt = fromBase64Url(header.salt)\n  const cek = await argon2id({\n    password: passphrase,\n    salt,\n    parallelism: header.argon2.parallelism ?? 1,\n    iterations: header.argon2.iterations,\n    memorySize: header.argon2.memory,\n    hashLength: 32,\n    outputType: 'binary',\n  })\n\n  const expectedKcmp = fromBase64Url(header.kcmp)\n  if (!verifyKeyCommitment(cek, expectedKcmp)) {\n    zeroize(cek)\n    throw new Error('Key commitment verification failed — wrong passphrase')\n  }\n\n  const aad = canonicalAad({\n    v: header.v,\n    suite: header.suite,\n    aead: header.aead,\n    docId: header.docId,\n    vaultId: header.vaultId,\n    epoch: header.epoch,\n    policy: { mode: header.policy?.mode ?? 'passphrase' },\n  })\n\n  const nonce = fromBase64Url(header.nonce)\n  try {\n    const plaintext = aesGcmDecrypt(cek, nonce, ciphertext, aad)\n    zeroize(cek)\n    return plaintext\n  } catch (e) {\n    zeroize(cek)\n    throw new Error('AEAD decryption failed — data may be corrupted or tampered')\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;AA0CA,eAAsB,wBACpB,WACA,YACA,UAA0C,EAAE,EACpB;CACxB,MAAM,OAAO,YAAY,GAAG;CAC5B,MAAM,eAAe;EACnB,QAAQ,QAAQ,UAAU;EAC1B,YAAY,QAAQ,cAAc;EAClC,aAAa;EACd;CAED,MAAM,MAAM,MAAM,SAAS;EACzB,UAAU;EACV;EACA,aAAa,aAAa;EAC1B,YAAY,aAAa;EACzB,YAAY,aAAa;EACzB,YAAY;EACZ,YAAY;EACb,CAAC;CAEF,MAAM,QAAQ,qBAAqB;CACnC,MAAM,QAAQ,QAAQ,SAAS,cAAc;CAC7C,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,QAAQ,QAAQ,SAAS;CAa/B,MAAM,EAAE,eAAe,cAAc,KAAK,OAAO,WAFrC,aATM;EAChB,GAAG;EACH,OAAO;EACP,MAAM;EACN;EACA;EACA;EACA,QAAQ,EAAE,MAAM,cAAuB;EACxC,CACkC,CAE6B;CAChE,MAAM,OAAO,cAAc,IAAI;CAE/B,MAAMA,SAAsB;EAC1B,GAAG;EACH,OAAO;EACP,MAAM;EACN;EACA;EACA;EACA,OAAO,YAAY,MAAM;EACzB,MAAM,YAAY,KAAK;EACvB,MAAM,YAAY,KAAK;EACvB,QAAQ;EACR,QAAQ,EAAE,MAAM,cAAc;EAC/B;AAED,SAAQ,IAAI;AACZ,QAAO;EAAE;EAAQ;EAAY;;AAG/B,eAAsB,wBACpB,OACA,YACqB;CACrB,MAAM,EAAE,QAAQ,eAAe;AAE/B,KAAI,OAAO,UAAU,QAAQ,OAAO,UAAU,KAC5C,OAAM,IAAI,MAAM,kDAAkD,OAAO,QAAQ;AAEnF,KAAI,CAAC,OAAO,QAAQ,CAAC,OAAO,OAC1B,OAAM,IAAI,MAAM,gCAAgC;CAIlD,MAAM,MAAM,MAAM,SAAS;EACzB,UAAU;EACV,MAHW,cAAc,OAAO,KAAK;EAIrC,aAAa,OAAO,OAAO,eAAe;EAC1C,YAAY,OAAO,OAAO;EAC1B,YAAY,OAAO,OAAO;EAC1B,YAAY;EACZ,YAAY;EACb,CAAC;AAGF,KAAI,CAAC,oBAAoB,KADJ,cAAc,OAAO,KAAK,CACJ,EAAE;AAC3C,UAAQ,IAAI;AACZ,QAAM,IAAI,MAAM,wDAAwD;;CAG1E,MAAM,MAAM,aAAa;EACvB,GAAG,OAAO;EACV,OAAO,OAAO;EACd,MAAM,OAAO;EACb,OAAO,OAAO;EACd,SAAS,OAAO;EAChB,OAAO,OAAO;EACd,QAAQ,EAAE,MAAM,OAAO,QAAQ,QAAQ,cAAc;EACtD,CAAC;CAEF,MAAM,QAAQ,cAAc,OAAO,MAAM;AACzC,KAAI;EACF,MAAM,YAAY,cAAc,KAAK,OAAO,YAAY,IAAI;AAC5D,UAAQ,IAAI;AACZ,SAAO;UACA,GAAG;AACV,UAAQ,IAAI;AACZ,QAAM,IAAI,MAAM,6DAA6D"}