function insertError(str) { const ring = document.querySelector('.lds-ring'); ring.insertAdjacentHTML('afterend', `

Error: ${str}

`); ring.remove(); } function finishRedirect() { window.location=location.search.slice(1)+location.hash || "/"; } const wasmSupported = (() => { try { if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") { const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)); if (module instanceof WebAssembly.Module) return new WebAssembly.Instance(module) instanceof WebAssembly.Instance; } } catch (e) { } return false; })(); function postResponse(powResponse, captchaResponse) { const body = { 'pow_response': powResponse, }; if (captchaResponse) { body['h-captcha-response'] = captchaResponse; body['g-recaptcha-response'] = captchaResponse; } fetch('/bot-check', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams(body), redirect: 'manual', }).then(res => { const s = res.status; if (s >= 400 && s < 500) { return insertError('bad challenge response request.'); } else if (s >= 500) { return insertError('server responded with error.'); } finishRedirect(); }).catch(err => { insertError('failed to send challenge response.'); }); } const powFinished = new Promise((resolve, reject) => { window.addEventListener('DOMContentLoaded', async () => { if (!wasmSupported) { return insertError('browser does not support WebAssembly.'); } const { time, kb, pow, diff } = document.querySelector('[data-pow]').dataset; const argonOpts = { time: time, mem: kb, hashLen: 32, parallelism: 1, type: argon2.ArgonType.Argon2id, }; console.log('Got pow', pow, 'with difficulty', diff); const diffString = '0'.repeat(Math.floor(diff/8)); const combined = pow; const [userkey, challenge, signature] = combined.split("#"); const start = Date.now(); if (window.Worker) { const threads = Math.min(8,Math.ceil(window.navigator.hardwareConcurrency/2)); let finished = false; const messageHandler = (e) => { if (finished) { return; } finished = true; workers.forEach(w => w.terminate()); const [workerId, answer] = e.data; console.log('Worker', workerId, 'returned answer', answer, 'in', Date.now()-start+'ms'); const dummyTime = 5000 - (Date.now()-start); window.setTimeout(() => { resolve(`${combined}#${answer}`); }, dummyTime); } const workers = []; for (let i = 0; i < threads; i++) { const argonWorker = new Worker('/js/worker.js'); argonWorker.onmessage = messageHandler; workers.push(argonWorker); } for (let i = 0; i < threads; i++) { await new Promise(res => setTimeout(res, 100)); workers[i].postMessage([userkey, challenge, diff, diffString, argonOpts, i, threads]); } } else { console.warn('No webworker support, running in main/UI thread!'); let i = 0; let start = Date.now(); while(true) { const hash = await argon2.hash({ pass: challenge + i.toString(), salt: userkey, ...argonOpts, }); if (hash.hashHex.startsWith(diffString) && ((parseInt(hash.hashHex[diffString.length],16) & 0xff >> (((diffString.length+1)*8)-diff)) === 0)) { console.log('Main thread found solution:', hash.hashHex, 'in', (Date.now()-start)+'ms'); break; } ++i; } const dummyTime = 5000 - (Date.now()-start); window.setTimeout(() => { resolve(`${combined}#${i}`); }, dummyTime); } }); }).then((powResponse) => { const hasCaptchaForm = document.getElementById('captcha'); if (!hasCaptchaForm) { postResponse(powResponse); } return powResponse; }); function onCaptchaSubmit(captchaResponse) { const captchaElem = document.querySelector('[data-sitekey]'); captchaElem.insertAdjacentHTML('afterend', `
`); captchaElem.remove(); powFinished.then((powResponse) => { postResponse(powResponse, captchaResponse); }); }