UNPKG

browser-canvas-fingerprinting

Version:

A simple canvas fingerprinting implementation in browser with specific information used to generate fingerprint

625 lines (613 loc) 21.8 kB
// src/sha256.js async function asha256(arrayBuffer) { return Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", arrayBuffer))).map((b) => b.toString(16).padStart(2, "0")).join(""); } async function sha256(input) { return await asha256(new TextEncoder().encode(input)); } async function bsha256(blob) { return await asha256(await blob.arrayBuffer()); } // src/font.js function getFontsByCanvasMeasure() { const fonts = [ "Arial", "Arial Black", "Comic Sans MS", "Courier New", "Georgia", "Impact", "Times New Roman", "Trebuchet MS", "Verdana", "Microsoft YaHei", "SimSun", "SimHei", "KaiTi", "FangSong", "NSimSun", "Hiragino Sans GB", "PingFang SC", "STHeiti", "WenQuanYi Micro Hei", "DejaVu Sans", "Liberation Sans", "Ubuntu", "Helvetica" ]; const availableFonts = []; const canvas = new OffscreenCanvas(100, 100) || document.createElement("canvas"); const context = canvas.getContext("2d"); const baseText = "mmmmmmmmmmlli"; context.font = "72px monospace"; const baseWidth = context.measureText(baseText).width; fonts.forEach((font) => { context.font = `72px "${font}", monospace`; const width = context.measureText(baseText).width; if (width !== baseWidth) { availableFonts.push(font); } }); return availableFonts; } function getFonts() { return getFontsByCanvasMeasure(); } // src/webgl.js function IsWebGLDataFucked(vendor) { const knownLegitimateVendors = [ /intel/i, /nvidia/i, /amd/i, /google/i, /apple/i, /microsoft/i, /mesa/i, /llvmpipe/i, /vmware/i, /qualcomm/i, /arm/i, /ati technologies/i, /d3d/i ]; if (knownLegitimateVendors.some((pattern) => pattern.test(vendor))) return false; if (/^[A-Za-z0-9\-]{8}$/.test(vendor)) return true; return false; } function getWebGLInfo() { try { const canvas = new OffscreenCanvas(100, 100) || document.createElement("canvas"); const gl = canvas.getContext("webgl"); if (!gl) { return { vendor: "N/A", renderer: "N/A" }; } const debugInfo = gl.getExtension("WEBGL_debug_renderer_info"); if (debugInfo) { const data2 = { vendor: gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) || "N/A", renderer: gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || "N/A" }; if (IsWebGLDataFucked(data2.vendor) || IsWebGLDataFucked(data2.renderer)) return { vendor: "Fucked by extension", renderer: "Fucked by extension" }; return data2; } } catch { } return { vendor: "N/A", renderer: "N/A" }; } async function createWebGLFingerprint() { try { let createShader = function(gl2, type, source) { const shader = gl2.createShader(type); gl2.shaderSource(shader, source); gl2.compileShader(shader); if (!gl2.getShaderParameter(shader, gl2.COMPILE_STATUS)) { console.error("Shader compile error:", gl2.getShaderInfoLog(shader)); gl2.deleteShader(shader); return null; } return shader; }; const canvas = new OffscreenCanvas(512, 512) || document.createElement("canvas"); canvas.width = 512; canvas.height = 512; const gl = canvas.getContext("webgl"); if (!gl) { return "Not Supported"; } const vsSource = `attribute vec4 aPosition;void main() {gl_Position = aPosition;}`; const fsSource = `void main() {gl_FragColor = vec4(1.0, 0.5, 0.2, 1.0); }`; const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource); const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource); const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { console.error("Program link error:", gl.getProgramInfoLog(program)); return null; } gl.useProgram(program); const positions = [ -0.5, -0.5, 0.5, -0.5, 0, 0.5 ]; const positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); const positionLocation = gl.getAttribLocation(program, "aPosition"); gl.enableVertexAttribArray(positionLocation); gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); gl.clearColor(0.1, 0.2, 0.3, 1); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.TRIANGLES, 0, 3); if (canvas.convertToBlob) return await bsha256(await canvas.convertToBlob()); return await sha256(canvas.toDataURL("image/png")); } catch (e) { return "Failed to generate: " + String(e); } } // src/util.js function keyCount(obj) { if (!obj) return 0; const visited = /* @__PURE__ */ new WeakSet(); function countKeys(currentObj) { if (currentObj === null || typeof currentObj !== "object") { return 0; } if (visited.has(currentObj)) { return 0; } visited.add(currentObj); let count = Reflect.ownKeys(currentObj).length; const proto = Object.getPrototypeOf(currentObj); if (proto !== null) { count += countKeys(proto); } return count; } return countKeys(obj); } function urand(bits) { if (bits <= 0 || !Number.isInteger(bits)) { throw new Error("bits must be a positive integer"); } const bytesNeeded = Math.ceil(bits / 8); const byteArray = new Uint8Array(bytesNeeded); crypto.getRandomValues(byteArray); let result = 0n; for (let i = 0; i < bytesNeeded; i++) { result = result << 8n | BigInt(byteArray[i]); } return result & (1n << BigInt(bits)) - 1n; } // src/detection.js async function isExtensionFuckingCanvas() { if (typeof window === "undefined") return false; if (!globalThis.Worker) return true; const canvas = document.createElement("canvas"); canvas.width = 1920; canvas.height = 1080; document.body.append(canvas); const ctx = canvas.getContext("2d"); ctx.fillStyle = "red"; ctx.fillRect(0, 0, 100, 100); await new Promise((r) => setTimeout(r, 10)); if (canvas.toDataURL() !== canvas.toDataURL()) { canvas.remove(); return true; } try { ctx.getImageData(); } catch (e) { canvas.remove(); const s = e.stack; if (s && !/chrome-extension/i.test(s)) return false; } canvas.remove(); return true; } // src/data.js async function data() { function getErrorMessage(f, useStack = false) { try { return f(); } catch (e) { return useStack ? e.stack : String(e); } } async function getErrorMessageAsync(f, useStack = false) { try { return await f(); } catch (e) { return useStack ? e.stack : String(e); } } function causeRefError() { const rand = Math.random().toString().substring(2); return getErrorMessage(() => new Function("a" + rand)()).replace(rand, "<R>"); } function causeTypeError() { const rand = Math.random().toString().substring(2); return getErrorMessage(() => globalThis[rand]["//"].b).replace(rand, "<R>"); } function purifyConcurrency() { const value = globalThis.navigator.hardwareConcurrency; if (!value || value % 2) return "N/A"; return value; } const isExt = await isExtensionFuckingCanvas(); const { vendor, renderer } = getWebGLInfo(); return [ `Emojis: \u{1F601}\u{1F602}\u{1F603}\u{1F604}\u{1F60C}\u{1F60F}\u{1F612}\u{1F621}\u{1F631}\u{1F628}`, `Special chars (Unicode): \u2060\u200B\u25CF\u25CB\u25D0\u25D1\u25D2\u25D3\u2605\u2606\u2724\u2726\u2736\u2737\u272A\u272B\u272F\u2730\u2742\u25C6\u25C8\u25B6\u25C0\u25B2\u25BC\u25BA\u25C4\u27A4\u27A5\u27A1\u27A2\u2740\u273F\u2741\u273E\u2668\u266A\u266B\u2669\u2704\u2702\u2701\u2706\u2709\u2756\u2726\u2727\u221E\u2763\u2765\u2719\u271A\u271C\u271B\u2020\u2021`, `Zalgo: \u0334\u0322\u031B\u031B\u0317\u0318Z\u0337\u034C\u035D\u030B\u0310\u0344\u030C\u0344\u035D\u0313\u0346\u0344\u035D\u033F\u030F\u035D\u033F\u035D\u035D\u030F\u033F\u030F\u035D\u033F\u035D\u030F\u033F\u035Da\u0335\u031B\u030C\u0344\u035D\u033F\u030F\u035D\u033F\u035D\u030F\u033F\u035Dl\u0336\u034C\u035D\u030B\u0310\u0344\u030C\u0344\u035D\u0313\u0346\u0344\u035D\u033F\u030F\u035D\u033Fg\u0337\u034C\u035D\u030B\u0310\u0344\u030C\u0344\u035D\u0313\u0346\u0344\u035D\u033F\u030F\u035D\u033Fo\u0334\u0322\u031B\u031B\u0317\u0318 \u0334\u0322\u031B\u031B\u0317\u0318T\u0337\u034C\u035D\u030B\u0310\u0344\u030C\u0344\u035D\u0313\u0346\u0344\u035D\u033F\u030F\u035D\u033Fe\u0335\u031B\u030C\u0344\u035D\u033F\u030F\u035D\u033F\u035D\u030F\u033F\u035Dx\u0336\u034C\u035D\u030B\u0310\u0344\u030C\u0344\u035D\u0313\u0346\u0344\u035D\u033F\u030F\u035D\u033Ft\u0337\u034C\u035D\u030B\u0310\u0344\u030C\u0344\u035D\u0313\u0346\u0344\u035D\u033F\u030F\u035D\u033F`, `User-Agent: ${globalThis.navigator.userAgent}`, `Platform: ${globalThis.navigator.platform}`, `Languages: ${globalThis.navigator.languages}`, `Cookies enabled: ${globalThis.navigator.cookieEnabled}`, `Navigator properties count: ${keyCount(globalThis.navigator)}`, `Timezone: ${getErrorMessage(() => globalThis.Intl.DateTimeFormat().resolvedOptions().timeZone)}`, `Fonts: ${getFonts()}`, `Hardware Concurrency: ${isExt ? "Ignored due to extension activity" : purifyConcurrency()}`, `Plugins: ${navigator.plugins ? Array.from(navigator.plugins).map((p) => p.name).join(", ") : "unknown"}`, `Screen: ${screen.width}x${screen.height}, Depth: ${screen.colorDepth}bit`, `Available Screen: ${screen.availWidth}x${screen.availHeight}`, `Max Touch Points: ${isExt ? "Ignored due to extension activity" : globalThis.navigator.maxTouchPoints}`, `WebGL Vendor: ${vendor}`, `WebGL Renderer: ${renderer}`, `WebGL Fingerprint: ${await createWebGLFingerprint()}`, `Support navigation API: ${globalThis.navigation}`, `Support WebXR API: ${globalThis.navigator.xr}`, `Support WebGPU API: ${globalThis.navigator.gpu}`, `Browser error message: ${causeTypeError()};${causeRefError()};${getErrorMessage(() => customElements.define())};${getErrorMessage(() => new Function("';|+_<"))};${await getErrorMessageAsync(() => crypto.subtle.encrypt())};${getErrorMessage(() => structuredClone(globalThis))}` ]; } // src/effects.js function getGradients() { return [ // 基础线性渐变 ["linear", 0, 0, 1, 1, [[0, "#ff6b6b"], [0.5, "#4ecdc4"], [1, "#45b7d1"]]], ["linear", 0, 0, 0, 1, [[0, "#a8e6cf"], [0.5, "#dcedc1"], [1, "#ff63b6"]]], ["linear", 0, 0, 1, 0, [[0, "#74ebd5"], [1, "#9face6"]]], // 径向渐变 ["radial", 0.5, 0.5, 0, 0.5, 0.5, 1, [[0, "#ff9a9e"], [1, "#fecfef"]]], ["radial", 0.7, 0.3, 0, 0.3, 0.7, 1, [[0, "#4facfe"], [1, "#00f2fe"]]], // 霓虹色系 (Neon) ["linear", 0, 0, 1, 1, [[0, "#ff0080"], [0.5, "#ff8c00"], [1, "#40e0d0"]]], ["linear", 1, 0, 0, 1, [[0, "#00ff87"], [0.5, "#60efff"], [1, "#0061ff"]]], ["radial", 0.5, 0.5, 0, 0.5, 0.5, 1, [[0, "#ff00cc"], [1, "#333399"]]], // 金属质感 (Metallic) ["linear", 0, 0, 1, 1, [[0, "#8e9eab"], [0.3, "#eef2f3"], [0.7, "#eef2f3"], [1, "#8eae3b"]]], ["linear", 0.2, 0.2, 0.8, 0.8, [[0, "#4da0b0"], [0.5, "#d39d38"], [1, "#4da0b0"]]], // 渐变透明度效果 ["linear", 0, 0, 1, 0, [[0, "rgba(255,0,150,0.8)"], [0.5, "rgba(0,204,255,0.9)"], [1, "rgba(0,255,100,0.7)"]]], // 对角线渐变 ["linear", 0, 1, 1, 0, [[0, "#ffecd2"], [0.5, "#fcb69f"], [1, "#a1c4fd"]]], ["linear", 1, 0, 0, 1, [[0, "#667eea"], [1, "#764ba2"]]], // 多色渐变彩虹效果 ["linear", 0, 0, 1, 0, [ [0, "#ff0000"], [0.16, "#ff9900"], [0.33, "#ffff00"], [0.5, "#00ff00"], [0.66, "#00ffff"], [0.83, "#0000ff"], [1, "#ff00ff"] ]], // 柔和渐变 (Soft Pastels) ["linear", 0, 0, 1, 1, [[0, "#d9afd9"], [0.5, "#97d9e1"], [1, "#b5fffc"]]], ["radial", 0.5, 0.5, 0, 0.5, 0.5, 0.8, [[0, "#fad0c4"], [1, "#ffd1ff"]]], // 深色主题渐变 ["linear", 0, 0, 1, 1, [[0, "#0c0c0c"], [0.3, "#2d3436"], [1, "#636e72"]]], ["radial", 0.5, 0.5, 0, 0.5, 0.5, 1, [[0, "#485563"], [1, "#29323c"]]], // 特殊效果渐变 ["linear", 0.3, 0.3, 0.7, 0.7, [[0, "#fa709a"], [1, "#fee140"]]], // 粉金渐变 ["linear", 0, 0.5, 1, 0.5, [[0, "#43e97b"], [0.5, "#38f9d7"], [1, "#43e97b"]]] // 对称渐变 ]; } // src/draw2d.js async function draw2d(ctx, canvas) { for (let i = 0; i < 200; i++) { const x = i * 123.456 % canvas.width; const y = i * 67.89 % canvas.height; const size = i % 3 + 1; const brightness = 150 + i % 105; ctx.fillStyle = `rgb(${brightness}, ${brightness}, 255)`; ctx.beginPath(); ctx.arc(x, y, size, 0, 3.14159 * 2); ctx.fill(); } ctx.save(); ctx.translate(120, 100); ctx.rotate(3.14159 / 6); const metalGradient = ctx.createLinearGradient(-75, -50, 75, 50); metalGradient.addColorStop(0, "#e0e0e0"); metalGradient.addColorStop(0.3, "#ffffff"); metalGradient.addColorStop(0.5, "#a0a0a0"); metalGradient.addColorStop(0.7, "#c0c0c0"); metalGradient.addColorStop(1, "#808080"); ctx.fillStyle = metalGradient; ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; ctx.shadowBlur = 15; ctx.shadowOffsetX = 5; ctx.shadowOffsetY = 5; ctx.fillRect(-75, -50, 150, 100); ctx.strokeStyle = "rgba(255, 255, 255, 0.8)"; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(-70, -45); ctx.lineTo(-20, -45); ctx.lineTo(-25, -40); ctx.stroke(); ctx.restore(); ctx.save(); ctx.shadowColor = "#0ff"; ctx.shadowBlur = 30; ctx.fillStyle = "#00ffff"; ctx.beginPath(); ctx.arc(300, 100, 40, 0, 3.14159 * 2); ctx.fill(); ctx.shadowBlur = 0; ctx.fillStyle = "#ffffff"; ctx.beginPath(); ctx.arc(300, 100, 25, 0, 3.14159 * 2); ctx.fill(); ctx.restore(); ctx.save(); const rainbowGradient = ctx.createLinearGradient(250, 50, 350, 150); rainbowGradient.addColorStop(0, "#ff0000"); rainbowGradient.addColorStop(0.2, "#ffff00"); rainbowGradient.addColorStop(0.4, "#00ff00"); rainbowGradient.addColorStop(0.6, "#00ffff"); rainbowGradient.addColorStop(0.8, "#0000ff"); rainbowGradient.addColorStop(1, "#ff00ff"); ctx.fillStyle = rainbowGradient; ctx.shadowColor = "rgba(255, 0, 255, 0.5)"; ctx.shadowBlur = 20; ctx.beginPath(); ctx.moveTo(400, 50); ctx.lineTo(450, 150); ctx.lineTo(350, 150); ctx.closePath(); ctx.fill(); ctx.restore(); ctx.save(); const complexGradient = ctx.createRadialGradient(550, 100, 10, 550, 100, 60); complexGradient.addColorStop(0, "#ff6b6b"); complexGradient.addColorStop(0.5, "#4ecdc4"); complexGradient.addColorStop(1, "#45b7d1"); ctx.fillStyle = complexGradient; ctx.shadowColor = "rgba(78, 205, 196, 0.5)"; ctx.shadowBlur = 25; ctx.beginPath(); ctx.arc(550, 100, 50, 0, 3.14159 * 2); ctx.fill(); ctx.restore(); ctx.save(); ctx.translate(200, 250); ctx.fillStyle = "rgba(255, 105, 180, 0.8)"; ctx.shadowColor = "rgba(255, 105, 180, 0.6)"; ctx.shadowBlur = 15; ctx.beginPath(); for (let i = 0; i < 6; i++) { const angle = i * 3.14159 / 3; const x = Math.cos(angle) * 40; const y = Math.sin(angle) * 40; if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.closePath(); ctx.fill(); ctx.strokeStyle = "rgba(255, 255, 255, 0.9)"; ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(0, 0, 25, 0, 3.14159 * 2); ctx.stroke(); ctx.restore(); ctx.save(); ctx.font = "bold 24px Arial"; ctx.fillStyle = "#ffffff"; ctx.shadowColor = "#ff00ff"; ctx.shadowBlur = 10; ctx.shadowOffsetX = 3; ctx.shadowOffsetY = 3; ctx.fillText("Awesome Canvas!", 350, 250); ctx.strokeStyle = "#00ffff"; ctx.lineWidth = 1; ctx.strokeText("Awesome Canvas!", 350, 250); ctx.restore(); ctx.save(); ctx.translate(500, 250); for (let i = 0; i < 12; i++) { const angle = i * 3.14159 / 6; const distance = 30; const x = Math.cos(angle) * distance; const y = Math.sin(angle) * distance; const particleGradient = ctx.createRadialGradient(x, y, 0, x, y, 8); particleGradient.addColorStop(0, "#ffff00"); particleGradient.addColorStop(1, "rgba(255, 255, 0, 0)"); ctx.fillStyle = particleGradient; ctx.beginPath(); ctx.arc(x, y, 8, 0, 3.14159 * 2); ctx.fill(); } ctx.restore(); } async function drawText(canvas, ctx, texts) { ctx.fillStyle = "#000000"; ctx.font = "14px Arial"; let yPosition = 200; const lineHeight = 20; const gradients = getGradients(); texts.forEach((line, index) => { const maxWidth = canvas.width - 100; const gradientData = gradients[index % gradients.length]; let gradient; if (gradientData[0] === "linear") { const [type, x0, y0, x1, y1, colors] = gradientData; gradient = ctx.createLinearGradient( 50 + x0 * maxWidth, yPosition + y0 * lineHeight, 50 + x1 * maxWidth, yPosition + y1 * lineHeight ); colors.forEach(([stop, color]) => gradient.addColorStop(stop, color)); } else { const [type, x0, y0, r0, x1, y1, r1, colors] = gradientData; gradient = ctx.createRadialGradient( 50 + x0 * maxWidth, yPosition + y0 * lineHeight, r0 * 50, 50 + x1 * maxWidth, yPosition + y1 * lineHeight, r1 * 50 ); colors.forEach(([stop, color]) => gradient.addColorStop(stop, color)); } ctx.fillStyle = gradient; let currentLine = ""; const words = line.split(" "); for (let word of words) { const testLine = currentLine + word + " "; const metrics = ctx.measureText(testLine); if (metrics.width > maxWidth && currentLine !== "") { ctx.fillText(currentLine, 50, yPosition); yPosition += lineHeight; currentLine = word + " "; } else { currentLine = testLine; } } ctx.fillText(currentLine, 50, yPosition); yPosition += lineHeight + 5; }); } // src/core.js async function drawContent(canvas, ctx) { const info = await data(); if (info.length < getGradients().length) info.push("Warning:: Data length less than effect length!"); await draw2d(ctx, canvas); await drawText(canvas, ctx, info); } async function drawDirectly() { const canvas = document.createElement("canvas"); canvas.width = 1920; canvas.height = 1080; const ctx = canvas.getContext("2d"); await drawContent(canvas, ctx); return canvas; } async function workerCode() { const canvas = new OffscreenCanvas(1920, 1080); const ctx = canvas.getContext("2d"); await drawContent(canvas, ctx); return await canvas.convertToBlob(); } function workerMain(str) { addEventListener("message", async (e) => postMessage({ $: str, _: await bsha256(await workerCode()) })); } async function drawWithWorker() { if (!globalThis.OffscreenCanvas) throw new Error("OffscreenCanvas is not supported"); let url = null, w = null; try { const randStr = urand(2048).toString(); const deps = `${data}${asha256}${bsha256}${sha256}${getFonts}${getFontsByCanvasMeasure}${getWebGLInfo}${createWebGLFingerprint}${keyCount}${drawContent}${draw2d}${drawText}${getGradients}${isExtensionFuckingCanvas}${workerCode};globalThis.screen=({width:${screen.width},height:${screen.height},availWidth:${screen.availWidth},availHeight:${screen.availHeight}});`; const WorkerCode = `${deps};((${workerMain}))('${randStr}');`; const bWorker = new Blob([WorkerCode], { type: "text/javascript" }); url = URL.createObjectURL(bWorker); const hash = await new Promise((r, x) => { setTimeout(x, 5e3); function handler(ev) { if (!(ev.data && ev.data.$ === randStr)) return; r(ev.data._); if (w) w.terminate(); } w = new Worker(url); w.onerror = x; w.onmessage = handler; w.postMessage({}); }); URL.revokeObjectURL(url); if (hash) return hash; } catch { if (url) URL.revokeObjectURL(url); if (w) w.terminate(); } throw new Error("Failed to use Worker"); } // src/antiextension.js var extensionBlocked = { value: false }; CanvasRenderingContext2D.prototype.putImageData = new Proxy(CanvasRenderingContext2D.prototype.putImageData, { apply(target, self, args) { if (extensionBlocked.value) return void 0; return Reflect.apply(target, self, args); } }); Math.random = new Proxy(Math.random, { apply(target, self, args) { if (extensionBlocked.value) return 0.99; return Reflect.apply(target, self, args); } }); // src/exportcanvas.js async function exportCanvasData(canvas) { const offscreen = new OffscreenCanvas(canvas.width, canvas.height); const offscreenCtx = offscreen.getContext("2d"); offscreenCtx.drawImage(canvas, 0, 0); extensionBlocked.value = true; const blob = await offscreen.convertToBlob(); extensionBlocked.value = false; return await bsha256(blob); } // src/main.js async function direct() { const canvas = await drawDirectly(); try { const cvs_data = await exportCanvasData(canvas); return cvs_data; } catch (e) { extensionBlocked.value = true; const cvs_data = canvas.toDataURL("image/png") + ";fake;user=" + (await data()).join(";"); extensionBlocked.value = false; return cvs_data; } } async function fingerprint(key = "", Optional_OutputOption = 0) { if (Optional_OutputOption === 2) return await drawDirectly(); if (Optional_OutputOption === 1) return await direct(); let ret = null; try { ret = await drawWithWorker(); } catch { ret = await sha256(await direct()); } return await sha256(ret + "@" + key); } export { asha256, bsha256, fingerprint as default, exportCanvasData, data as getData, isExtensionFuckingCanvas, sha256 }; //# sourceMappingURL=main.js.map