Fix cryptographic random bias in fingerprint mask generation

This commit is contained in:
lockbitchat
2025-10-20 00:42:12 -04:00
parent 9c1258cd1e
commit b18e943abd
2 changed files with 19 additions and 17 deletions

32
dist/app-boot.js vendored
View File

@@ -8194,16 +8194,15 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
} }
} }
// Helper function: unbiased integer in [min, max] // Helper function: unbiased integer in [min, max]
getUnbiasedRandomInRange(min, max) { getSafeRandomInt(min, max) {
if (!Number.isInteger(min) || !Number.isInteger(max)) { if (!Number.isInteger(min) || !Number.isInteger(max)) {
throw new Error("getUnbiasedRandomInRange requires integer min and max"); throw new Error("getSafeRandomInt requires integer min and max");
} }
const range = max - min + 1; const range = max - min + 1;
if (range <= 0) throw new Error("Invalid range"); if (range <= 0) throw new Error("Invalid range");
const bytesNeeded = Math.max(1, Math.ceil(Math.log2(range) / 8)); const bytesNeeded = Math.ceil(Math.log2(range) / 8);
const maxValue = Math.pow(256, bytesNeeded); const maxValue = Math.pow(256, bytesNeeded);
const threshold = maxValue - maxValue % range; const threshold = maxValue - maxValue % range;
const bucketSize = threshold / range;
let randomValue; let randomValue;
do { do {
const randomBytes = crypto.getRandomValues(new Uint8Array(bytesNeeded)); const randomBytes = crypto.getRandomValues(new Uint8Array(bytesNeeded));
@@ -8212,22 +8211,23 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
randomValue = randomValue << 8 | randomBytes[i]; randomValue = randomValue << 8 | randomBytes[i];
} }
} while (randomValue >= threshold); } while (randomValue >= threshold);
return min + Math.floor(randomValue / bucketSize); return min + randomValue % range;
} }
// Helper: unbiased float in [minFloat, maxFloat] with optional precision // Safe helper: unbiased float in [minFloat, maxFloat] with scale
getUnbiasedRandomFloat(minFloat, maxFloat, scale = 100) { getSafeRandomFloat(minFloat, maxFloat, scale = 100) {
const minInt = Math.round(minFloat * scale); const minInt = Math.round(minFloat * scale);
const maxInt = Math.round(maxFloat * scale); const maxInt = Math.round(maxFloat * scale);
const intVal = this.getUnbiasedRandomInRange(minInt, maxInt); const intVal = this.getSafeRandomInt(minInt, maxInt);
return intVal / scale; return intVal / scale;
} }
// Generate fingerprint mask // Generate fingerprint mask
generateFingerprintMask() { generateFingerprintMask() {
const cryptoRandom = crypto.getRandomValues(new Uint8Array(128)); const cryptoRandom = crypto.getRandomValues(new Uint8Array(128));
const mask = { const mask = {
timingOffset: this.getUnbiasedRandomInRange(0, 1500), timingOffset: this.getSafeRandomInt(0, 1500),
sizeVariation: this.getUnbiasedRandomFloat(0.75, 1.25), // 01500ms
// float, unbiased sizeVariation: this.getSafeRandomFloat(0.75, 1.25),
// unbiased float
noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))), noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))),
headerVariations: [ headerVariations: [
"X-Client-Version", "X-Client-Version",
@@ -8242,10 +8242,12 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
"X-Anonymous", "X-Anonymous",
"X-Private" "X-Private"
], ],
noiseIntensity: this.getUnbiasedRandomInRange(50, 150), noiseIntensity: this.getSafeRandomInt(50, 150),
sizeMultiplier: this.getUnbiasedRandomFloat(0.75, 1.25), // 50150%
// float, unbiased sizeMultiplier: this.getSafeRandomFloat(0.75, 1.25),
timingVariation: this.getUnbiasedRandomInRange(100, 1100) // unbiased float
timingVariation: this.getSafeRandomInt(100, 1100)
// 1001100ms
}; };
return mask; return mask;
} }

File diff suppressed because one or more lines are too long