{"version":3,"file":"index.mjs","names":[],"sources":["../../src/crypto/index.ts"],"sourcesContent":["import type { SecretConfig } from \"@better-auth/core\";\nimport { getWebcryptoSubtle } from \"@better-auth/utils\";\nimport { createHash } from \"@better-auth/utils/hash\";\nimport { xchacha20poly1305 } from \"@noble/ciphers/chacha.js\";\nimport {\n\tbytesToHex,\n\thexToBytes,\n\tmanagedNonce,\n\tutf8ToBytes,\n} from \"@noble/ciphers/utils.js\";\n\nexport type { SecretConfig };\n\nconst algorithm = { name: \"HMAC\", hash: \"SHA-256\" };\n\nconst ENVELOPE_PREFIX = \"$ba$\";\n\nexport function parseEnvelope(\n\tdata: string,\n): { version: number; ciphertext: string } | null {\n\tif (!data.startsWith(ENVELOPE_PREFIX)) return null;\n\tconst firstSep = ENVELOPE_PREFIX.length;\n\tconst secondSep = data.indexOf(\"$\", firstSep);\n\tif (secondSep === -1) return null;\n\tconst version = parseInt(data.slice(firstSep, secondSep), 10);\n\tif (!Number.isInteger(version) || version < 0) return null;\n\tconst ciphertext = data.slice(secondSep + 1);\n\treturn { version, ciphertext };\n}\n\nexport function formatEnvelope(version: number, ciphertext: string): string {\n\treturn `${ENVELOPE_PREFIX}${version}$${ciphertext}`;\n}\n\nexport type SymmetricEncryptOptions = {\n\tkey: string | SecretConfig;\n\tdata: string;\n};\n\nasync function rawEncrypt(secret: string, data: string): Promise<string> {\n\tconst keyAsBytes = await createHash(\"SHA-256\").digest(secret);\n\tconst dataAsBytes = utf8ToBytes(data);\n\tconst chacha = managedNonce(xchacha20poly1305)(new Uint8Array(keyAsBytes));\n\treturn bytesToHex(chacha.encrypt(dataAsBytes));\n}\n\nasync function rawDecrypt(secret: string, hex: string): Promise<string> {\n\tconst keyAsBytes = await createHash(\"SHA-256\").digest(secret);\n\tconst dataAsBytes = hexToBytes(hex);\n\tconst chacha = managedNonce(xchacha20poly1305)(new Uint8Array(keyAsBytes));\n\treturn new TextDecoder().decode(chacha.decrypt(dataAsBytes));\n}\n\nexport const symmetricEncrypt = async ({\n\tkey,\n\tdata,\n}: SymmetricEncryptOptions) => {\n\tif (typeof key === \"string\") {\n\t\treturn rawEncrypt(key, data);\n\t}\n\tconst secret = key.keys.get(key.currentVersion);\n\tif (!secret) {\n\t\tthrow new Error(`Secret version ${key.currentVersion} not found in keys`);\n\t}\n\tconst ciphertext = await rawEncrypt(secret, data);\n\treturn formatEnvelope(key.currentVersion, ciphertext);\n};\n\nexport type SymmetricDecryptOptions = {\n\tkey: string | SecretConfig;\n\tdata: string;\n};\n\nexport const symmetricDecrypt = async ({\n\tkey,\n\tdata,\n}: SymmetricDecryptOptions) => {\n\tif (typeof key === \"string\") {\n\t\treturn rawDecrypt(key, data);\n\t}\n\tconst envelope = parseEnvelope(data);\n\tif (envelope) {\n\t\tconst secret = key.keys.get(envelope.version);\n\t\tif (!secret) {\n\t\t\tthrow new Error(\n\t\t\t\t`Secret version ${envelope.version} not found in keys (key may have been retired)`,\n\t\t\t);\n\t\t}\n\t\treturn rawDecrypt(secret, envelope.ciphertext);\n\t}\n\t// Legacy bare-hex payload\n\tif (key.legacySecret) {\n\t\treturn rawDecrypt(key.legacySecret, data);\n\t}\n\tthrow new Error(\n\t\t\"Cannot decrypt legacy bare-hex payload: no legacy secret available. Set BETTER_AUTH_SECRET for backwards compatibility.\",\n\t);\n};\n\nexport const getCryptoKey = async (secret: string | BufferSource) => {\n\tconst secretBuf =\n\t\ttypeof secret === \"string\" ? new TextEncoder().encode(secret) : secret;\n\treturn await getWebcryptoSubtle().importKey(\n\t\t\"raw\",\n\t\tsecretBuf,\n\t\talgorithm,\n\t\tfalse,\n\t\t[\"sign\", \"verify\"],\n\t);\n};\n\nexport const makeSignature = async (\n\tvalue: string,\n\tsecret: string | BufferSource,\n): Promise<string> => {\n\tconst key = await getCryptoKey(secret);\n\tconst signature = await getWebcryptoSubtle().sign(\n\t\talgorithm.name,\n\t\tkey,\n\t\tnew TextEncoder().encode(value),\n\t);\n\t// the returned base64 encoded signature will always be 44 characters long and end with one or two equal signs\n\treturn btoa(String.fromCharCode(...new Uint8Array(signature)));\n};\n\nexport * from \"./buffer\";\nexport * from \"./jwt\";\nexport * from \"./password\";\nexport * from \"./random\";\n"],"mappings":";;;;;;;;;;AAaA,MAAM,YAAY;CAAE,MAAM;CAAQ,MAAM;CAAW;AAEnD,MAAM,kBAAkB;AAExB,SAAgB,cACf,MACiD;AACjD,KAAI,CAAC,KAAK,WAAW,gBAAgB,CAAE,QAAO;CAC9C,MAAM,WAAW;CACjB,MAAM,YAAY,KAAK,QAAQ,KAAK,SAAS;AAC7C,KAAI,cAAc,GAAI,QAAO;CAC7B,MAAM,UAAU,SAAS,KAAK,MAAM,UAAU,UAAU,EAAE,GAAG;AAC7D,KAAI,CAAC,OAAO,UAAU,QAAQ,IAAI,UAAU,EAAG,QAAO;AAEtD,QAAO;EAAE;EAAS,YADC,KAAK,MAAM,YAAY,EAAE;EACd;;AAG/B,SAAgB,eAAe,SAAiB,YAA4B;AAC3E,QAAO,GAAG,kBAAkB,QAAQ,GAAG;;AAQxC,eAAe,WAAW,QAAgB,MAA+B;CACxE,MAAM,aAAa,MAAM,WAAW,UAAU,CAAC,OAAO,OAAO;CAC7D,MAAM,cAAc,YAAY,KAAK;AAErC,QAAO,WADQ,aAAa,kBAAkB,CAAC,IAAI,WAAW,WAAW,CAAC,CACjD,QAAQ,YAAY,CAAC;;AAG/C,eAAe,WAAW,QAAgB,KAA8B;CACvE,MAAM,aAAa,MAAM,WAAW,UAAU,CAAC,OAAO,OAAO;CAC7D,MAAM,cAAc,WAAW,IAAI;CACnC,MAAM,SAAS,aAAa,kBAAkB,CAAC,IAAI,WAAW,WAAW,CAAC;AAC1E,QAAO,IAAI,aAAa,CAAC,OAAO,OAAO,QAAQ,YAAY,CAAC;;AAG7D,MAAa,mBAAmB,OAAO,EACtC,KACA,WAC8B;AAC9B,KAAI,OAAO,QAAQ,SAClB,QAAO,WAAW,KAAK,KAAK;CAE7B,MAAM,SAAS,IAAI,KAAK,IAAI,IAAI,eAAe;AAC/C,KAAI,CAAC,OACJ,OAAM,IAAI,MAAM,kBAAkB,IAAI,eAAe,oBAAoB;CAE1E,MAAM,aAAa,MAAM,WAAW,QAAQ,KAAK;AACjD,QAAO,eAAe,IAAI,gBAAgB,WAAW;;AAQtD,MAAa,mBAAmB,OAAO,EACtC,KACA,WAC8B;AAC9B,KAAI,OAAO,QAAQ,SAClB,QAAO,WAAW,KAAK,KAAK;CAE7B,MAAM,WAAW,cAAc,KAAK;AACpC,KAAI,UAAU;EACb,MAAM,SAAS,IAAI,KAAK,IAAI,SAAS,QAAQ;AAC7C,MAAI,CAAC,OACJ,OAAM,IAAI,MACT,kBAAkB,SAAS,QAAQ,gDACnC;AAEF,SAAO,WAAW,QAAQ,SAAS,WAAW;;AAG/C,KAAI,IAAI,aACP,QAAO,WAAW,IAAI,cAAc,KAAK;AAE1C,OAAM,IAAI,MACT,0HACA;;AAGF,MAAa,eAAe,OAAO,WAAkC;CACpE,MAAM,YACL,OAAO,WAAW,WAAW,IAAI,aAAa,CAAC,OAAO,OAAO,GAAG;AACjE,QAAO,MAAM,oBAAoB,CAAC,UACjC,OACA,WACA,WACA,OACA,CAAC,QAAQ,SAAS,CAClB;;AAGF,MAAa,gBAAgB,OAC5B,OACA,WACqB;CACrB,MAAM,MAAM,MAAM,aAAa,OAAO;CACtC,MAAM,YAAY,MAAM,oBAAoB,CAAC,KAC5C,UAAU,MACV,KACA,IAAI,aAAa,CAAC,OAAO,MAAM,CAC/B;AAED,QAAO,KAAK,OAAO,aAAa,GAAG,IAAI,WAAW,UAAU,CAAC,CAAC"}