Security Update

This commit is contained in:
lockbitchat
2025-09-01 15:15:42 -04:00
parent 14b001710a
commit a04fc16d58

View File

@@ -1,6 +1,20 @@
// Import EnhancedSecureFileTransfer
import { EnhancedSecureFileTransfer } from '../transfer/EnhancedSecureFileTransfer.js';
// ============================================
// CRITICAL SECURITY WARNING
// ============================================
//
// MITM PROTECTION: Self-signed ECDSA keys DO NOT provide authentication!
// - MITM can substitute both keys and "self-sign" them
// - ECDSA signatures only prove packet integrity, not identity
// - SAS (Short Authentication String) verification is the ONLY protection
//
// REQUIREMENT: Both parties MUST verify the same code out-of-band:
// - Voice call, video call, or in-person verification
// - Compare verification codes before allowing traffic
// - No traffic should be allowed before SAS verification
//
// ============================================
// MUTEX SYSTEM FIXES - RESOLVING MESSAGE DELIVERY ISSUES
// ============================================
@@ -103,22 +117,25 @@ class EnhancedSecureWebRTCManager {
static DEBUG_MODE = false; // Set to true during development, false in production
// ============================================
// DTLS CLIENTHELLO RACE CONDITION PROTECTION
// SECURITY WARNING: DTLS PROTECTION REMOVED
// ============================================
// Защита от DTLS ClientHello race condition (октябрь 2024)
static DTLS_PROTECTION = {
SUPPORTED_CIPHERS: [
'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256',
'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384',
'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256',
'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'
],
MIN_TLS_VERSION: '1.2',
MAX_TLS_VERSION: '1.3',
CLIENTHELLO_TIMEOUT: 5000, // 5 seconds
ICE_VERIFICATION_TIMEOUT: 3000 // 3 seconds
};
//
// REMOVED: Fake DTLS ClientHello validation (security theater)
// REASON: Browser WebRTC doesn't provide access to DTLS layer in JavaScript
//
// REAL SECURITY: Use these mechanisms instead:
// 1. Out-of-band key fingerprint verification (SAS/QR codes)
// 2. SDP fingerprint validation (not "client hello" data)
// 3. Public key pinning if known in advance
// 4. Certificate transparency validation
//
// static DTLS_PROTECTION = {
// SUPPORTED_CIPHERS: [...], // REMOVED: Fake cipher validation
// MIN_TLS_VERSION: '1.2', // REMOVED: Fake TLS version check
// MAX_TLS_VERSION: '1.3', // REMOVED: Fake TLS version check
// CLIENTHELLO_TIMEOUT: 5000, // REMOVED: Fake timeout
// ICE_VERIFICATION_TIMEOUT: 3000 // REMOVED: Fake timeout
// };
constructor(onMessage, onStatusChange, onKeyExchange, onVerificationRequired, onAnswerError = null, config = {}) {
// Determine runtime mode
@@ -193,6 +210,11 @@ class EnhancedSecureWebRTCManager {
this.onMessage = onMessage;
this.onStatusChange = onStatusChange;
this.onKeyExchange = onKeyExchange;
// CRITICAL: SAS verification callback - this is the ONLY MITM protection
// - Self-signed ECDSA keys don't provide authentication
// - MITM can substitute both keys and "self-sign" them
// - SAS must be compared out-of-band (voice, video, in-person)
// - Both parties must verify the same code before allowing traffic
this.onVerificationRequired = onVerificationRequired;
this.onAnswerError = onAnswerError; // Callback for response processing errors
this.isInitiator = false;
@@ -332,11 +354,11 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
this.maxOldKeys = EnhancedSecureWebRTCManager.LIMITS.MAX_OLD_KEYS; // Keep last 3 key versions for decryption
this.peerConnection = null;
this.dataChannel = null;
// DTLS Race Condition Protection
this.verifiedICEEndpoints = new Set(); // Верифицированные ICE endpoints
this.dtlsClientHelloQueue = new Map(); // Очередь DTLS ClientHello сообщений
this.iceVerificationInProgress = false; // Флаг процесса ICE верификации
this.dtlsProtectionEnabled = true; // Включена ли защита от DTLS race condition
// SECURITY: DTLS protection removed - was security theater
// this.verifiedICEEndpoints = new Set(); // REMOVED: Fake endpoint verification
// this.dtlsClientHelloQueue = new Map(); // REMOVED: Fake DTLS queue
// this.iceVerificationInProgress = false; // REMOVED: Fake ICE verification
// this.dtlsProtectionEnabled = true; // REMOVED: Fake DTLS protection
this.securityFeatures = {
@@ -1904,154 +1926,45 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
}
// ============================================
// DTLS CLIENTHELLO RACE CONDITION PROTECTION
// SECURITY: DTLS FUNCTIONS REMOVED
// ============================================
//
// REMOVED: validateDTLSSource() - was security theater
// REASON: Browser WebRTC doesn't provide DTLS layer access
//
// REAL SECURITY: Implement proper key verification instead:
// - Out-of-band fingerprint verification (SAS/QR)
// - SDP certificate fingerprint validation
// - Public key pinning
// - Certificate transparency checks
//
/**
* REMOVED: addVerifiedICEEndpoint() - was part of fake DTLS protection
*
* DTLS protection ClientHello race condition
* REAL SECURITY: Use proper endpoint verification:
* - ICE candidate validation
* - SDP integrity checks
* - Certificate fingerprint validation
*/
async validateDTLSSource(clientHelloData, expectedSource) {
try {
if (!this.verifiedICEEndpoints.has(expectedSource)) {
this._secureLog('error', 'DTLS ClientHello from unverified source - possible race condition attack', {
source: expectedSource,
verifiedEndpoints: Array.from(this.verifiedICEEndpoints),
timestamp: Date.now()
});
throw new Error('DTLS ClientHello from unverified source - possible race condition attack');
}
if (!clientHelloData.cipherSuite ||
!EnhancedSecureWebRTCManager.DTLS_PROTECTION.SUPPORTED_CIPHERS.includes(clientHelloData.cipherSuite)) {
this._secureLog('error', 'Invalid cipher suite in ClientHello', {
receivedCipher: clientHelloData.cipherSuite,
supportedCiphers: EnhancedSecureWebRTCManager.DTLS_PROTECTION.SUPPORTED_CIPHERS
});
throw new Error('Invalid cipher suite in ClientHello');
}
if (clientHelloData.tlsVersion) {
const version = clientHelloData.tlsVersion;
if (version < EnhancedSecureWebRTCManager.DTLS_PROTECTION.MIN_TLS_VERSION ||
version > EnhancedSecureWebRTCManager.DTLS_PROTECTION.MAX_TLS_VERSION) {
this._secureLog('error', 'Unsupported TLS version in ClientHello', {
receivedVersion: version,
minVersion: EnhancedSecureWebRTCManager.DTLS_PROTECTION.MIN_TLS_VERSION,
maxVersion: EnhancedSecureWebRTCManager.DTLS_PROTECTION.MAX_TLS_VERSION
});
throw new Error('Unsupported TLS version in ClientHello');
}
}
this._secureLog('info', 'DTLS ClientHello validation passed', {
source: expectedSource,
cipherSuite: clientHelloData.cipherSuite,
tlsVersion: clientHelloData.tlsVersion
});
return true;
} catch (error) {
this._secureLog('error', 'DTLS ClientHello validation failed', {
error: error.message,
source: expectedSource,
timestamp: Date.now()
});
throw error;
}
}
/**
* Adds ICE endpoint to the list of verified ones
* REMOVED: handleDTLSClientHello() - was part of fake DTLS protection
*
* REAL SECURITY: Browser handles DTLS automatically
* - No JavaScript access to DTLS layer
* - Focus on application-level security
* - Implement proper key verification
*/
addVerifiedICEEndpoint(endpoint) {
this.verifiedICEEndpoints.add(endpoint);
this._secureLog('info', 'ICE endpoint verified and added to DTLS protection', {
endpoint: endpoint,
totalVerified: this.verifiedICEEndpoints.size
});
}
/**
* Handles DTLS ClientHello with race condition protection
* REMOVED: completeICEVerification() - was part of fake DTLS protection
*
* REAL SECURITY: ICE verification happens automatically in WebRTC
* - Browser handles ICE candidate validation
* - Focus on application-level security measures
* - Implement proper connection verification
*/
async handleDTLSClientHello(clientHelloData, sourceEndpoint) {
try {
if (this.iceVerificationInProgress) {
this.dtlsClientHelloQueue.set(sourceEndpoint, {
data: clientHelloData,
timestamp: Date.now(),
attempts: 0
});
this._secureLog('warn', 'DTLS ClientHello queued - ICE verification in progress', {
source: sourceEndpoint,
queueSize: this.dtlsClientHelloQueue.size
});
return false;
}
// Validate the source of the DTLS packet
await this.validateDTLSSource(clientHelloData, sourceEndpoint);
this._secureLog('info', 'DTLS ClientHello processed successfully', {
source: sourceEndpoint,
cipherSuite: clientHelloData.cipherSuite
});
return true;
} catch (error) {
this._secureLog('error', 'DTLS ClientHello handling failed', {
error: error.message,
source: sourceEndpoint,
timestamp: Date.now()
});
this.verifiedICEEndpoints.delete(sourceEndpoint);
throw error;
}
}
/**
* Completes ICE verification and processes pending DTLS messages
*/
async completeICEVerification(verifiedEndpoints) {
try {
this.iceVerificationInProgress = false;
for (const endpoint of verifiedEndpoints) {
this.addVerifiedICEEndpoint(endpoint);
}
// Processing Deferred DTLS ClientHello Messages
for (const [endpoint, queuedData] of this.dtlsClientHelloQueue.entries()) {
try {
if (this.verifiedICEEndpoints.has(endpoint)) {
await this.handleDTLSClientHello(queuedData.data, endpoint);
this.dtlsClientHelloQueue.delete(endpoint);
}
} catch (error) {
this._secureLog('error', 'Failed to process queued DTLS ClientHello', {
endpoint: endpoint,
error: error.message
});
}
}
this._secureLog('info', 'ICE verification completed and DTLS queue processed', {
verifiedEndpoints: verifiedEndpoints.length,
processedQueue: this.dtlsClientHelloQueue.size
});
} catch (error) {
this._secureLog('error', 'ICE verification completion failed', {
error: error.message
});
throw error;
}
}
// ============================================
// SECURE LOGGING SYSTEM
@@ -8340,13 +8253,37 @@ async processMessage(data) {
'ECDSA'
);
// Validate exported data
if (!ecdhPublicKeyData?.keyData || !ecdhPublicKeyData?.signature) {
throw new Error('Failed to export ECDH public key with signature');
// CRITICAL: Strict validation of exported data with hard disconnect on failure
// - Any validation failure in critical security path must abort connection
// - No fallback allowed for cryptographic validation
// - Prevent bypass of security checks through syntax/validation errors
if (!ecdhPublicKeyData || typeof ecdhPublicKeyData !== 'object') {
this._secureLog('error', 'CRITICAL: ECDH key export failed - invalid object structure', { operationId });
throw new Error('CRITICAL SECURITY FAILURE: ECDH key export validation failed - hard abort required');
}
if (!ecdsaPublicKeyData?.keyData || !ecdsaPublicKeyData?.signature) {
throw new Error('Failed to export ECDSA public key with signature');
if (!ecdhPublicKeyData.keyData || !ecdhPublicKeyData.signature) {
this._secureLog('error', 'CRITICAL: ECDH key export incomplete - missing keyData or signature', {
operationId,
hasKeyData: !!ecdhPublicKeyData.keyData,
hasSignature: !!ecdhPublicKeyData.signature
});
throw new Error('CRITICAL SECURITY FAILURE: ECDH key export incomplete - hard abort required');
}
if (!ecdsaPublicKeyData || typeof ecdsaPublicKeyData !== 'object') {
this._secureLog('error', 'CRITICAL: ECDSA key export failed - invalid object structure', { operationId });
throw new Error('CRITICAL SECURITY FAILURE: ECDSA key export validation failed - hard abort required');
}
if (!ecdsaPublicKeyData.keyData || !ecdsaPublicKeyData.signature) {
this._secureLog('error', 'CRITICAL: ECDSA key export incomplete - missing keyData or signature', {
operationId,
hasKeyData: !!ecdsaPublicKeyData.keyData,
hasSignature: !!ecdsaPublicKeyData.signature
});
throw new Error('CRITICAL SECURITY FAILURE: ECDSA key export incomplete - hard abort required');
}
// ============================================
@@ -8414,10 +8351,16 @@ async processMessage(data) {
});
// ============================================
// PHASE 8: GENERATE VERIFICATION CODE
// PHASE 8: GENERATE SAS FOR OUT-OF-BAND VERIFICATION
// ============================================
// Generate verification code for out-of-band auth
//
// CRITICAL SECURITY: This is the ONLY way to prevent MITM attacks
// - Self-signed ECDSA keys don't provide authentication
// - MITM can substitute both keys and "self-sign" them
// - SAS must be compared out-of-band (voice, video, in-person)
// - Both parties must verify the same code before allowing traffic
//
// Generate verification code for out-of-band authentication
this.verificationCode = window.EnhancedSecureCryptoUtils.generateVerificationCode();
// Validate verification code
@@ -8830,31 +8773,18 @@ async processMessage(data) {
this._throwSecureError(error, 'ecdsa_key_import');
}
// Verify ECDSA key self-signature
const ecdsaPackageCopy = { ...offerData.ecdsaPublicKey };
delete ecdsaPackageCopy.signature;
const ecdsaPackageString = JSON.stringify(ecdsaPackageCopy);
const ecdsaSignatureValid = await window.EnhancedSecureCryptoUtils.verifySignature(
peerECDSAPublicKey,
offerData.ecdsaPublicKey.signature,
ecdsaPackageString
);
if (!ecdsaSignatureValid) {
this._secureLog('error', 'Invalid ECDSA signature detected - possible MITM attack', {
operationId: operationId,
timestamp: offerData.timestamp,
version: offerData.version
});
throw new Error('Invalid ECDSA key signature possible MITM attack');
}
this._secureLog('info', 'ECDSA signature verification passed', {
operationId: operationId,
timestamp: offerData.timestamp,
version: offerData.version
});
// SECURITY: Self-signature verification removed - was security theater
//
// PROBLEM: Self-signed ECDSA keys don't provide authentication
// MITM can substitute both keys and "self-sign" them
//
// REAL SECURITY: Use out-of-band verification instead:
// - SAS (Short Authentication String) comparison
// - QR code fingerprint verification
// - Pre-shared public key fingerprints
// - Certificate transparency validation
//
// Note: ECDSA signature only proves packet integrity, not identity
// ============================================
// PHASE 6: IMPORT AND VERIFY ECDH KEY
@@ -9077,13 +9007,37 @@ async processMessage(data) {
'ECDSA'
);
// Validate exported data
if (!ecdhPublicKeyData?.keyData || !ecdhPublicKeyData?.signature) {
throw new Error('Failed to export ECDH public key with signature');
// CRITICAL: Strict validation of exported data with hard disconnect on failure
// - Any validation failure in critical security path must abort connection
// - No fallback allowed for cryptographic validation
// - Prevent bypass of security checks through syntax/validation errors
if (!ecdhPublicKeyData || typeof ecdhPublicKeyData !== 'object') {
this._secureLog('error', 'CRITICAL: ECDH key export failed - invalid object structure', { operationId });
throw new Error('CRITICAL SECURITY FAILURE: ECDH key export validation failed - hard abort required');
}
if (!ecdsaPublicKeyData?.keyData || !ecdsaPublicKeyData?.signature) {
throw new Error('Failed to export ECDSA public key with signature');
if (!ecdhPublicKeyData.keyData || !ecdhPublicKeyData.signature) {
this._secureLog('error', 'CRITICAL: ECDH key export incomplete - missing keyData or signature', {
operationId,
hasKeyData: !!ecdhPublicKeyData.keyData,
hasSignature: !!ecdhPublicKeyData.signature
});
throw new Error('CRITICAL SECURITY FAILURE: ECDH key export incomplete - hard abort required');
}
if (!ecdsaPublicKeyData || typeof ecdsaPublicKeyData !== 'object') {
this._secureLog('error', 'CRITICAL: ECDSA key export failed - invalid object structure', { operationId });
throw new Error('CRITICAL SECURITY FAILURE: ECDSA key export validation failed - hard abort required');
}
if (!ecdsaPublicKeyData.keyData || !ecdsaPublicKeyData.signature) {
this._secureLog('error', 'CRITICAL: ECDSA key export incomplete - missing keyData or signature', {
operationId,
hasKeyData: !!ecdsaPublicKeyData.keyData,
hasSignature: !!ecdsaPublicKeyData.signature
});
throw new Error('CRITICAL SECURITY FAILURE: ECDSA key export incomplete - hard abort required');
}
// ============================================
@@ -9415,18 +9369,61 @@ async processMessage(data) {
async handleSecureAnswer(answerData) {
try {
if (!answerData || answerData.type !== 'enhanced_secure_answer' || !answerData.sdp) {
throw new Error('Invalid response format');
// CRITICAL: Strict validation of answer data to prevent syntax errors
// - Any validation failure in critical security path must abort connection
// - No fallback allowed for cryptographic validation
if (!answerData || typeof answerData !== 'object' || Array.isArray(answerData)) {
this._secureLog('error', 'CRITICAL: Invalid answer data structure', {
hasAnswerData: !!answerData,
answerDataType: typeof answerData,
isArray: Array.isArray(answerData)
});
throw new Error('CRITICAL SECURITY FAILURE: Answer data must be a non-null object');
}
if (answerData.type !== 'enhanced_secure_answer' || !answerData.sdp) {
this._secureLog('error', 'CRITICAL: Invalid answer format', {
type: answerData.type,
hasSdp: !!answerData.sdp
});
throw new Error('CRITICAL SECURITY FAILURE: Invalid answer format - hard abort required');
}
// Import peer's ECDH public key from the signed package
if (!answerData.ecdhPublicKey || !answerData.ecdhPublicKey.keyData) {
throw new Error('Missing ECDH public key data');
// CRITICAL: Strict validation of ECDH public key structure
if (!answerData.ecdhPublicKey || typeof answerData.ecdhPublicKey !== 'object' || Array.isArray(answerData.ecdhPublicKey)) {
this._secureLog('error', 'CRITICAL: Invalid ECDH public key structure in answer', {
hasEcdhKey: !!answerData.ecdhPublicKey,
ecdhKeyType: typeof answerData.ecdhPublicKey,
isArray: Array.isArray(answerData.ecdhPublicKey)
});
throw new Error('CRITICAL SECURITY FAILURE: Missing or invalid ECDH public key structure');
}
if (!answerData.ecdhPublicKey.keyData || !answerData.ecdhPublicKey.signature) {
this._secureLog('error', 'CRITICAL: ECDH key missing keyData or signature in answer', {
hasKeyData: !!answerData.ecdhPublicKey.keyData,
hasSignature: !!answerData.ecdhPublicKey.signature
});
throw new Error('CRITICAL SECURITY FAILURE: ECDH key missing keyData or signature');
}
// First, import and verify the ECDSA public key for signature verification
if (!answerData.ecdsaPublicKey || !answerData.ecdsaPublicKey.keyData) {
throw new Error('Missing ECDSA public key data for signature verification');
// CRITICAL: Strict validation of ECDSA public key structure
if (!answerData.ecdsaPublicKey || typeof answerData.ecdsaPublicKey !== 'object' || Array.isArray(answerData.ecdsaPublicKey)) {
this._secureLog('error', 'CRITICAL: Invalid ECDSA public key structure in answer', {
hasEcdsaKey: !!answerData.ecdsaPublicKey,
ecdsaKeyType: typeof answerData.ecdsaPublicKey,
isArray: Array.isArray(answerData.ecdsaPublicKey)
});
throw new Error('CRITICAL SECURITY FAILURE: Missing or invalid ECDSA public key structure');
}
if (!answerData.ecdsaPublicKey.keyData || !answerData.ecdsaPublicKey.signature) {
this._secureLog('error', 'CRITICAL: ECDSA key missing keyData or signature in answer', {
hasKeyData: !!answerData.ecdsaPublicKey.keyData,
hasSignature: !!answerData.ecdsaPublicKey.signature
});
throw new Error('CRITICAL SECURITY FAILURE: ECDSA key missing keyData or signature');
}
// Additional MITM protection: Validate answer data structure
@@ -9479,28 +9476,18 @@ async processMessage(data) {
['verify']
);
// Verify ECDSA key's self-signature
const ecdsaPackageCopy = { ...answerData.ecdsaPublicKey };
delete ecdsaPackageCopy.signature;
const ecdsaPackageString = JSON.stringify(ecdsaPackageCopy);
const ecdsaSignatureValid = await window.EnhancedSecureCryptoUtils.verifySignature(
peerECDSAPublicKey,
answerData.ecdsaPublicKey.signature,
ecdsaPackageString
);
if (!ecdsaSignatureValid) {
window.EnhancedSecureCryptoUtils.secureLog.log('error', 'Invalid ECDSA signature detected - possible MITM attack', {
timestamp: answerData.timestamp,
version: answerData.version
});
throw new Error('Invalid ECDSA key signature possible MITM attack');
}
window.EnhancedSecureCryptoUtils.secureLog.log('info', 'ECDSA signature verification passed', {
timestamp: answerData.timestamp,
version: answerData.version
});
// SECURITY: Self-signature verification removed - was security theater
//
// PROBLEM: Self-signed ECDSA keys don't provide authentication
// MITM can substitute both keys and "self-sign" them
//
// REAL SECURITY: Use out-of-band verification instead:
// - SAS (Short Authentication String) comparison
// - QR code fingerprint verification
// - Pre-shared public key fingerprints
// - Certificate transparency validation
//
// Note: ECDSA signature only proves packet integrity, not identity
// Now import and verify the ECDH public key using the verified ECDSA key
const peerPublicKey = await window.EnhancedSecureCryptoUtils.importPublicKeyFromSignedPackage(
@@ -9543,31 +9530,16 @@ async processMessage(data) {
// Store peer's public key for PFS key rotation
this.peerPublicKey = peerPublicKey;
// ✅ ДОБАВИТЬ: Проверка DTLS защиты перед генерацией ключей
if (this.dtlsProtectionEnabled) {
// Имитируем проверку DTLS ClientHello (в реальном WebRTC это происходит автоматически)
const mockClientHelloData = {
cipherSuite: 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256',
tlsVersion: '1.3'
};
// Получаем endpoint из peer connection
const localEndpoint = this.peerConnection?.localDescription?.sdp || 'local-endpoint';
const remoteEndpoint = this.peerConnection?.remoteDescription?.sdp || 'remote-endpoint';
// Добавляем endpoints в верифицированные
this.addVerifiedICEEndpoint(localEndpoint);
this.addVerifiedICEEndpoint(remoteEndpoint);
// Валидируем DTLS источник
await this.validateDTLSSource(mockClientHelloData, remoteEndpoint);
this._secureLog('info', 'DTLS protection validated before key derivation', {
localEndpoint: localEndpoint.substring(0, 50),
remoteEndpoint: remoteEndpoint.substring(0, 50),
verifiedEndpoints: this.verifiedICEEndpoints.size
});
}
// SECURITY: DTLS protection removed - was security theater
//
// REAL SECURITY: Implement proper key verification instead:
// - Out-of-band fingerprint verification (SAS/QR codes)
// - SDP certificate fingerprint validation
// - Public key pinning if known in advance
// - Certificate transparency validation
//
// Note: Browser WebRTC handles DTLS automatically
// JavaScript cannot access DTLS layer for validation
const derivedKeys = await window.EnhancedSecureCryptoUtils.deriveSharedKeys(
this.ecdhKeyPair.privateKey,
@@ -9686,11 +9658,18 @@ async processMessage(data) {
}
initiateVerification() {
// CRITICAL SECURITY: SAS verification initiation
// - This is the ONLY protection against MITM attacks
// - Self-signed ECDSA keys don't provide authentication
// - Both parties must compare the same verification code out-of-band
if (this.isInitiator) {
// Ensure verification initiation notice wasn't already sent
if (!this.verificationInitiationSent) {
this.verificationInitiationSent = true;
this.deliverMessageToUI('🔐 Confirm the security code with your peer to complete the connection', 'system');
this.deliverMessageToUI('🔐 CRITICAL: Compare verification code with peer out-of-band (voice/video/in-person) to prevent MITM attack!', 'system');
this.deliverMessageToUI(`🔐 Your verification code: ${this.verificationCode}`, 'system');
this.deliverMessageToUI('🔐 Ask peer to confirm this exact code before allowing traffic!', 'system');
}
} else {
// Responder confirms verification automatically if codes match
@@ -9699,12 +9678,19 @@ async processMessage(data) {
}
confirmVerification() {
// CRITICAL SECURITY: SAS verification confirmation
// - This sends our verification code to the peer
// - Peer must compare this code with their own out-of-band
// - Only after mutual verification is the connection MITM-protected
try {
const verificationPayload = {
type: 'verification',
data: {
code: this.verificationCode,
timestamp: Date.now()
timestamp: Date.now(),
verificationMethod: 'SAS',
securityLevel: 'MITM_PROTECTION_REQUIRED'
}
};
@@ -9715,23 +9701,31 @@ async processMessage(data) {
// Ensure verification success notice wasn't already sent
if (!this.verificationNotificationSent) {
this.verificationNotificationSent = true;
this.deliverMessageToUI('✅ Verification successful. The channel is now secure!', 'system');
this.deliverMessageToUI('✅ SAS verification code sent to peer. Wait for mutual verification to complete.', 'system');
}
this.processMessageQueue();
} catch (error) {
this._secureLog('error', '❌ Verification failed:', { errorType: error?.constructor?.name || 'Unknown' });
this.deliverMessageToUI('❌ Verification failed', 'system');
this._secureLog('error', '❌ SAS verification failed:', { errorType: error?.constructor?.name || 'Unknown' });
this.deliverMessageToUI('❌ SAS verification failed', 'system');
}
}
handleVerificationRequest(data) {
// CRITICAL SECURITY: SAS verification is the ONLY MITM protection
// - Self-signed ECDSA keys don't provide authentication
// - MITM can substitute both keys and "self-sign" them
// - This verification must happen out-of-band (voice, video, in-person)
if (data.code === this.verificationCode) {
// ✅ SAS verification successful - MITM protection confirmed
const responsePayload = {
type: 'verification_response',
data: {
verified: true,
timestamp: Date.now()
timestamp: Date.now(),
verificationMethod: 'SAS', // Indicate SAS was used
securityLevel: 'MITM_PROTECTED'
}
};
this.dataChannel.send(JSON.stringify(responsePayload));
@@ -9741,30 +9735,56 @@ async processMessage(data) {
// Ensure verification success notice wasn't already sent
if (!this.verificationNotificationSent) {
this.verificationNotificationSent = true;
this.deliverMessageToUI('✅ Verification successful. The channel is now secure!', 'system');
this.deliverMessageToUI('✅ SAS verification successful! MITM protection confirmed. Channel is now secure!', 'system');
}
this.processMessageQueue();
} else {
this.deliverMessageToUI('❌ Verification code mismatch! Possible MITM attack detected. Connection aborted for safety!', 'system');
// ❌ SAS verification failed - possible MITM attack
this._secureLog('error', 'SAS verification failed - possible MITM attack', {
receivedCode: data.code,
expectedCode: this.verificationCode,
timestamp: Date.now()
});
this.deliverMessageToUI('❌ SAS verification failed! Possible MITM attack detected. Connection aborted for safety!', 'system');
this.disconnect();
}
}
handleVerificationResponse(data) {
// CRITICAL SECURITY: SAS verification response handling
// - This confirms that the peer has verified our SAS code
// - Both parties must verify the same code out-of-band
// - Only after mutual SAS verification is the connection MITM-protected
if (data.verified) {
// ✅ Peer has verified our SAS code - mutual verification complete
this.isVerified = true;
this.onStatusChange('connected');
// Log successful mutual SAS verification
this._secureLog('info', 'Mutual SAS verification completed - MITM protection active', {
verificationMethod: data.verificationMethod || 'SAS',
securityLevel: data.securityLevel || 'MITM_PROTECTED',
timestamp: Date.now()
});
// Ensure verification success notice wasn't already sent
if (!this.verificationNotificationSent) {
this.verificationNotificationSent = true;
this.deliverMessageToUI('✅ Verification successful. The channel is now secure!', 'system');
this.deliverMessageToUI('✅ Mutual SAS verification complete! MITM protection active. Channel is now secure!', 'system');
}
this.processMessageQueue();
} else {
this.deliverMessageToUI('❌ Verification failed!', 'system');
// ❌ Peer verification failed - connection not secure
this._secureLog('error', 'Peer SAS verification failed - connection not secure', {
responseData: data,
timestamp: Date.now()
});
this.deliverMessageToUI('❌ Peer verification failed! Connection not secure!', 'system');
this.disconnect();
}
}
@@ -9781,11 +9801,20 @@ async processMessage(data) {
offerData.salt.length === 32;
}
// Enhanced validation with backward compatibility
// CRITICAL: Enhanced validation with strict security checks
// - Syntax errors in validation can break security flow
// - Any validation failure must result in hard disconnect
// - No fallback allowed for security-critical validation
validateEnhancedOfferData(offerData) {
try {
if (!offerData || typeof offerData !== 'object') {
throw new Error('Offer data must be an object');
// CRITICAL: Strict type checking to prevent syntax errors
if (!offerData || typeof offerData !== 'object' || Array.isArray(offerData)) {
this._secureLog('error', 'CRITICAL: Invalid offer data structure', {
hasOfferData: !!offerData,
offerDataType: typeof offerData,
isArray: Array.isArray(offerData)
});
throw new Error('CRITICAL SECURITY FAILURE: Offer data must be a non-null object');
}
// Basic required fields for all versions
@@ -9828,18 +9857,48 @@ async processMessage(data) {
throw new Error('Offer is too old (older than 1 hour)');
}
// Validate key structures (more lenient)
if (!offerData.ecdhPublicKey || typeof offerData.ecdhPublicKey !== 'object') {
throw new Error('Invalid ECDH public key structure');
// CRITICAL: Strict validation of key structures to prevent syntax errors
if (!offerData.ecdhPublicKey || typeof offerData.ecdhPublicKey !== 'object' || Array.isArray(offerData.ecdhPublicKey)) {
this._secureLog('error', 'CRITICAL: Invalid ECDH public key structure', {
hasEcdhKey: !!offerData.ecdhPublicKey,
ecdhKeyType: typeof offerData.ecdhPublicKey,
isArray: Array.isArray(offerData.ecdhPublicKey)
});
throw new Error('CRITICAL SECURITY FAILURE: Invalid ECDH public key structure - hard abort required');
}
if (!offerData.ecdsaPublicKey || typeof offerData.ecdsaPublicKey !== 'object') {
throw new Error('Invalid ECDSA public key structure');
if (!offerData.ecdsaPublicKey || typeof offerData.ecdsaPublicKey !== 'object' || Array.isArray(offerData.ecdsaPublicKey)) {
this._secureLog('error', 'CRITICAL: Invalid ECDSA public key structure', {
hasEcdsaKey: !!offerData.ecdsaPublicKey,
ecdsaKeyType: typeof offerData.ecdsaPublicKey,
isArray: Array.isArray(offerData.ecdsaPublicKey)
});
throw new Error('CRITICAL SECURITY FAILURE: Invalid ECDSA public key structure - hard abort required');
}
// Validate verification code format (more flexible)
// CRITICAL: Validate key internal structure to prevent syntax errors
if (!offerData.ecdhPublicKey.keyData || !offerData.ecdhPublicKey.signature) {
this._secureLog('error', 'CRITICAL: ECDH key missing keyData or signature', {
hasKeyData: !!offerData.ecdhPublicKey.keyData,
hasSignature: !!offerData.ecdhPublicKey.signature
});
throw new Error('CRITICAL SECURITY FAILURE: ECDH key missing keyData or signature');
}
if (!offerData.ecdsaPublicKey.keyData || !offerData.ecdsaPublicKey.signature) {
this._secureLog('error', 'CRITICAL: ECDSA key missing keyData or signature', {
hasKeyData: !!offerData.ecdsaPublicKey.keyData,
hasSignature: !!offerData.ecdsaPublicKey.signature
});
throw new Error('CRITICAL SECURITY FAILURE: ECDSA key missing keyData or signature');
}
// CRITICAL: Validate SAS verification code format
// - This code is the ONLY protection against MITM attacks
// - Self-signed ECDSA keys don't provide authentication
// - Code must be at least 6 characters for security
if (typeof offerData.verificationCode !== 'string' || offerData.verificationCode.length < 6) {
throw new Error('Invalid verification code format');
throw new Error('Invalid SAS verification code format - MITM protection required');
}
this._secureLog('info', 'v4.0 offer validation passed', {
@@ -9849,6 +9908,7 @@ async processMessage(data) {
});
} else {
// v3.0 backward compatibility validation
// NOTE: v3.0 has limited security - SAS verification is still critical
const v3RequiredFields = ['publicKey', 'salt', 'verificationCode'];
for (const field of v3RequiredFields) {
if (!offerData[field]) {
@@ -9879,10 +9939,18 @@ async processMessage(data) {
return true;
} catch (error) {
window.EnhancedSecureCryptoUtils.secureLog.log('error', 'Offer validation failed', {
error: error.message
// CRITICAL: Security validation errors must be logged and result in hard abort
// - No fallback or graceful handling for security-critical validation
// - Syntax errors in critical path must break connection immediately
this._secureLog('error', 'CRITICAL: Security validation failed - hard abort required', {
error: error.message,
errorType: error.constructor.name,
timestamp: Date.now()
});
return false; // Return false instead of throwing to allow graceful handling
// CRITICAL: Re-throw security validation errors to ensure hard abort
// Do NOT return false for security-critical validation failures
throw new Error(`CRITICAL SECURITY VALIDATION FAILURE: ${error.message}`);
}
}