This commit is contained in:
2026-09-12 21:55:40 +02:00
parent 53055ea5c6
commit 90860b9279
24 changed files with 1958 additions and 45 deletions
+138 -2
View File
@@ -83,6 +83,102 @@
this.rawPub = null;
this.rawSeed = null;
this.isSessionReady = false;
this.hwFingerprint = null;
}
/**
* Compute a hardware-level fingerprint based on GPU, CPU, RAM, screen, and Web Audio DSP.
* Survives across private browsing windows and different browsers on the same physical device.
*/
async getHardwareFingerprint() {
if (this.hwFingerprint) return this.hwFingerprint;
try {
let glVendor = '';
let glRenderer = '';
let glLimits = '';
try {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (gl) {
const ext = gl.getExtension('WEBGL_debug_renderer_info');
if (ext) {
glVendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL) || '';
glRenderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL) || '';
}
glLimits = [
gl.getParameter(gl.MAX_TEXTURE_SIZE) || 0,
gl.getParameter(gl.MAX_RENDERBUFFER_SIZE) || 0,
gl.getParameter(gl.MAX_VERTEX_ATTRIBS) || 0,
gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS) || 0,
gl.getParameter(gl.MAX_VARYING_VECTORS) || 0,
gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS) || 0
].join(',');
}
} catch (e) {}
const concurrency = navigator.hardwareConcurrency || 0;
const memory = navigator.deviceMemory || 0;
const screenInfo = [
window.screen ? window.screen.width : 0,
window.screen ? window.screen.height : 0,
window.screen ? window.screen.colorDepth : 0,
window.screen ? window.screen.pixelDepth : 0,
window.devicePixelRatio || 1
].join('x');
let audioFp = '';
try {
const AudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;
if (AudioContext) {
const actx = new AudioContext(1, 44100, 44100);
const osc = actx.createOscillator();
osc.type = 'triangle';
osc.frequency.setValueAtTime(10000, actx.currentTime);
const comp = actx.createDynamicsCompressor();
comp.threshold.setValueAtTime(-50, actx.currentTime);
comp.knee.setValueAtTime(40, actx.currentTime);
comp.ratio.setValueAtTime(12, actx.currentTime);
comp.reduction.setValueAtTime(-20, actx.currentTime);
comp.attack.setValueAtTime(0, actx.currentTime);
comp.release.setValueAtTime(0.25, actx.currentTime);
osc.connect(comp);
comp.connect(actx.destination);
osc.start(0);
const renderedBuffer = await actx.startRendering();
const channel = renderedBuffer.getChannelData(0);
let sum = 0;
const end = Math.min(channel.length, 5000);
for (let i = 4500; i < end; i++) {
sum += Math.abs(channel[i] || 0);
}
audioFp = sum.toFixed(7);
}
} catch (e) {}
const rawHardware = [
glVendor,
glRenderer,
glLimits,
concurrency,
memory,
screenInfo,
audioFp
].join('~~~');
const encoder = new TextEncoder();
const data = encoder.encode(rawHardware);
const hashBuf = await window.crypto.subtle.digest('SHA-256', data);
const hashHex = bytesToHex(new Uint8Array(hashBuf));
this.hwFingerprint = `HW:${hashHex}`;
return this.hwFingerprint;
} catch (err) {
console.warn('[ANON_SSH] Failed to compute hardware fingerprint:', err);
return null;
}
}
getDomain() {
@@ -430,6 +526,8 @@
const timestamp = Date.now();
const message = `anon-auth:${timestamp}:${this.pubkey}`;
const signature = await this.sign(message);
const tombstone = this.getTombstone();
const hwFingerprint = await this.getHardwareFingerprint();
const res = await fetch('/api/v2/anon/session', {
method: 'POST',
@@ -437,11 +535,27 @@
body: JSON.stringify({
pubkey: this.pubkey,
timestamp: timestamp,
signature: signature
signature: signature,
tombstone: tombstone,
hw_fingerprint: hwFingerprint
})
});
const data = await res.json();
if (data.banned) {
this.setTombstone({
banned: true,
fingerprint: data.fingerprint || this.fingerprint,
hw_fingerprint: data.hw_fingerprint || hwFingerprint,
reason: data.reason,
expires: data.expires
});
if (window.location.pathname !== '/banned') {
window.location.href = data.redirect || '/banned';
}
return;
}
if (data.success) {
this.isSessionReady = true;
if (data.csrf_token) {
@@ -452,7 +566,8 @@
window.f0ckAnonIdentity = {
userId: data.user_id,
fingerprint: data.fingerprint,
shortFingerprint: data.short_fingerprint
shortFingerprint: data.short_fingerprint,
hwFingerprint: data.hw_fingerprint || hwFingerprint
};
window.dispatchEvent(new CustomEvent('f0ck:anon_session_ready', { detail: window.f0ckAnonIdentity }));
}
@@ -461,6 +576,27 @@
}
}
/**
* Get client ban tombstone if previously banned.
*/
getTombstone() {
try {
const raw = localStorage.getItem('f0ck_anon_tombstone');
return raw ? JSON.parse(raw) : null;
} catch (e) {
return null;
}
}
/**
* Save client ban tombstone.
*/
setTombstone(tombstone) {
try {
localStorage.setItem('f0ck_anon_tombstone', JSON.stringify(tombstone));
} catch (e) {}
}
/**
* Trigger browser download of id_ed25519 private key file
*/