Security Update
This commit is contained in:
@@ -1,6 +1,20 @@
|
|||||||
// Import EnhancedSecureFileTransfer
|
// Import EnhancedSecureFileTransfer
|
||||||
import { EnhancedSecureFileTransfer } from '../transfer/EnhancedSecureFileTransfer.js';
|
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
|
// 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
|
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)
|
// REMOVED: Fake DTLS ClientHello validation (security theater)
|
||||||
static DTLS_PROTECTION = {
|
// REASON: Browser WebRTC doesn't provide access to DTLS layer in JavaScript
|
||||||
SUPPORTED_CIPHERS: [
|
//
|
||||||
'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256',
|
// REAL SECURITY: Use these mechanisms instead:
|
||||||
'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384',
|
// 1. Out-of-band key fingerprint verification (SAS/QR codes)
|
||||||
'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256',
|
// 2. SDP fingerprint validation (not "client hello" data)
|
||||||
'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'
|
// 3. Public key pinning if known in advance
|
||||||
],
|
// 4. Certificate transparency validation
|
||||||
MIN_TLS_VERSION: '1.2',
|
//
|
||||||
MAX_TLS_VERSION: '1.3',
|
// static DTLS_PROTECTION = {
|
||||||
CLIENTHELLO_TIMEOUT: 5000, // 5 seconds
|
// SUPPORTED_CIPHERS: [...], // REMOVED: Fake cipher validation
|
||||||
ICE_VERIFICATION_TIMEOUT: 3000 // 3 seconds
|
// 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 = {}) {
|
constructor(onMessage, onStatusChange, onKeyExchange, onVerificationRequired, onAnswerError = null, config = {}) {
|
||||||
// Determine runtime mode
|
// Determine runtime mode
|
||||||
@@ -193,6 +210,11 @@ class EnhancedSecureWebRTCManager {
|
|||||||
this.onMessage = onMessage;
|
this.onMessage = onMessage;
|
||||||
this.onStatusChange = onStatusChange;
|
this.onStatusChange = onStatusChange;
|
||||||
this.onKeyExchange = onKeyExchange;
|
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.onVerificationRequired = onVerificationRequired;
|
||||||
this.onAnswerError = onAnswerError; // Callback for response processing errors
|
this.onAnswerError = onAnswerError; // Callback for response processing errors
|
||||||
this.isInitiator = false;
|
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.maxOldKeys = EnhancedSecureWebRTCManager.LIMITS.MAX_OLD_KEYS; // Keep last 3 key versions for decryption
|
||||||
this.peerConnection = null;
|
this.peerConnection = null;
|
||||||
this.dataChannel = null;
|
this.dataChannel = null;
|
||||||
// DTLS Race Condition Protection
|
// SECURITY: DTLS protection removed - was security theater
|
||||||
this.verifiedICEEndpoints = new Set(); // Верифицированные ICE endpoints
|
// this.verifiedICEEndpoints = new Set(); // REMOVED: Fake endpoint verification
|
||||||
this.dtlsClientHelloQueue = new Map(); // Очередь DTLS ClientHello сообщений
|
// this.dtlsClientHelloQueue = new Map(); // REMOVED: Fake DTLS queue
|
||||||
this.iceVerificationInProgress = false; // Флаг процесса ICE верификации
|
// this.iceVerificationInProgress = false; // REMOVED: Fake ICE verification
|
||||||
this.dtlsProtectionEnabled = true; // Включена ли защита от DTLS race condition
|
// this.dtlsProtectionEnabled = true; // REMOVED: Fake DTLS protection
|
||||||
|
|
||||||
|
|
||||||
this.securityFeatures = {
|
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
|
// SECURE LOGGING SYSTEM
|
||||||
@@ -8340,13 +8253,37 @@ async processMessage(data) {
|
|||||||
'ECDSA'
|
'ECDSA'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Validate exported data
|
// CRITICAL: Strict validation of exported data with hard disconnect on failure
|
||||||
if (!ecdhPublicKeyData?.keyData || !ecdhPublicKeyData?.signature) {
|
// - Any validation failure in critical security path must abort connection
|
||||||
throw new Error('Failed to export ECDH public key with signature');
|
// - 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) {
|
if (!ecdhPublicKeyData.keyData || !ecdhPublicKeyData.signature) {
|
||||||
throw new Error('Failed to export ECDSA public key with 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();
|
this.verificationCode = window.EnhancedSecureCryptoUtils.generateVerificationCode();
|
||||||
|
|
||||||
// Validate verification code
|
// Validate verification code
|
||||||
@@ -8830,31 +8773,18 @@ async processMessage(data) {
|
|||||||
this._throwSecureError(error, 'ecdsa_key_import');
|
this._throwSecureError(error, 'ecdsa_key_import');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify ECDSA key self-signature
|
// SECURITY: Self-signature verification removed - was security theater
|
||||||
const ecdsaPackageCopy = { ...offerData.ecdsaPublicKey };
|
//
|
||||||
delete ecdsaPackageCopy.signature;
|
// PROBLEM: Self-signed ECDSA keys don't provide authentication
|
||||||
const ecdsaPackageString = JSON.stringify(ecdsaPackageCopy);
|
// MITM can substitute both keys and "self-sign" them
|
||||||
|
//
|
||||||
const ecdsaSignatureValid = await window.EnhancedSecureCryptoUtils.verifySignature(
|
// REAL SECURITY: Use out-of-band verification instead:
|
||||||
peerECDSAPublicKey,
|
// - SAS (Short Authentication String) comparison
|
||||||
offerData.ecdsaPublicKey.signature,
|
// - QR code fingerprint verification
|
||||||
ecdsaPackageString
|
// - Pre-shared public key fingerprints
|
||||||
);
|
// - Certificate transparency validation
|
||||||
|
//
|
||||||
if (!ecdsaSignatureValid) {
|
// Note: ECDSA signature only proves packet integrity, not identity
|
||||||
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
|
|
||||||
});
|
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// PHASE 6: IMPORT AND VERIFY ECDH KEY
|
// PHASE 6: IMPORT AND VERIFY ECDH KEY
|
||||||
@@ -9077,13 +9007,37 @@ async processMessage(data) {
|
|||||||
'ECDSA'
|
'ECDSA'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Validate exported data
|
// CRITICAL: Strict validation of exported data with hard disconnect on failure
|
||||||
if (!ecdhPublicKeyData?.keyData || !ecdhPublicKeyData?.signature) {
|
// - Any validation failure in critical security path must abort connection
|
||||||
throw new Error('Failed to export ECDH public key with signature');
|
// - 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) {
|
if (!ecdhPublicKeyData.keyData || !ecdhPublicKeyData.signature) {
|
||||||
throw new Error('Failed to export ECDSA public key with 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) {
|
async handleSecureAnswer(answerData) {
|
||||||
try {
|
try {
|
||||||
if (!answerData || answerData.type !== 'enhanced_secure_answer' || !answerData.sdp) {
|
// CRITICAL: Strict validation of answer data to prevent syntax errors
|
||||||
throw new Error('Invalid response format');
|
// - 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
|
// CRITICAL: Strict validation of ECDH public key structure
|
||||||
if (!answerData.ecdhPublicKey || !answerData.ecdhPublicKey.keyData) {
|
if (!answerData.ecdhPublicKey || typeof answerData.ecdhPublicKey !== 'object' || Array.isArray(answerData.ecdhPublicKey)) {
|
||||||
throw new Error('Missing ECDH public key data');
|
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
|
// CRITICAL: Strict validation of ECDSA public key structure
|
||||||
if (!answerData.ecdsaPublicKey || !answerData.ecdsaPublicKey.keyData) {
|
if (!answerData.ecdsaPublicKey || typeof answerData.ecdsaPublicKey !== 'object' || Array.isArray(answerData.ecdsaPublicKey)) {
|
||||||
throw new Error('Missing ECDSA public key data for signature verification');
|
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
|
// Additional MITM protection: Validate answer data structure
|
||||||
@@ -9479,28 +9476,18 @@ async processMessage(data) {
|
|||||||
['verify']
|
['verify']
|
||||||
);
|
);
|
||||||
|
|
||||||
// Verify ECDSA key's self-signature
|
// SECURITY: Self-signature verification removed - was security theater
|
||||||
const ecdsaPackageCopy = { ...answerData.ecdsaPublicKey };
|
//
|
||||||
delete ecdsaPackageCopy.signature;
|
// PROBLEM: Self-signed ECDSA keys don't provide authentication
|
||||||
const ecdsaPackageString = JSON.stringify(ecdsaPackageCopy);
|
// MITM can substitute both keys and "self-sign" them
|
||||||
const ecdsaSignatureValid = await window.EnhancedSecureCryptoUtils.verifySignature(
|
//
|
||||||
peerECDSAPublicKey,
|
// REAL SECURITY: Use out-of-band verification instead:
|
||||||
answerData.ecdsaPublicKey.signature,
|
// - SAS (Short Authentication String) comparison
|
||||||
ecdsaPackageString
|
// - QR code fingerprint verification
|
||||||
);
|
// - Pre-shared public key fingerprints
|
||||||
|
// - Certificate transparency validation
|
||||||
if (!ecdsaSignatureValid) {
|
//
|
||||||
window.EnhancedSecureCryptoUtils.secureLog.log('error', 'Invalid ECDSA signature detected - possible MITM attack', {
|
// Note: ECDSA signature only proves packet integrity, not identity
|
||||||
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
|
|
||||||
});
|
|
||||||
|
|
||||||
// Now import and verify the ECDH public key using the verified ECDSA key
|
// Now import and verify the ECDH public key using the verified ECDSA key
|
||||||
const peerPublicKey = await window.EnhancedSecureCryptoUtils.importPublicKeyFromSignedPackage(
|
const peerPublicKey = await window.EnhancedSecureCryptoUtils.importPublicKeyFromSignedPackage(
|
||||||
@@ -9543,31 +9530,16 @@ async processMessage(data) {
|
|||||||
// Store peer's public key for PFS key rotation
|
// Store peer's public key for PFS key rotation
|
||||||
this.peerPublicKey = peerPublicKey;
|
this.peerPublicKey = peerPublicKey;
|
||||||
|
|
||||||
// ✅ ДОБАВИТЬ: Проверка DTLS защиты перед генерацией ключей
|
// SECURITY: DTLS protection removed - was security theater
|
||||||
if (this.dtlsProtectionEnabled) {
|
//
|
||||||
// Имитируем проверку DTLS ClientHello (в реальном WebRTC это происходит автоматически)
|
// REAL SECURITY: Implement proper key verification instead:
|
||||||
const mockClientHelloData = {
|
// - Out-of-band fingerprint verification (SAS/QR codes)
|
||||||
cipherSuite: 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256',
|
// - SDP certificate fingerprint validation
|
||||||
tlsVersion: '1.3'
|
// - Public key pinning if known in advance
|
||||||
};
|
// - Certificate transparency validation
|
||||||
|
//
|
||||||
// Получаем endpoint из peer connection
|
// Note: Browser WebRTC handles DTLS automatically
|
||||||
const localEndpoint = this.peerConnection?.localDescription?.sdp || 'local-endpoint';
|
// JavaScript cannot access DTLS layer for validation
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const derivedKeys = await window.EnhancedSecureCryptoUtils.deriveSharedKeys(
|
const derivedKeys = await window.EnhancedSecureCryptoUtils.deriveSharedKeys(
|
||||||
this.ecdhKeyPair.privateKey,
|
this.ecdhKeyPair.privateKey,
|
||||||
@@ -9686,11 +9658,18 @@ async processMessage(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initiateVerification() {
|
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) {
|
if (this.isInitiator) {
|
||||||
// Ensure verification initiation notice wasn't already sent
|
// Ensure verification initiation notice wasn't already sent
|
||||||
if (!this.verificationInitiationSent) {
|
if (!this.verificationInitiationSent) {
|
||||||
this.verificationInitiationSent = true;
|
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 {
|
} else {
|
||||||
// Responder confirms verification automatically if codes match
|
// Responder confirms verification automatically if codes match
|
||||||
@@ -9699,12 +9678,19 @@ async processMessage(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
confirmVerification() {
|
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 {
|
try {
|
||||||
const verificationPayload = {
|
const verificationPayload = {
|
||||||
type: 'verification',
|
type: 'verification',
|
||||||
data: {
|
data: {
|
||||||
code: this.verificationCode,
|
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
|
// Ensure verification success notice wasn't already sent
|
||||||
if (!this.verificationNotificationSent) {
|
if (!this.verificationNotificationSent) {
|
||||||
this.verificationNotificationSent = true;
|
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();
|
this.processMessageQueue();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._secureLog('error', '❌ Verification failed:', { errorType: error?.constructor?.name || 'Unknown' });
|
this._secureLog('error', '❌ SAS verification failed:', { errorType: error?.constructor?.name || 'Unknown' });
|
||||||
this.deliverMessageToUI('❌ Verification failed', 'system');
|
this.deliverMessageToUI('❌ SAS verification failed', 'system');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleVerificationRequest(data) {
|
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) {
|
if (data.code === this.verificationCode) {
|
||||||
|
// ✅ SAS verification successful - MITM protection confirmed
|
||||||
const responsePayload = {
|
const responsePayload = {
|
||||||
type: 'verification_response',
|
type: 'verification_response',
|
||||||
data: {
|
data: {
|
||||||
verified: true,
|
verified: true,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now(),
|
||||||
|
verificationMethod: 'SAS', // Indicate SAS was used
|
||||||
|
securityLevel: 'MITM_PROTECTED'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.dataChannel.send(JSON.stringify(responsePayload));
|
this.dataChannel.send(JSON.stringify(responsePayload));
|
||||||
@@ -9741,30 +9735,56 @@ async processMessage(data) {
|
|||||||
// Ensure verification success notice wasn't already sent
|
// Ensure verification success notice wasn't already sent
|
||||||
if (!this.verificationNotificationSent) {
|
if (!this.verificationNotificationSent) {
|
||||||
this.verificationNotificationSent = true;
|
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();
|
this.processMessageQueue();
|
||||||
} else {
|
} 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();
|
this.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleVerificationResponse(data) {
|
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) {
|
if (data.verified) {
|
||||||
|
// ✅ Peer has verified our SAS code - mutual verification complete
|
||||||
this.isVerified = true;
|
this.isVerified = true;
|
||||||
this.onStatusChange('connected');
|
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
|
// Ensure verification success notice wasn't already sent
|
||||||
if (!this.verificationNotificationSent) {
|
if (!this.verificationNotificationSent) {
|
||||||
this.verificationNotificationSent = true;
|
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();
|
this.processMessageQueue();
|
||||||
} else {
|
} 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();
|
this.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9781,11 +9801,20 @@ async processMessage(data) {
|
|||||||
offerData.salt.length === 32;
|
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) {
|
validateEnhancedOfferData(offerData) {
|
||||||
try {
|
try {
|
||||||
if (!offerData || typeof offerData !== 'object') {
|
// CRITICAL: Strict type checking to prevent syntax errors
|
||||||
throw new Error('Offer data must be an object');
|
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
|
// Basic required fields for all versions
|
||||||
@@ -9828,18 +9857,48 @@ async processMessage(data) {
|
|||||||
throw new Error('Offer is too old (older than 1 hour)');
|
throw new Error('Offer is too old (older than 1 hour)');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate key structures (more lenient)
|
// CRITICAL: Strict validation of key structures to prevent syntax errors
|
||||||
if (!offerData.ecdhPublicKey || typeof offerData.ecdhPublicKey !== 'object') {
|
if (!offerData.ecdhPublicKey || typeof offerData.ecdhPublicKey !== 'object' || Array.isArray(offerData.ecdhPublicKey)) {
|
||||||
throw new Error('Invalid ECDH public key structure');
|
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') {
|
if (!offerData.ecdsaPublicKey || typeof offerData.ecdsaPublicKey !== 'object' || Array.isArray(offerData.ecdsaPublicKey)) {
|
||||||
throw new Error('Invalid ECDSA public key structure');
|
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) {
|
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', {
|
this._secureLog('info', 'v4.0 offer validation passed', {
|
||||||
@@ -9849,6 +9908,7 @@ async processMessage(data) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// v3.0 backward compatibility validation
|
// v3.0 backward compatibility validation
|
||||||
|
// NOTE: v3.0 has limited security - SAS verification is still critical
|
||||||
const v3RequiredFields = ['publicKey', 'salt', 'verificationCode'];
|
const v3RequiredFields = ['publicKey', 'salt', 'verificationCode'];
|
||||||
for (const field of v3RequiredFields) {
|
for (const field of v3RequiredFields) {
|
||||||
if (!offerData[field]) {
|
if (!offerData[field]) {
|
||||||
@@ -9879,10 +9939,18 @@ async processMessage(data) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
window.EnhancedSecureCryptoUtils.secureLog.log('error', 'Offer validation failed', {
|
// CRITICAL: Security validation errors must be logged and result in hard abort
|
||||||
error: error.message
|
// - 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}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user