diff --git a/src/network/EnhancedSecureWebRTCManager.js b/src/network/EnhancedSecureWebRTCManager.js index af31466..2c91a23 100644 --- a/src/network/EnhancedSecureWebRTCManager.js +++ b/src/network/EnhancedSecureWebRTCManager.js @@ -274,8 +274,8 @@ this._secureLog('info', 'πŸ”’ Enhanced Mutex system fully initialized and valida // 1. Nested Encryption Layer this.nestedEncryptionKey = null; - this.nestedEncryptionIV = null; - this.nestedEncryptionCounter = 0; + // CRITICAL FIX: Removed nestedEncryptionIV and nestedEncryptionCounter + // Each nested encryption now generates fresh random IV for maximum security // 2. Packet Padding this.paddingConfig = { @@ -642,55 +642,104 @@ _initializeMutexSystem() { this._secureLog('info', `πŸ”§ Secure logging initialized (Production: ${this._isProductionMode})`); } /** - * Shim to redirect arbitrary console.log calls to _secureLog('info', ...) + * CRITICAL FIX: Shim to redirect arbitrary console.log calls to _secureLog('info', ...) + * Fixed syntax errors and improved error handling */ _secureLogShim(...args) { try { - if (!args || args.length === 0) { + // Validate arguments array + if (!Array.isArray(args) || args.length === 0) { return; } - const [message, ...rest] = args; - if (rest.length === 0) { - this._secureLog('info', String(message)); + + // CRITICAL FIX: Proper destructuring with fallback + const message = args[0]; + const restArgs = args.slice(1); + + // Handle different argument patterns + if (restArgs.length === 0) { + this._secureLog('info', String(message || '')); return; } - if (rest.length === 1) { - this._secureLog('info', String(message), rest[0]); + + if (restArgs.length === 1) { + this._secureLog('info', String(message || ''), restArgs[0]); return; } - this._secureLog('info', String(message), { args: rest }); - } catch (e) { - // Fallback β€” do not disrupt main execution flow + + // CRITICAL FIX: Proper object structure for multiple args + this._secureLog('info', String(message || ''), { + additionalArgs: restArgs, + argCount: restArgs.length + }); + } catch (error) { + // CRITICAL FIX: Better error handling - fallback to original console if available + try { + if (this._originalConsole?.log) { + this._originalConsole.log(...args); + } + } catch (fallbackError) { + // Silent failure to prevent execution disruption + } } } /** - * Redirects global console.log to this instance's secure logger + * CRITICAL FIX: Redirects global console.log to this instance's secure logger + * Improved error handling and validation */ _redirectConsoleLogToSecure() { try { - if (typeof console === 'undefined') return; - // Preserve originals once + // Validate console availability + if (typeof console === 'undefined' || !console) { + return; + } + + // CRITICAL FIX: Preserve originals once with better validation if (!this._originalConsole) { this._originalConsole = { - log: console.log?.bind(console), - info: console.info?.bind(console), - warn: console.warn?.bind(console), - error: console.error?.bind(console), - debug: console.debug?.bind(console) + log: (typeof console.log === 'function') ? console.log.bind(console) : null, + info: (typeof console.info === 'function') ? console.info.bind(console) : null, + warn: (typeof console.warn === 'function') ? console.warn.bind(console) : null, + error: (typeof console.error === 'function') ? console.error.bind(console) : null, + debug: (typeof console.debug === 'function') ? console.debug.bind(console) : null }; } + const self = this; - // Only console.log is redirected to secure; warn/error stay intact + + // CRITICAL FIX: Only console.log is redirected to secure; warn/error stay intact + // Better error handling and validation console.log = function(...args) { try { - self._secureLogShim(...args); - } catch (e) { - // Fallback to original log on failure - if (self._originalConsole?.log) self._originalConsole.log(...args); + // Validate self and method availability + if (self && typeof self._secureLogShim === 'function') { + self._secureLogShim(...args); + } else { + // Fallback to original if shim is not available + if (self._originalConsole?.log) { + self._originalConsole.log(...args); + } + } + } catch (error) { + // CRITICAL FIX: Better fallback handling + try { + if (self._originalConsole?.log) { + self._originalConsole.log(...args); + } + } catch (fallbackError) { + // Silent failure to prevent execution disruption + } } }; - } catch (e) { - // Safe degradation + } catch (error) { + // CRITICAL FIX: Better error logging for debugging + try { + if (this._originalConsole?.error) { + this._originalConsole.error('❌ Failed to redirect console.log to secure logger:', error); + } + } catch (logError) { + // Silent failure - last resort + } } } /** @@ -2053,8 +2102,8 @@ _initializeMutexSystem() { ); // Generate random IV for nested encryption - this.nestedEncryptionIV = crypto.getRandomValues(new Uint8Array(EnhancedSecureWebRTCManager.SIZES.NESTED_ENCRYPTION_IV_SIZE)); - this.nestedEncryptionCounter = 0; + // CRITICAL FIX: No need for base IV or counter - each encryption gets fresh random IV + // This ensures maximum entropy and prevents IV reuse attacks } catch (error) { this._secureLog('error', '❌ Failed to generate nested encryption key:', { errorType: error?.constructor?.name || 'Unknown' }); @@ -2068,10 +2117,9 @@ _initializeMutexSystem() { } try { - // Create unique IV for each encryption - const uniqueIV = new Uint8Array(EnhancedSecureWebRTCManager.SIZES.NESTED_ENCRYPTION_IV_SIZE); - uniqueIV.set(this.nestedEncryptionIV); - uniqueIV[11] = (this.nestedEncryptionCounter++) & 0xFF; + // CRITICAL FIX: Generate fresh random 96-bit IV for each encryption + // This prevents IV reuse attacks that completely break AES-GCM security + const uniqueIV = crypto.getRandomValues(new Uint8Array(EnhancedSecureWebRTCManager.SIZES.NESTED_ENCRYPTION_IV_SIZE)); // Encrypt data with nested layer const encrypted = await crypto.subtle.encrypt( @@ -2097,10 +2145,10 @@ _initializeMutexSystem() { return data; } - // FIX: Check that the data is actually encrypted - if (!(data instanceof ArrayBuffer) || data.byteLength < 20) { + // CRITICAL FIX: Check that the data is actually encrypted with proper IV size + if (!(data instanceof ArrayBuffer) || data.byteLength < EnhancedSecureWebRTCManager.SIZES.NESTED_ENCRYPTION_IV_SIZE + 16) { if (window.DEBUG_MODE) { - console.log('πŸ“ Data not encrypted or too short for nested decryption'); + console.log('πŸ“ Data not encrypted or too short for nested decryption (need IV + minimum encrypted data)'); } return data; } @@ -7349,8 +7397,17 @@ checkFileTransferReadiness() { } } - // ΠšΠ Π˜Π’Π˜Π§Π•Π‘ΠšΠžΠ• Π˜Π‘ΠŸΠ ΠΠ’Π›Π•ΠΠ˜Π•: ΠœΠ΅Ρ‚ΠΎΠ΄ для ΠΏΡ€ΠΈΠ½ΡƒΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Ρ„Π°ΠΉΠ»ΠΎΠ²ΠΎΠΉ систСмы - forceInitializeFileTransfer() { + // CRITICAL FIX: ΠœΠ΅Ρ‚ΠΎΠ΄ для ΠΏΡ€ΠΈΠ½ΡƒΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Ρ„Π°ΠΉΠ»ΠΎΠ²ΠΎΠΉ систСмы + // УстранСн busy-wait, Π·Π°ΠΌΠ΅Π½Π΅Π½ Π½Π° асинхронноС ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠ΅ + async forceInitializeFileTransfer(options = {}) { + // CRITICAL FIX: ДобавляСм Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΎΡ‚ΠΌΠ΅Π½Ρ‹ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ + const abortController = new AbortController(); + const { signal = abortController.signal, timeout = 6000 } = options; + + // БвязываСм внСшний signal с Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½ΠΈΠΌ abortController + if (signal && signal !== abortController.signal) { + signal.addEventListener('abort', () => abortController.abort()); + } try { // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ Π³ΠΎΡ‚ΠΎΠ²Π½ΠΎΡΡ‚ΡŒ соСдинСния if (!this.isVerified) { @@ -7374,17 +7431,46 @@ checkFileTransferReadiness() { // Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΡƒΠ΅ΠΌ Π½ΠΎΠ²ΡƒΡŽ систСму this.initializeFileTransfer(); - // Π–Π΄Π΅ΠΌ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ + // CRITICAL FIX: ЗамСняСм busy-wait Π½Π° асинхронноС ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠ΅ + // Π­Ρ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΎΡ‚Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΡƒ UI-ΠΏΠΎΡ‚ΠΎΠΊΠ° ΠΈ DoS-Π°Ρ‚Π°ΠΊΠΈ let attempts = 0; const maxAttempts = 50; - while (!this.fileTransferSystem && attempts < maxAttempts) { - // Π‘ΠΈΠ½Ρ…Ρ€ΠΎΠ½Π½ΠΎΠ΅ ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠ΅ - const start = Date.now(); - while (Date.now() - start < 100) { - // busy wait - } - attempts++; - } + const checkInterval = 100; // 100ms ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ°ΠΌΠΈ + const maxWaitTime = maxAttempts * checkInterval; // МаксимальноС врСмя оТидания + + // CRITICAL FIX: Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ Promise.race для возмоТности ΠΎΡ‚ΠΌΠ΅Π½Ρ‹ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ + const initializationPromise = new Promise((resolve, reject) => { + const checkInitialization = () => { + // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ ΠΎΡ‚ΠΌΠ΅Π½Ρƒ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ + if (abortController.signal.aborted) { + reject(new Error('Operation cancelled')); + return; + } + + if (this.fileTransferSystem) { + resolve(true); + return; + } + + if (attempts >= maxAttempts) { + reject(new Error(`Initialization timeout after ${maxWaitTime}ms`)); + return; + } + + attempts++; + setTimeout(checkInitialization, checkInterval); + }; + + checkInitialization(); + }); + + // CRITICAL FIX: Π–Π΄Π΅ΠΌ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ с Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒΡŽ ΠΎΡ‚ΠΌΠ΅Π½Ρ‹ ΠΈ Ρ‚Π°ΠΉΠΌΠ°ΡƒΡ‚ΠΎΠΌ + await Promise.race([ + initializationPromise, + new Promise((_, reject) => + setTimeout(() => reject(new Error(`Global timeout after ${timeout}ms`)), timeout) + ) + ]); if (this.fileTransferSystem) { return true; @@ -7393,10 +7479,62 @@ checkFileTransferReadiness() { } } catch (error) { - this._secureLog('error', '❌ Force file transfer initialization failed:', { errorType: error?.constructor?.name || 'Unknown' }); + // CRITICAL FIX: Π£Π»ΡƒΡ‡ΡˆΠ΅Π½Π½Π°Ρ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок + if (error.name === 'AbortError' || error.message.includes('cancelled')) { + this._secureLog('info', '⏹️ File transfer initialization cancelled by user'); + return { cancelled: true }; + } + + this._secureLog('error', '❌ Force file transfer initialization failed:', { + errorType: error?.constructor?.name || 'Unknown', + message: error.message, + attempts: attempts + }); + return { error: error.message, attempts: attempts }; + } + } + + // CRITICAL FIX: ΠœΠ΅Ρ‚ΠΎΠ΄ для ΠΎΡ‚ΠΌΠ΅Π½Ρ‹ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Ρ„Π°ΠΉΠ»ΠΎΠ²ΠΎΠΉ систСмы + cancelFileTransferInitialization() { + try { + if (this.fileTransferSystem) { + this.fileTransferSystem.cleanup(); + this.fileTransferSystem = null; + this._secureLog('info', '⏹️ File transfer initialization cancelled'); + return true; + } + return false; + } catch (error) { + this._secureLog('error', '❌ Failed to cancel file transfer initialization:', { + errorType: error?.constructor?.name || 'Unknown' + }); return false; } } + + // CRITICAL FIX: Validate nested encryption security + _validateNestedEncryptionSecurity() { + if (this.securityFeatures.hasNestedEncryption && this.nestedEncryptionKey) { + // Test that we can generate fresh random IVs + try { + const testIV1 = crypto.getRandomValues(new Uint8Array(EnhancedSecureWebRTCManager.SIZES.NESTED_ENCRYPTION_IV_SIZE)); + const testIV2 = crypto.getRandomValues(new Uint8Array(EnhancedSecureWebRTCManager.SIZES.NESTED_ENCRYPTION_IV_SIZE)); + + // Verify IVs are different (very unlikely to be the same with 96-bit random) + if (testIV1.every((byte, index) => byte === testIV2[index])) { + console.error('❌ CRITICAL: Nested encryption IV generation failed - IVs are identical!'); + return false; + } + + console.log('βœ… Nested encryption security validation passed - fresh random IVs generated'); + return true; + } catch (error) { + console.error('❌ CRITICAL: Nested encryption security validation failed:', error); + return false; + } + } + return true; + } } class SecureKeyStorage { @@ -7409,6 +7547,15 @@ class SecureKeyStorage { // Master encryption key for storage encryption this._storageMasterKey = null; this._initializeStorageMaster(); + + // CRITICAL FIX: Validate storage integrity after initialization + setTimeout(() => { + if (!this.validateStorageIntegrity()) { + console.error('❌ CRITICAL: Key storage integrity check failed'); + } + }, 100); + + // CRITICAL FIX: Storage integrity validation only (nested encryption validation is in main class) } async _initializeStorageMaster() { @@ -7443,6 +7590,11 @@ class SecureKeyStorage { // For extractable keys, proceed with encryption const keyData = await crypto.subtle.exportKey('jwk', cryptoKey); const encryptedKeyData = await this._encryptKeyData(keyData); + + // CRITICAL FIX: Validate that extractable keys are properly encrypted + if (!encryptedKeyData || encryptedKeyData.byteLength === 0) { + throw new Error('Failed to encrypt extractable key data'); + } // Create a storage object const storageObject = { @@ -7465,7 +7617,9 @@ class SecureKeyStorage { this._keyMetadata.set(keyId, { ...metadata, created: Date.now(), - lastAccessed: Date.now() + lastAccessed: Date.now(), + extractable: true, + encrypted: true // CRITICAL FIX: Mark extractable keys as encrypted }); return true; @@ -7486,7 +7640,18 @@ class SecureKeyStorage { // For non-encrypted keys (non-extractable), return directly if (!metadata.encrypted) { - return this._keyReferences.get(keyId); + // CRITICAL FIX: Only non-extractable keys should be non-encrypted + if (metadata.extractable === false) { + return this._keyReferences.get(keyId); + } else { + // This should never happen - extractable keys must be encrypted + this._secureLog('error', '❌ SECURITY VIOLATION: Extractable key marked as non-encrypted', { + keyId, + extractable: metadata.extractable, + encrypted: metadata.encrypted + }); + return null; + } } // For encrypted keys, decrypt and recreate @@ -7593,6 +7758,38 @@ class SecureKeyStorage { } } + // CRITICAL FIX: Validate storage integrity + validateStorageIntegrity() { + const violations = []; + + for (const [keyId, metadata] of this._keyMetadata.entries()) { + // Check: extractable keys must be encrypted + if (metadata.extractable === true && metadata.encrypted !== true) { + violations.push({ + keyId, + type: 'EXTRACTABLE_KEY_NOT_ENCRYPTED', + metadata + }); + } + + // Check: non-extractable keys should not be encrypted + if (metadata.extractable === false && metadata.encrypted === true) { + violations.push({ + keyId, + type: 'NON_EXTRACTABLE_KEY_ENCRYPTED', + metadata + }); + } + } + + if (violations.length > 0) { + console.error('❌ Storage integrity violations detected:', violations); + return false; + } + + return true; + } + getStorageStats() { return { totalKeys: this._keyReferences.size,