Fix cryptographic random bias (CodeQL js/biased-cryptographic-random)

Reimplemented getUnbiasedRandomInRange() using rejection sampling for all range sizes to eliminate modulo bias and ensure uniform cryptographically secure randomness.
This commit is contained in:
lockbitchat
2025-10-20 00:25:50 -04:00
parent 333c4b87b5
commit 4233ba3d7e
3 changed files with 40 additions and 56 deletions
+10 -19
View File
@@ -8196,24 +8196,18 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
// Helper function to generate unbiased random values in a range // Helper function to generate unbiased random values in a range
getUnbiasedRandomInRange(min, max) { getUnbiasedRandomInRange(min, max) {
const range = max - min + 1; const range = max - min + 1;
if (range > 256) { if (range <= 0) throw new Error("Invalid range");
const bytesNeeded = 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;
let randomValue2;
do {
const randomBytes = crypto.getRandomValues(new Uint8Array(bytesNeeded));
randomValue2 = 0;
for (let i = 0; i < bytesNeeded; i++) {
randomValue2 = (randomValue2 << 8) + randomBytes[i];
}
} while (randomValue2 >= threshold);
return randomValue2 % range + min;
}
let randomValue; let randomValue;
do { do {
randomValue = crypto.getRandomValues(new Uint8Array(1))[0]; const randomBytes = crypto.getRandomValues(new Uint8Array(bytesNeeded));
} while (randomValue >= 256 - 256 % range); randomValue = 0;
for (let i = 0; i < bytesNeeded; i++) {
randomValue = randomValue << 8 | randomBytes[i];
}
} while (randomValue >= threshold);
return randomValue % range + min; return randomValue % range + min;
} }
// Generate fingerprint mask for anti-fingerprinting with enhanced randomization // Generate fingerprint mask for anti-fingerprinting with enhanced randomization
@@ -8221,11 +8215,10 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
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.getUnbiasedRandomInRange(0, 1500),
// 0-1500ms // 01500ms
sizeVariation: this.getUnbiasedRandomInRange(75, 125) / 100, sizeVariation: this.getUnbiasedRandomInRange(75, 125) / 100,
// 0.75 to 1.25 // 0.751.25
noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))), noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))),
// Increased size
headerVariations: [ headerVariations: [
"X-Client-Version", "X-Client-Version",
"X-Session-ID", "X-Session-ID",
@@ -8240,11 +8233,9 @@ var EnhancedSecureWebRTCManager = class _EnhancedSecureWebRTCManager {
"X-Private" "X-Private"
], ],
noiseIntensity: this.getUnbiasedRandomInRange(50, 150), noiseIntensity: this.getUnbiasedRandomInRange(50, 150),
// 50-150% // 50150%
sizeMultiplier: this.getUnbiasedRandomInRange(75, 125) / 100, sizeMultiplier: this.getUnbiasedRandomInRange(75, 125) / 100,
// 0.75-1.25
timingVariation: this.getUnbiasedRandomInRange(100, 1100) timingVariation: this.getUnbiasedRandomInRange(100, 1100)
// 100-1100ms
}; };
return mask; return mask;
} }
+3 -3
View File
File diff suppressed because one or more lines are too long
+11 -18
View File
@@ -4484,9 +4484,9 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
// Helper function to generate unbiased random values in a range // Helper function to generate unbiased random values in a range
getUnbiasedRandomInRange(min, max) { getUnbiasedRandomInRange(min, max) {
const range = max - min + 1; const range = max - min + 1;
if (range <= 0) throw new Error('Invalid range');
// If range is larger than 256, use multiple bytes // Use rejection sampling to avoid modulo bias
if (range > 256) {
const bytesNeeded = 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);
@@ -4496,41 +4496,34 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
const randomBytes = crypto.getRandomValues(new Uint8Array(bytesNeeded)); const randomBytes = crypto.getRandomValues(new Uint8Array(bytesNeeded));
randomValue = 0; randomValue = 0;
for (let i = 0; i < bytesNeeded; i++) { for (let i = 0; i < bytesNeeded; i++) {
randomValue = (randomValue << 8) + randomBytes[i]; randomValue = (randomValue << 8) | randomBytes[i];
} }
} while (randomValue >= threshold); } while (randomValue >= threshold); // discard biased values
return (randomValue % range) + min; return (randomValue % range) + min;
} }
// For ranges <= 256, use single byte with rejection sampling
let randomValue;
do {
randomValue = crypto.getRandomValues(new Uint8Array(1))[0];
} while (randomValue >= 256 - (256 % range));
return (randomValue % range) + min;
}
// Generate fingerprint mask for anti-fingerprinting with enhanced randomization // Generate fingerprint mask for anti-fingerprinting with enhanced randomization
generateFingerprintMask() { generateFingerprintMask() {
// Enhanced randomization to prevent side-channel attacks
const cryptoRandom = crypto.getRandomValues(new Uint8Array(128)); const cryptoRandom = crypto.getRandomValues(new Uint8Array(128));
const mask = { const mask = {
timingOffset: this.getUnbiasedRandomInRange(0, 1500), // 0-1500ms timingOffset: this.getUnbiasedRandomInRange(0, 1500), // 01500ms
sizeVariation: this.getUnbiasedRandomInRange(75, 125) / 100, // 0.75 to 1.25 sizeVariation: this.getUnbiasedRandomInRange(75, 125) / 100, // 0.751.25
noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))), // Increased size noisePattern: Array.from(crypto.getRandomValues(new Uint8Array(64))),
headerVariations: [ headerVariations: [
'X-Client-Version', 'X-Session-ID', 'X-Request-ID', 'X-Timestamp', 'X-Signature', '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' 'X-Secure', 'X-Encrypted', 'X-Protected', 'X-Safe', 'X-Anonymous', 'X-Private'
], ],
noiseIntensity: this.getUnbiasedRandomInRange(50, 150), // 50-150% noiseIntensity: this.getUnbiasedRandomInRange(50, 150), // 50150%
sizeMultiplier: this.getUnbiasedRandomInRange(75, 125) / 100, // 0.75-1.25 sizeMultiplier: this.getUnbiasedRandomInRange(75, 125) / 100,
timingVariation: this.getUnbiasedRandomInRange(100, 1100) // 100-1100ms timingVariation: this.getUnbiasedRandomInRange(100, 1100)
}; };
return mask; return mask;
} }
// Security configuration - all features enabled by default // Security configuration - all features enabled by default
configureSecurityForSession() { configureSecurityForSession() {
this._secureLog('info', '🔧 Configuring security - all features enabled by default'); this._secureLog('info', '🔧 Configuring security - all features enabled by default');