{"version":3,"file":"static/js/630.89d9f30d.chunk.js","mappings":"mBACO,MAAMA,EAAU,IAAIC,YAOpB,SAASC,EAAOC,GACnB,MAAO,IAAI,IAAIC,WAAWD,IACrBE,KAAKC,GAAMA,EAAEC,SAAS,IAAIC,SAAS,EAAG,OACtCC,KAAK,GACd,CAQOC,eAAeC,EAAKC,EAAWC,GAClC,OAAOC,OAAOC,OAAOC,OAAOJ,EAAUK,cAA+B,kBAATJ,EAAoBb,EAAQkB,OAAOL,GAAQ,IAAIT,WAAWS,GAC1H,CAQOH,eAAeS,EAAQP,EAAWC,GACrC,OAAOX,QAAaS,EAAKC,EAAWC,GACxC,CCwHO,SAASO,EAAeC,EAAWC,GAAmD,IAA7CV,EAASW,UAAAC,OAAA,QAAAC,IAAAF,UAAA,GAAAA,UAAA,GAAG,UAAWG,EAAGH,UAAAC,OAAA,QAAAC,IAAAF,UAAA,GAAAA,UAAA,GAAG,IAAKI,EAAKJ,UAAAC,OAAA,QAAAC,IAAAF,UAAA,GAAAA,UAAA,GAAG,EACtF,MAAMK,EAAa,IAAIC,gBACjBC,EAAYC,KAAKC,MAgBvB,MAAO,CACHC,QAhBOvB,WACP,IAAK,IAAIwB,EAAIP,EAAOO,GAAKR,EAAKQ,GAAK,EAAG,CAClC,GAAIN,EAAWO,OAAOC,QAClB,OAAO,KAGX,SADgBjB,EAAQP,EAAWU,EAAOY,KAChCb,EACN,MAAO,CACHgB,OAAQH,EACRI,KAAMP,KAAKC,MAAQF,EAG/B,CACA,OAAO,IAAI,EAGFS,GACTX,aAER,CC3KA,IAAIA,EAEJY,UAAY9B,UAER,MAAM,KAAE+B,EAAI,QAAEC,GAAYC,EAAQ9B,KAEX,IAAD+B,EAAtB,GAAa,UAATH,EAEU,QAAVG,EAAAhB,SAAU,IAAAgB,GAAVA,EAAYC,QAEZjB,OAAaH,OAIZ,GAAa,SAATgB,EAAiB,CAEtB,MAAM,UAAE7B,EAAS,UAAES,EAAS,IAAEK,EAAG,KAAEJ,EAAI,MAAEK,GAAUe,GAAW,CAAC,EAEzDI,EAAS1B,EAAeC,EAAWC,EAAMV,EAAWc,EAAKC,GAE/DC,EAAakB,EAAOlB,WAEpBkB,EAAOb,QAAQc,MAAMC,IAGrBC,YAAYD,EAAW,IAAKA,EAAUE,QAAQ,GAASF,EAAS,GAIpE,E","sources":["../node_modules/altcha-lib/dist/helpers.js","../node_modules/altcha-lib/dist/index.js","shared/Altcha/ChallengeWorker.js"],"sourcesContent":["// Create a TextEncoder instance to convert strings to UTF-8 byte arrays.\nexport const encoder = new TextEncoder();\n/**\n * Converts an ArrayBuffer or Uint8Array to a hexadecimal string.\n *\n * @param ab - The ArrayBuffer or Uint8Array to convert.\n * @returns The hexadecimal string representation of the input.\n */\nexport function ab2hex(ab) {\n return [...new Uint8Array(ab)]\n .map((x) => x.toString(16).padStart(2, '0'))\n .join('');\n}\n/**\n * Generates a cryptographic hash using the specified algorithm.\n *\n * @param algorithm - The cryptographic hash algorithm to use (e.g., 'SHA-256').\n * @param data - The data to hash, either as a string or ArrayBuffer.\n * @returns A Promise that resolves to the computed hash as an ArrayBuffer.\n */\nexport async function hash(algorithm, data) {\n return crypto.subtle.digest(algorithm.toUpperCase(), typeof data === 'string' ? encoder.encode(data) : new Uint8Array(data));\n}\n/**\n * Generates a cryptographic hash using the specified algorithm and returns it as a hexadecimal string.\n *\n * @param algorithm - The cryptographic hash algorithm to use (e.g., 'SHA-256').\n * @param data - The data to hash, either as a string or ArrayBuffer.\n * @returns A Promise that resolves to the computed hash as a hexadecimal string.\n */\nexport async function hashHex(algorithm, data) {\n return ab2hex(await hash(algorithm, data));\n}\n/**\n * Generates an HMAC using the specified algorithm and secret key.\n *\n * @param algorithm - The cryptographic hash algorithm to use for HMAC (e.g., 'SHA-256').\n * @param data - The data to sign, either as a string or ArrayBuffer.\n * @param secret - The secret key to use for HMAC.\n * @returns A Promise that resolves to the computed HMAC as an ArrayBuffer.\n */\nexport async function hmac(algorithm, data, secret) {\n const key = await crypto.subtle.importKey('raw', encoder.encode(secret), {\n name: 'HMAC',\n hash: algorithm,\n }, false, ['sign', 'verify']);\n return crypto.subtle.sign('HMAC', key, typeof data === 'string' ? encoder.encode(data) : new Uint8Array(data));\n}\n/**\n * Generates an HMAC using the specified algorithm and secret key, and returns it as a hexadecimal string.\n *\n * @param algorithm - The cryptographic hash algorithm to use for HMAC (e.g., 'SHA-256').\n * @param data - The data to sign, either as a string or ArrayBuffer.\n * @param secret - The secret key to use for HMAC.\n * @returns A Promise that resolves to the computed HMAC as a hexadecimal string.\n */\nexport async function hmacHex(algorithm, data, secret) {\n return ab2hex(await hmac(algorithm, data, secret));\n}\n/**\n * Generates a random sequence of bytes of the specified length.\n *\n * @param length - The number of random bytes to generate.\n * @returns A Uint8Array containing the random bytes.\n */\nexport function randomBytes(length) {\n const ab = new Uint8Array(length);\n crypto.getRandomValues(ab);\n return ab;\n}\n/**\n * Generates a random integer between 1 and the specified maximum value (inclusive).\n *\n * @param max - The maximum value for the random integer.\n * @returns A random integer between 1 and the specified max value.\n */\nexport function randomInt(max) {\n const ab = new Uint32Array(1);\n crypto.getRandomValues(ab);\n const randomNumber = ab[0] / (0xffffffff + 1);\n return Math.floor(randomNumber * max + 1);\n}\n","import { ab2hex, hash, hashHex, hmacHex, randomBytes, randomInt, } from './helpers.js';\nconst DEFAULT_MAX_NUMBER = 1e6;\nconst DEFAULT_SALT_LEN = 12;\nconst DEFAULT_ALG = 'SHA-256';\n/**\n * Creates a challenge for the client to solve.\n *\n * @param {ChallengeOptions} options - Options for creating the challenge.\n * @returns {Promise<Challenge>} The created challenge.\n */\nexport async function createChallenge(options) {\n const algorithm = options.algorithm || DEFAULT_ALG;\n const maxnumber = options.maxnumber || options.maxNumber || DEFAULT_MAX_NUMBER;\n const saltLength = options.saltLength || DEFAULT_SALT_LEN;\n const params = new URLSearchParams(options.params);\n if (options.expires) {\n params.set('expires', String(Math.floor(options.expires.getTime() / 1000)));\n }\n let salt = options.salt || ab2hex(randomBytes(saltLength));\n // params.size doesn't work with Node 16\n if (Object.keys(Object.fromEntries(params)).length) {\n salt = salt + '?' + params.toString();\n }\n const number = options.number === undefined ? randomInt(maxnumber) : options.number;\n const challenge = await hashHex(algorithm, salt + number);\n return {\n algorithm,\n challenge,\n maxnumber,\n salt,\n signature: await hmacHex(algorithm, challenge, options.hmacKey),\n };\n}\n/**\n * Extracts parameters from the payload.\n *\n * @param {string | Payload | Challenge} payload - The payload from which to extract parameters.\n * @returns {Record<string, string>} The extracted parameters.\n */\nexport function extractParams(payload) {\n if (typeof payload === 'string') {\n payload = JSON.parse(atob(payload));\n }\n return Object.fromEntries(new URLSearchParams(payload?.salt?.split('?')?.[1] || ''));\n}\n/**\n * Verifies the solution provided by the client.\n *\n * @param {string | Payload} payload - The payload to verify.\n * @param {string} hmacKey - The HMAC key used for verification.\n * @param {boolean} [checkExpires=true] - Whether to check if the challenge has expired.\n * @returns {Promise<boolean>} Whether the solution is valid.\n */\nexport async function verifySolution(payload, hmacKey, checkExpires = true) {\n if (typeof payload === 'string') {\n try {\n payload = JSON.parse(atob(payload));\n }\n catch {\n return false;\n }\n }\n const params = extractParams(payload);\n const expires = params.expires || params.expire;\n if (checkExpires && expires) {\n const date = new Date(parseInt(expires, 10) * 1000);\n if (Number.isNaN(date.getTime()) || date.getTime() < Date.now()) {\n return false;\n }\n }\n const check = await createChallenge({\n algorithm: payload.algorithm,\n hmacKey,\n number: payload.number,\n salt: payload.salt,\n });\n return (check.challenge === payload.challenge &&\n check.signature === payload.signature);\n}\n/**\n * Verifies the hash of form fields.\n *\n * @param {FormData | Record<string, unknown>} formData - The form data to verify.\n * @param {string[]} fields - The fields to include in the hash.\n * @param {string} fieldsHash - The expected hash of the fields.\n * @param {string} [algorithm=DEFAULT_ALG] - The hash algorithm to use.\n * @returns {Promise<boolean>} Whether the fields hash is valid.\n */\nexport async function verifyFieldsHash(formData, fields, fieldsHash, algorithm = DEFAULT_ALG) {\n const data = formData instanceof FormData ? Object.fromEntries(formData) : formData;\n const lines = [];\n for (const field of fields) {\n lines.push(String(data[field] || ''));\n }\n return (await hashHex(algorithm, lines.join('\\n'))) === fieldsHash;\n}\n/**\n * Verifies the server's signature.\n *\n * @param {string | ServerSignaturePayload} payload - The payload to verify.\n * @param {string} hmacKey - The HMAC key used for verification.\n * @returns {Promise<{verificationData: ServerSignatureVerificationData | null, verified: boolean}>} The verification result.\n */\nexport async function verifyServerSignature(payload, hmacKey) {\n if (typeof payload === 'string') {\n try {\n payload = JSON.parse(atob(payload));\n }\n catch {\n return {\n verificationData: null,\n verified: false,\n };\n }\n }\n const signature = await hmacHex(payload.algorithm, await hash(payload.algorithm, payload.verificationData), hmacKey);\n let verificationData = null;\n try {\n const params = new URLSearchParams(payload.verificationData);\n verificationData = {\n ...Object.fromEntries(params),\n expire: parseInt(params.get('expire') || '0', 10),\n fields: params.get('fields')?.split(','),\n reasons: params.get('reasons')?.split(','),\n score: params.get('score')\n ? parseFloat(params.get('score') || '0')\n : undefined,\n time: parseInt(params.get('time') || '0', 10),\n verified: params.get('verified') === 'true',\n };\n }\n catch {\n // noop\n }\n return {\n verificationData,\n verified: payload.verified === true &&\n verificationData?.verified === true &&\n verificationData.expire > Math.floor(Date.now() / 1000) &&\n payload.signature === signature,\n };\n}\n/**\n * Solves a challenge by brute force.\n *\n * @param {string} challenge - The challenge to solve.\n * @param {string} salt - The salt used in the challenge.\n * @param {string} [algorithm='SHA-256'] - The hash algorithm used.\n * @param {number} [max=1e6] - The maximum number to try.\n * @param {number} [start=0] - The starting number.\n * @returns {{promise: Promise<Solution | null>, controller: AbortController}} The solution promise and abort controller.\n */\nexport function solveChallenge(challenge, salt, algorithm = 'SHA-256', max = 1e6, start = 0) {\n const controller = new AbortController();\n const startTime = Date.now();\n const fn = async () => {\n for (let n = start; n <= max; n += 1) {\n if (controller.signal.aborted) {\n return null;\n }\n const t = await hashHex(algorithm, salt + n);\n if (t === challenge) {\n return {\n number: n,\n took: Date.now() - startTime,\n };\n }\n }\n return null;\n };\n return {\n promise: fn(),\n controller,\n };\n}\n/**\n * Solves a challenge using web workers for parallel computation.\n *\n * @param {string | URL | (() => Worker)} workerScript - The worker script or function to create a worker.\n * @param {number} concurrency - The number of workers to use.\n * @param {string} challenge - The challenge to solve.\n * @param {string} salt - The salt used in the challenge.\n * @param {string} [algorithm='SHA-256'] - The hash algorithm used.\n * @param {number} [max=1e6] - The maximum number to try.\n * @param {number} [startNumber=0] - The starting number.\n * @returns {Promise<Solution | null>} The solution, or null if not found.\n */\nexport async function solveChallengeWorkers(workerScript, concurrency, challenge, salt, algorithm = 'SHA-256', max = 1e6, startNumber = 0) {\n const workers = [];\n concurrency = Math.max(1, Math.min(16, concurrency));\n for (let i = 0; i < concurrency; i++) {\n if (typeof workerScript === 'function') {\n workers.push(workerScript());\n }\n else {\n workers.push(new Worker(workerScript, {\n type: 'module',\n }));\n }\n }\n const step = Math.ceil(max / concurrency);\n const solutions = await Promise.all(workers.map((worker, i) => {\n const start = startNumber + i * step;\n return new Promise((resolve) => {\n worker.addEventListener('message', (message) => {\n if (message.data) {\n for (const w of workers) {\n if (w !== worker) {\n w.postMessage({ type: 'abort' });\n }\n }\n }\n resolve(message.data);\n });\n worker.postMessage({\n payload: {\n algorithm,\n challenge,\n max: start + step,\n salt,\n start,\n },\n type: 'work',\n });\n });\n }));\n for (const worker of workers) {\n worker.terminate();\n }\n return solutions.find((solution) => !!solution) || null;\n}\nexport default {\n createChallenge,\n extractParams,\n solveChallenge,\n solveChallengeWorkers,\n verifyFieldsHash,\n verifyServerSignature,\n verifySolution,\n};\n","import { solveChallenge } from \"altcha-lib\";\r\n\r\n\r\nlet controller = undefined;\r\n\r\nonmessage = async (message) => {\r\n\r\n const { type, payload } = message.data;\r\n\r\n if (type === 'abort') {\r\n\r\n controller?.abort();\r\n\r\n controller = undefined;\r\n\r\n }\r\n\r\n else if (type === 'work') {\r\n\r\n const { algorithm, challenge, max, salt, start } = payload || {};\r\n\r\n const result = solveChallenge(challenge, salt, algorithm, max, start);\r\n\r\n controller = result.controller;\r\n\r\n result.promise.then((solution) => {\r\n\r\n \r\n postMessage(solution ? { ...solution, worker: true } : solution);\r\n\r\n });\r\n\r\n }\r\n\r\n};\r\n "],"names":["encoder","TextEncoder","ab2hex","ab","Uint8Array","map","x","toString","padStart","join","async","hash","algorithm","data","crypto","subtle","digest","toUpperCase","encode","hashHex","solveChallenge","challenge","salt","arguments","length","undefined","max","start","controller","AbortController","startTime","Date","now","promise","n","signal","aborted","number","took","fn","onmessage","type","payload","message","_controller","abort","result","then","solution","postMessage","worker"],"sourceRoot":""}