Fix cryptographic random bias in fingerprint mask generation

This commit is contained in:
lockbitchat
2025-10-20 00:42:03 -04:00
parent 60e4bb6b8a
commit 9c1258cd1e

View File

@@ -4482,57 +4482,56 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
} }
// 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;
if (range <= 0) throw new Error('Invalid range');
const bytesNeeded = Math.max(1, Math.ceil(Math.log2(range) / 8));
const maxValue = Math.pow(256, bytesNeeded);
const threshold = maxValue - (maxValue % range);
const bucketSize = threshold / range; // exact integer
let randomValue;
do {
const randomBytes = crypto.getRandomValues(new Uint8Array(bytesNeeded));
randomValue = 0;
for (let i = 0; i < bytesNeeded; i++) {
randomValue = (randomValue << 8) | randomBytes[i];
} }
} while (randomValue >= threshold); const range = max - min + 1;
if (range <= 0) throw new Error('Invalid range');
// Use bucket index instead of modulo // Для больших диапазонов используем несколько байт
return min + Math.floor(randomValue / bucketSize); const bytesNeeded = Math.ceil(Math.log2(range) / 8);
} const maxValue = Math.pow(256, bytesNeeded);
const threshold = maxValue - (maxValue % range);
// Helper: unbiased float in [minFloat, maxFloat] with optional precision let randomValue;
getUnbiasedRandomFloat(minFloat, maxFloat, scale = 100) { do {
const minInt = Math.round(minFloat * scale); const randomBytes = crypto.getRandomValues(new Uint8Array(bytesNeeded));
const maxInt = Math.round(maxFloat * scale); randomValue = 0;
const intVal = this.getUnbiasedRandomInRange(minInt, maxInt); for (let i = 0; i < bytesNeeded; i++) {
return intVal / scale; randomValue = (randomValue << 8) | randomBytes[i];
} }
} while (randomValue >= threshold);
// Generate fingerprint mask return min + (randomValue % range);
generateFingerprintMask() { }
const cryptoRandom = crypto.getRandomValues(new Uint8Array(128));
const mask = { // Safe helper: unbiased float in [minFloat, maxFloat] with scale
timingOffset: this.getUnbiasedRandomInRange(0, 1500), getSafeRandomFloat(minFloat, maxFloat, scale = 100) {
sizeVariation: this.getUnbiasedRandomFloat(0.75, 1.25), // float, unbiased const minInt = Math.round(minFloat * scale);
noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))), const maxInt = Math.round(maxFloat * scale);
headerVariations: [ const intVal = this.getSafeRandomInt(minInt, maxInt);
'X-Client-Version', 'X-Session-ID', 'X-Request-ID', 'X-Timestamp', 'X-Signature', return intVal / scale;
'X-Secure', 'X-Encrypted', 'X-Protected', 'X-Safe', 'X-Anonymous', 'X-Private' }
],
noiseIntensity: this.getUnbiasedRandomInRange(50, 150), // Generate fingerprint mask
sizeMultiplier: this.getUnbiasedRandomFloat(0.75, 1.25), // float, unbiased generateFingerprintMask() {
timingVariation: this.getUnbiasedRandomInRange(100, 1100) const cryptoRandom = crypto.getRandomValues(new Uint8Array(128));
};
return mask; const mask = {
} timingOffset: this.getSafeRandomInt(0, 1500), // 01500ms
sizeVariation: this.getSafeRandomFloat(0.75, 1.25), // unbiased float
noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))),
headerVariations: [
'X-Client-Version', 'X-Session-ID', 'X-Request-ID', 'X-Timestamp', 'X-Signature',
'X-Secure', 'X-Encrypted', 'X-Protected', 'X-Safe', 'X-Anonymous', 'X-Private'
],
noiseIntensity: this.getSafeRandomInt(50, 150), // 50150%
sizeMultiplier: this.getSafeRandomFloat(0.75, 1.25), // unbiased float
timingVariation: this.getSafeRandomInt(100, 1100) // 1001100ms
};
return mask;
}
// Security configuration - all features enabled by default // Security configuration - all features enabled by default
configureSecurityForSession() { configureSecurityForSession() {