gfsd
This commit is contained in:
+138
-2
@@ -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
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user