{"version":3,"file":"xaes.cjs","names":["ensureUint8Array"],"sources":["../../../src/common/crypto/xaes.ts"],"sourcesContent":["// Original at https://github.com/dchest/xaes MIT License as of 2024-07-02\n// More at https://words.filippo.io/dispatches/xaes-256-gcm/\n\nimport { ensureUint8Array } from '../data/bin'\n\n/**\n * Implementation of XAES-256-GCM as defined in https://c2sp.org/XAES-256-GCM\n * based on the Web Cryptography API (https://www.w3.org/TR/WebCryptoAPI/).\n *\n * It uses a 256-bit AES-CBC CryptoKey and a 192-bit nonce to derive\n * a 256-bit key and a 96-bit nonce for AES-GCM.\n *\n * Due to the use of the standard CryptoKey and Web Cryptography API operations,\n * this implementation is fully compatible with other parts of the Web Cryptography API.\n * For example, keys can be stored in IndexedDB and be non-extractable. The only\n * additional requirement is that the key must have 'encrypt' usage even for decryption,\n * however, there's no real distinction between encryption and decryption operations\n * for AES-GCM anyway (you can simulate decryption by encrypting the ciphertext).\n */\n\n/**\n * Generates an AES block using the given key and data xored with the given xor.\n */\nasync function aesBlock(key: CryptoKey, data: BufferSource, xor: BufferSource): Promise<Uint8Array> {\n  const block = await crypto.subtle.encrypt(\n    { name: 'AES-CBC', iv: xor },\n    key,\n    data,\n  )\n  return new Uint8Array(block, 0, 16)\n}\n\n/**\n * Derives a half key.\n */\nasync function halfKey(index: number, key: CryptoKey, iv: Uint8Array, k1: Uint8Array): Promise<Uint8Array> {\n  const m = new Uint8Array(16)\n  m[1] = index\n  m[2] = 0x58 // 'X'\n  m.set(ensureUint8Array(iv.subarray(0, 12)), 4)\n  return await aesBlock(key, m, ensureUint8Array(k1))\n}\n\n/**\n * Derives a 256-bit key and 96-bit nonce from the given 256-bit key and a 192-bit nonce.\n */\nasync function deriveKeyNonce(key: CryptoKey, iv: BufferSource): Promise<{ key: CryptoKey, iv: Uint8Array }> {\n  if (key.algorithm.name !== 'AES-CBC') {\n    throw new Error('key must be for AES-CBC')\n  }\n  if (!key.usages.includes('encrypt')) {\n    throw new Error('key must have \\'encrypt\\' usage')\n  }\n  // @ts-expect-error todo\n  if (key.algorithm.length !== 256) {\n    throw new Error('key must be 256 bits')\n  }\n  if (iv == null || iv.byteLength !== 24) {\n    throw new Error('iv must be 24 bytes')\n  }\n  const ivBytes = ArrayBuffer.isView(iv)\n    ? new Uint8Array(iv.buffer, iv.byteOffset, iv.byteLength)\n    : new Uint8Array(iv)\n  const k1 = await aesBlock(key, new Uint8Array(16), new Uint8Array(16))\n\n  let msb = 0\n  for (let i = k1.length - 1; i >= 0; i--) {\n    [msb, k1[i]] = [(k1[i] >>> 7) & 0xFF, ((k1[i] << 1) | msb) & 0xFF]\n  }\n  k1[k1.length - 1] ^= (msb * 0b10000111) & 0xFF\n\n  const kxBytes = new Uint8Array(32)\n  kxBytes.set(await halfKey(0x01, key, ivBytes, k1), 0)\n  kxBytes.set(await halfKey(0x02, key, ivBytes, k1), 16)\n\n  const kx = await crypto.subtle.importKey(\n    'raw',\n    kxBytes,\n    { name: 'AES-GCM', length: 256 },\n    false,\n    [...key.usages],\n  )\n  return {\n    key: kx,\n    iv: ensureUint8Array(ivBytes.subarray(12)),\n  }\n}\n/**\n * Encrypts data using XAES-256-GCM with the given key and iv.\n * Key must be a 256-bit AES-CBC CryptoKey with 'encrypt' usage.\n */\nexport async function encryptXAES(params: {\n  iv: BufferSource\n  additionalData?: BufferSource\n}, key: CryptoKey, data: BufferSource): Promise<ArrayBuffer> {\n  const derived = await deriveKeyNonce(key, params.iv)\n  return await crypto.subtle.encrypt(\n    {\n      name: 'AES-GCM',\n      iv: derived.iv as BufferSource,\n      tagLength: 128,\n      additionalData: params.additionalData ?? new Uint8Array(),\n    },\n    derived.key,\n    data,\n  )\n}\n\n/**\n * Decrypts data using XAES-256-GCM with the given key and iv.\n * Key must be a 256-bit AES-CBC CryptoKey with 'encrypt' and 'decrypt' usages.\n */\nexport async function decryptXAES(params: {\n  iv: BufferSource\n  additionalData?: BufferSource\n}, key: CryptoKey, data: BufferSource): Promise<ArrayBuffer> {\n  const derived = await deriveKeyNonce(key, params.iv)\n  return await crypto.subtle.decrypt(\n    {\n      name: 'AES-GCM',\n      iv: derived.iv as BufferSource,\n      tagLength: 128,\n      additionalData: params.additionalData ?? new Uint8Array(),\n    },\n    derived.key,\n    data,\n  )\n}\n\n/**\n * Generate a random key suitable for XAES-256-GCM.\n * The actual key is an AES-CBC CryptoKey with 256-bit length.\n *\n * This function is not necessary, as you can use crypto.subtle.generateKey with AES-CBC directly.\n */\nexport async function generateKeyXAES(extractable?: boolean): Promise<CryptoKey> {\n  return await crypto.subtle.generateKey(\n    {\n      name: 'AES-CBC',\n      // @ts-expect-error todo\n      length: 256,\n    },\n    extractable,\n    ['encrypt', 'decrypt'],\n  )\n}\n\n/**\n * Import a key suitable for XAES-256-GCM.\n * The actual key must be an AES-CBC CryptoKey with 256-bit length.\n *\n * This function is not necessary, as you can use crypto.subtle.importKey with AES-CBC directly.\n */\nexport async function importKeyXAES(format: 'jwk' | 'raw' | 'pkcs8' | 'spki', keyData: BufferSource | JsonWebKey, extractable?: boolean): Promise<CryptoKey> {\n  return await crypto.subtle.importKey( // @ts-expect-error-next-line\n    format,\n    keyData,\n    { name: 'AES-CBC', length: 256 },\n    extractable,\n    ['encrypt', 'decrypt'],\n  )\n}\n\n/**\n * Export a key.\n * The resulting export will have AES-CBC algorithm specified.\n *\n * This function is not necessary, as you can use crypto.subtle.exportKey directly.\n */\nexport async function exportKeyXAES(format: 'jwk' | 'pkcs8' | 'raw' | 'spki', key: CryptoKey): Promise<ArrayBuffer | JsonWebKey> {\n  return await crypto.subtle.exportKey(format, key)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAuBA,eAAe,SAAS,KAAgB,MAAoB,KAAwC;CAClG,MAAM,QAAQ,MAAM,OAAO,OAAO,QAChC;EAAE,MAAM;EAAW,IAAI;EAAK,EAC5B,KACA,KACD;AACD,QAAO,IAAI,WAAW,OAAO,GAAG,GAAG;;;;;AAMrC,eAAe,QAAQ,OAAe,KAAgB,IAAgB,IAAqC;CACzG,MAAM,IAAI,IAAI,WAAW,GAAG;AAC5B,GAAE,KAAK;AACP,GAAE,KAAK;AACP,GAAE,IAAIA,6BAAiB,GAAG,SAAS,GAAG,GAAG,CAAC,EAAE,EAAE;AAC9C,QAAO,MAAM,SAAS,KAAK,GAAGA,6BAAiB,GAAG,CAAC;;;;;AAMrD,eAAe,eAAe,KAAgB,IAA+D;AAC3G,KAAI,IAAI,UAAU,SAAS,UACzB,OAAM,IAAI,MAAM,0BAA0B;AAE5C,KAAI,CAAC,IAAI,OAAO,SAAS,UAAU,CACjC,OAAM,IAAI,MAAM,gCAAkC;AAGpD,KAAI,IAAI,UAAU,WAAW,IAC3B,OAAM,IAAI,MAAM,uBAAuB;AAEzC,KAAI,MAAM,QAAQ,GAAG,eAAe,GAClC,OAAM,IAAI,MAAM,sBAAsB;CAExC,MAAM,UAAU,YAAY,OAAO,GAAG,GAClC,IAAI,WAAW,GAAG,QAAQ,GAAG,YAAY,GAAG,WAAW,GACvD,IAAI,WAAW,GAAG;CACtB,MAAM,KAAK,MAAM,SAAS,KAAK,IAAI,WAAW,GAAG,EAAE,IAAI,WAAW,GAAG,CAAC;CAEtE,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,SAAS,GAAG,KAAK,GAAG,IAClC,EAAC,KAAK,GAAG,MAAM,CAAE,GAAG,OAAO,IAAK,MAAQ,GAAG,MAAM,IAAK,OAAO,IAAK;AAEpE,IAAG,GAAG,SAAS,MAAO,MAAM,MAAc;CAE1C,MAAM,UAAU,IAAI,WAAW,GAAG;AAClC,SAAQ,IAAI,MAAM,QAAQ,GAAM,KAAK,SAAS,GAAG,EAAE,EAAE;AACrD,SAAQ,IAAI,MAAM,QAAQ,GAAM,KAAK,SAAS,GAAG,EAAE,GAAG;AAStD,QAAO;EACL,KARS,MAAM,OAAO,OAAO,UAC7B,OACA,SACA;GAAE,MAAM;GAAW,QAAQ;GAAK,EAChC,OACA,CAAC,GAAG,IAAI,OAAO,CAChB;EAGC,IAAIA,6BAAiB,QAAQ,SAAS,GAAG,CAAC;EAC3C;;;;;;AAMH,eAAsB,YAAY,QAG/B,KAAgB,MAA0C;CAC3D,MAAM,UAAU,MAAM,eAAe,KAAK,OAAO,GAAG;AACpD,QAAO,MAAM,OAAO,OAAO,QACzB;EACE,MAAM;EACN,IAAI,QAAQ;EACZ,WAAW;EACX,gBAAgB,OAAO,kBAAkB,IAAI,YAAY;EAC1D,EACD,QAAQ,KACR,KACD;;;;;;AAOH,eAAsB,YAAY,QAG/B,KAAgB,MAA0C;CAC3D,MAAM,UAAU,MAAM,eAAe,KAAK,OAAO,GAAG;AACpD,QAAO,MAAM,OAAO,OAAO,QACzB;EACE,MAAM;EACN,IAAI,QAAQ;EACZ,WAAW;EACX,gBAAgB,OAAO,kBAAkB,IAAI,YAAY;EAC1D,EACD,QAAQ,KACR,KACD;;;;;;;;AASH,eAAsB,gBAAgB,aAA2C;AAC/E,QAAO,MAAM,OAAO,OAAO,YACzB;EACE,MAAM;EAEN,QAAQ;EACT,EACD,aACA,CAAC,WAAW,UAAU,CACvB;;;;;;;;AASH,eAAsB,cAAc,QAA0C,SAAoC,aAA2C;AAC3J,QAAO,MAAM,OAAO,OAAO,UACzB,QACA,SACA;EAAE,MAAM;EAAW,QAAQ;EAAK,EAChC,aACA,CAAC,WAAW,UAAU,CACvB;;;;;;;;AASH,eAAsB,cAAc,QAA0C,KAAmD;AAC/H,QAAO,MAAM,OAAO,OAAO,UAAU,QAAQ,IAAI"}