UNPKG

friendly-challenge

Version:

The client code used for FriendlyCaptcha (widget script, html, styling and webworker solvers)

463 lines (452 loc) 23.5 kB
(function () { 'use strict'; // Adapted from the base64-arraybuffer package implementation // (https://github.com/niklasvh/base64-arraybuffer, MIT licensed) const CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const EQ_CHAR = "=".charCodeAt(0); // Use a lookup table to find the index. const lookup = new Uint8Array(256); for (let i = 0; i < CHARS.length; i++) { lookup[CHARS.charCodeAt(i)] = i; } function decode(base64) { const len = base64.length; let bufferLength = (len * 3) >>> 2; // * 0.75 if (base64.charCodeAt(len - 1) === EQ_CHAR) bufferLength--; if (base64.charCodeAt(len - 2) === EQ_CHAR) bufferLength--; const bytes = new Uint8Array(bufferLength); for (let i = 0, p = 0; i < len; i += 4) { const encoded1 = lookup[base64.charCodeAt(i + 0)]; const encoded2 = lookup[base64.charCodeAt(i + 1)]; const encoded3 = lookup[base64.charCodeAt(i + 2)]; const encoded4 = lookup[base64.charCodeAt(i + 3)]; bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); } return bytes; } // WARNING: This file was autogenerated by wasmwrap and should not be edited manually. const base64 = "AGFzbQEAAAABKghgAABgAn9/AGADf39/AX9gAX8AYAR/f39/AGAAAX9gAX8Bf2ACf38BfwINAQNlbnYFYWJvcnQABAMMCwcGAwAAAQIFAQIABQMBAAEGFgR/AUEAC38BQQALfwBBAwt/AEHgDAsHbgkGbWVtb3J5AgAHX19hbGxvYwABCF9fcmV0YWluAAIJX19yZWxlYXNlAAMJX19jb2xsZWN0AAQHX19yZXNldAAFC19fcnR0aV9iYXNlAwMNVWludDhBcnJheV9JRAMCDHNvbHZlQmxha2UyYgAKCAELCvQSC5IBAQV/IABB8P///wNLBEAACyMBQRBqIgQgAEEPakFwcSICQRAgAkEQSxsiBmoiAj8AIgVBEHQiA0sEQCAFIAIgA2tB//8DakGAgHxxQRB2IgMgBSADShtAAEEASARAIANAAEEASARAAAsLCyACJAEgBEEQayICIAY2AgAgAkEBNgIEIAIgATYCCCACIAA2AgwgBAsEACAACwMAAQsDAAELBgAjACQBC7sCAQF/AkAgAUUNACAAQQA6AAAgACABakEEayICQQA6AAMgAUECTQ0AIABBADoAASAAQQA6AAIgAkEAOgACIAJBADoAASABQQZNDQAgAEEAOgADIAJBADoAACABQQhNDQAgAEEAIABrQQNxIgJqIgBBADYCACAAIAEgAmtBfHEiAmpBHGsiAUEANgIYIAJBCE0NACAAQQA2AgQgAEEANgIIIAFBADYCECABQQA2AhQgAkEYTQ0AIABBADYCDCAAQQA2AhAgAEEANgIUIABBADYCGCABQQA2AgAgAUEANgIEIAFBADYCCCABQQA2AgwgACAAQQRxQRhqIgFqIQAgAiABayEBA0AgAUEgTwRAIABCADcDACAAQgA3AwggAEIANwMQIABCADcDGCABQSBrIQEgAEEgaiEADAELCwsLcgACfyAARQRAQQxBAhABIQALIAALQQA2AgAgAEEANgIEIABBADYCCCABQfD///8DIAJ2SwRAQcAKQfAKQRJBORAAAAsgASACdCIBQQAQASICIAEQBiAAKAIAGiAAIAI2AgAgACACNgIEIAAgATYCCCAAC88BAQJ/QaABQQAQASIAQQxBAxABQYABQQAQBzYCACAAQQxBBBABQQhBAxAHNgIEIABCADcDCCAAQQA2AhAgAEIANwMYIABC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"; // This is a hand-pruned version of the assemblyscript loader, removing // a lot of functionality we don't need, saving in bundle size. function addUtilityExports(instance) { const extendedExports = {}; const exports = instance.exports; const memory = exports.memory; const alloc = exports["__alloc"]; const retain = exports["__retain"]; const rttiBase = exports["__rtti_base"] || ~0; // oob if not present /** Gets the runtime type info for the given id. */ function getInfo(id) { const U32 = new Uint32Array(memory.buffer); // const count = U32[rttiBase >>> 2]; // if ((id >>>= 0) >= count) throw Error("invalid id: " + id); return U32[((rttiBase + 4) >>> 2) + id * 2]; } /** Allocates a new array in the module's memory and returns its retained pointer. */ extendedExports.__allocArray = (id, values) => { const info = getInfo(id); const align = 31 - Math.clz32((info >>> 6) & 31); const length = values.length; const buf = alloc(length << align, 0); const arr = alloc(12, id); const U32 = new Uint32Array(memory.buffer); U32[(arr + 0) >>> 2] = retain(buf); U32[(arr + 4) >>> 2] = buf; U32[(arr + 8) >>> 2] = length << align; const buffer = memory.buffer; const view = new Uint8Array(buffer); if (info & (1 << 14)) { for (let i = 0; i < length; ++i) view[(buf >>> align) + i] = retain(values[i]); } else { view.set(values, buf >>> align); } return arr; }; extendedExports.__getUint8Array = (ptr) => { const U32 = new Uint32Array(memory.buffer); const bufPtr = U32[(ptr + 4) >>> 2]; return new Uint8Array(memory.buffer, bufPtr, U32[(bufPtr - 4) >>> 2] >>> 0); }; return demangle(exports, extendedExports); } /** Demangles an AssemblyScript module's exports to a friendly object structure. */ function demangle(exports, extendedExports = {}) { // extendedExports = Object.create(extendedExports); const setArgumentsLength = exports["__argumentsLength"] ? (length) => { exports["__argumentsLength"].value = length; } : exports["__setArgumentsLength"] || exports["__setargc"] || (() => { return {}; }); for (const internalName in exports) { if (!Object.prototype.hasOwnProperty.call(exports, internalName)) continue; const elem = exports[internalName]; // Only necessary if nested exports are present // let parts = internalName.split("."); // let curr = extendedExports; // while (parts.length > 1) { // let part = parts.shift(); // if (!Object.prototype.hasOwnProperty.call(curr, part as any)) curr[part as any] = {}; // curr = curr[part as any]; // } const name = internalName.split(".")[0]; if (typeof elem === "function" && elem !== setArgumentsLength) { (extendedExports[name] = (...args) => { setArgumentsLength(args.length); return elem(...args); }).original = elem; } else { extendedExports[name] = elem; } } return extendedExports; } async function instantiateWasmSolver(module) { const imports = { env: { abort() { throw Error("Wasm aborted"); }, }, }; const result = await WebAssembly.instantiate(module, imports); const exports = addUtilityExports(result); return { exports }; } async function getWasmSolver(module) { const w = await instantiateWasmSolver(module); const arrPtr = w.exports.__retain(w.exports.__allocArray(w.exports.Uint8Array_ID, new Uint8Array(128))); let solution = w.exports.__getUint8Array(arrPtr); return (puzzleBuffer, threshold, n = 4294967295) => { solution.set(puzzleBuffer); const hashPtr = w.exports.solveBlake2b(arrPtr, threshold, n); solution = w.exports.__getUint8Array(arrPtr); const hash = w.exports.__getUint8Array(hashPtr); w.exports.__release(hashPtr); return [solution, hash]; }; } // Blake2B made assemblyscript compatible, adapted from (CC0 licensed): // Blake2B in pure Javascript // Adapted from the reference implementation in RFC7693 // Ported to Javascript by DC - https://github.com/dcposch class Context { constructor(outlen) { this.b = new Uint8Array(128); this.h = new Uint32Array(16); this.t = 0; // input count this.c = 0; // pointer within buffer this.v = new Uint32Array(32); this.m = new Uint32Array(32); this.outlen = outlen; } } // Little-endian byte access function B2B_GET32(arr, i) { return (arr[i] ^ (arr[i + 1] << 8) ^ (arr[i + 2] << 16) ^ (arr[i + 3] << 24)); } // G Mixing function with everything inlined // performance at the cost of readability, especially faster in old browsers function B2B_G_FAST(v, m, a, b, c, d, ix, iy) { const x0 = m[ix]; const x1 = m[ix + 1]; const y0 = m[iy]; const y1 = m[iy + 1]; // va0 are the low bits, va1 are the high bits let va0 = v[a]; let va1 = v[a + 1]; let vb0 = v[b]; let vb1 = v[b + 1]; let vc0 = v[c]; let vc1 = v[c + 1]; let vd0 = v[d]; let vd1 = v[d + 1]; let w0, ww, xor0, xor1; // ADD64AA(v, a, b); // v[a,a+1] += v[b,b+1] ... in JS we must store a uint64 as two uint32s // ADD64AA(v,a,b) w0 = va0 + vb0; ww = ((va0 & vb0) | ((va0 | vb0) & ~w0)) >>> 31; va0 = w0; va1 = (va1 + vb1 + ww); // // ADD64AC(v, a, x0, x1); // v[a, a+1] += x ... x0 is the low 32 bits of x, x1 is the high 32 bits w0 = va0 + x0; ww = ((va0 & x0) | ((va0 | x0) & ~w0)) >>> 31; va0 = w0; va1 = (va1 + x1 + ww); // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated to the right by 32 bits xor0 = vd0 ^ va0; xor1 = vd1 ^ va1; // We can just swap high and low here becaeuse its a shift by 32 bits vd0 = xor1; vd1 = xor0; // ADD64AA(v, c, d); w0 = vc0 + vd0; ww = ((vc0 & vd0) | ((vc0 | vd0) & ~w0)) >>> 31; vc0 = w0; vc1 = (vc1 + vd1 + ww); // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 24 bits xor0 = vb0 ^ vc0; xor1 = vb1 ^ vc1; vb0 = (xor0 >>> 24) ^ (xor1 << 8); vb1 = (xor1 >>> 24) ^ (xor0 << 8); // ADD64AA(v, a, b); w0 = va0 + vb0; ww = ((va0 & vb0) | ((va0 | vb0) & ~w0)) >>> 31; va0 = w0; va1 = (va1 + vb1 + ww); // ADD64AC(v, a, y0, y1); w0 = va0 + y0; ww = ((va0 & y0) | ((va0 | y0) & ~w0)) >>> 31; va0 = w0; va1 = (va1 + y1 + ww); // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated right by 16 bits xor0 = vd0 ^ va0; xor1 = vd1 ^ va1; vd0 = (xor0 >>> 16) ^ (xor1 << 16); vd1 = (xor1 >>> 16) ^ (xor0 << 16); // ADD64AA(v, c, d); w0 = vc0 + vd0; ww = ((vc0 & vd0) | ((vc0 | vd0) & ~w0)) >>> 31; vc0 = w0; vc1 = (vc1 + vd1 + ww); // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 63 bits xor0 = vb0 ^ vc0; xor1 = vb1 ^ vc1; vb0 = ((xor1 >>> 31) ^ (xor0 << 1)); vb1 = ((xor0 >>> 31) ^ (xor1 << 1)); v[a] = va0; v[a + 1] = va1; v[b] = vb0; v[b + 1] = vb1; v[c] = vc0; v[c + 1] = vc1; v[d] = vd0; v[d + 1] = vd1; } // Initialization Vector const BLAKE2B_IV32 = [ 0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a, 0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19, ]; // Note these offsets have all been multiplied by two to make them offsets into // a uint32 buffer. const SIGMA82 = [ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 28, 20, 8, 16, 18, 30, 26, 12, 2, 24, 0, 4, 22, 14, 10, 6, 22, 16, 24, 0, 10, 4, 30, 26, 20, 28, 6, 12, 14, 2, 18, 8, 14, 18, 6, 2, 26, 24, 22, 28, 4, 12, 10, 20, 8, 0, 30, 16, 18, 0, 10, 14, 4, 8, 20, 30, 28, 2, 22, 24, 12, 16, 6, 26, 4, 24, 12, 20, 0, 22, 16, 6, 8, 26, 14, 10, 30, 28, 2, 18, 24, 10, 2, 30, 28, 26, 8, 20, 0, 14, 12, 6, 18, 4, 16, 22, 26, 22, 14, 28, 24, 2, 6, 18, 10, 0, 30, 8, 16, 12, 4, 20, 12, 30, 28, 18, 22, 6, 0, 16, 24, 4, 26, 14, 2, 8, 20, 10, 20, 4, 16, 8, 14, 12, 2, 10, 30, 22, 18, 28, 6, 24, 26, 0, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 28, 20, 8, 16, 18, 30, 26, 12, 2, 24, 0, 4, 22, 14, 10, 6, ]; // Compression function. 'last' flag indicates last block. function blake2bCompress(ctx, last) { const v = ctx.v; const m = ctx.m; // init work variables for (let i = 0; i < 16; i++) { v[i] = ctx.h[i]; v[i + 16] = BLAKE2B_IV32[i]; } // low 64 bits of offset v[24] = (v[24] ^ ctx.t); v[25] = (v[25] ^ (ctx.t / 0x100000000)); // high 64 bits not supported, offset may not be higher than 2**53-1 // last block flag set ? if (last) { v[28] = ~v[28]; v[29] = ~v[29]; } // get little-endian words for (let i = 0; i < 32; i++) { m[i] = B2B_GET32(ctx.b, 4 * i); } // twelve rounds of mixing for (let i = 0; i < 12; i++) { B2B_G_FAST(v, m, 0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1]); B2B_G_FAST(v, m, 2, 10, 18, 26, SIGMA82[i * 16 + 2], SIGMA82[i * 16 + 3]); B2B_G_FAST(v, m, 4, 12, 20, 28, SIGMA82[i * 16 + 4], SIGMA82[i * 16 + 5]); B2B_G_FAST(v, m, 6, 14, 22, 30, SIGMA82[i * 16 + 6], SIGMA82[i * 16 + 7]); B2B_G_FAST(v, m, 0, 10, 20, 30, SIGMA82[i * 16 + 8], SIGMA82[i * 16 + 9]); B2B_G_FAST(v, m, 2, 12, 22, 24, SIGMA82[i * 16 + 10], SIGMA82[i * 16 + 11]); B2B_G_FAST(v, m, 4, 14, 16, 26, SIGMA82[i * 16 + 12], SIGMA82[i * 16 + 13]); B2B_G_FAST(v, m, 6, 8, 18, 28, SIGMA82[i * 16 + 14], SIGMA82[i * 16 + 15]); } for (let i = 0; i < 16; i++) { ctx.h[i] = ctx.h[i] ^ v[i] ^ v[i + 16]; } } /** * FRIENDLY CAPTCHA optimization only, does not reset ctx.t (global byte counter) * Assumes no key */ function blake2bResetForShortMessage(ctx, input) { // Initialize State vector h with IV for (let i = 0; i < 16; i++) { ctx.h[i] = BLAKE2B_IV32[i]; } // Danger: These operations and resetting are really only possible because our input is exactly 128 bytes ctx.b.set(input); // ctx.m.fill(0); // ctx.v.fill(0); ctx.h[0] ^= 0x01010000 ^ ctx.outlen; } // This is not an enum to save some bytes in the output bundle. const SOLVER_TYPE_JS = 1; const SOLVER_TYPE_WASM = 2; const CHALLENGE_SIZE_BYTES = 128; const HASH_SIZE_BYTES = 32; /** * Solve the blake2b hashing problem, re-using the memory between different attempts (which solves up to 50% faster). * * This only changes the last 4 bytes of the input array to find a solution. To find multiple solutions * one could call this function multiple times with the 4 bytes in front of those last 4 bytes varying. * * * The goal is to find a nonce that, hashed together with the rest of the input header, has a value of its * most significant 32bits that is below some threshold. * Approximately this means: the hash value of it starts with K zeroes (little endian), which is expected to be * increasingly difficult as K increases. * * In practice you should ask the client to solve multiple (easier) puzzles which should reduce variance and also allows us * to show a progress bar. * @param input challenge bytes * @param threshold u32 value under which the solution's hash should be below. */ function solveBlake2bEfficient(input, threshold, n) { if (input.length != CHALLENGE_SIZE_BYTES) { throw Error("Invalid input"); } const buf = input.buffer; const view = new DataView(buf); const ctx = new Context(HASH_SIZE_BYTES); ctx.t = CHALLENGE_SIZE_BYTES; const start = view.getUint32(124, true); const end = start + n; for (let i = start; i < end; i++) { view.setUint32(124, i, true); blake2bResetForShortMessage(ctx, input); blake2bCompress(ctx, true); if (ctx.h[0] < threshold) { if (ASC_TARGET == 0) { // JS return new Uint8Array(ctx.h.buffer); } //@ts-ignore return Uint8Array.wrap(ctx.h.buffer); } } return new Uint8Array(0); } async function getJSSolver() { return (puzzleBuffer, threshold, n = 4294967295) => { const hash = solveBlake2bEfficient(puzzleBuffer, threshold, n); return [puzzleBuffer, hash]; }; } if (!Uint8Array.prototype.slice) { Object.defineProperty(Uint8Array.prototype, "slice", { value: function (begin, end) { return new Uint8Array(Array.prototype.slice.call(this, begin, end)); }, }); } self.ASC_TARGET = 0; // 1 for JS, 2 for WASM let solverType; // Puzzle consisting of zeroes let setSolver; const solver = new Promise((resolve) => (setSolver = resolve)); self.onerror = (evt) => { self.postMessage({ type: "error", message: JSON.stringify(evt), }); }; self.onmessage = async (evt) => { const data = evt.data; try { /** * Compile the WASM and setup the solver. * If WASM support is not present, it uses the JS version instead. */ if (data.type === "solver") { if (data.forceJS) { solverType = SOLVER_TYPE_JS; const s = await getJSSolver(); setSolver(s); } else { try { solverType = SOLVER_TYPE_WASM; const module = WebAssembly.compile(decode(base64)); const s = await getWasmSolver(await module); setSolver(s); } catch (e) { console.log("FriendlyCaptcha failed to initialize WebAssembly, falling back to Javascript solver: " + e.toString()); solverType = SOLVER_TYPE_JS; const s = await getJSSolver(); setSolver(s); } } self.postMessage({ type: "ready", solver: solverType, }); } else if (data.type === "start") { const solve = await solver; self.postMessage({ type: "started", }); let totalH = 0; let solution; // We loop over a uint32 to find as solution, it is technically possible (but extremely unlikely - only possible with very high difficulty) that // there is no solution, here we loop over one byte further up too in case that happens. for (let b = 0; b < 256; b++) { data.puzzleSolverInput[123] = b; const [s, hash] = solve(data.puzzleSolverInput, data.threshold); if (hash.length === 0) { // This means 2^32 puzzles were evaluated, which takes a while in a browser! // As we try 256 times, this is not fatal console.warn("FC: Internal error or no solution found"); totalH += Math.pow(2, 32) - 1; continue; } solution = s; break; } const view = new DataView(solution.slice(-4).buffer); totalH += view.getUint32(0, true); self.postMessage({ type: "done", solution: solution.slice(-8), h: totalH, puzzleIndex: data.puzzleIndex, puzzleNumber: data.puzzleNumber, }); } } catch (e) { setTimeout(() => { throw e; }); } }; })();