improve: add detection and enhanced logging for corrupted Signal sessions
Adds comprehensive handling for Signal Protocol decryption errors including Bad MAC and MessageCounterError which indicate corrupted/desynchronized sessions. Changes: - Add BAD_MAC_ERROR_TEXT constant for error detection - Extend DECRYPTION_RETRY_CONFIG with corruptedSessionErrors array - Add NACK_REASONS.CorruptedSession (553) for protocol-level reporting - Add isCorruptedSessionError() utility function - Enhanced error logging to differentiate corrupted sessions from other errors - Include decryptionJid in error context for easier debugging Impact: - Better visibility into session corruption issues - Clearer logs with ⚠️ warning emoji for corrupted sessions - Foundation for future automatic session cleanup on corruption - Helps diagnose and resolve "Bad MAC" errors in production Related to PR #135 (session cleanup) - provides detection layer that complements the preventive session cleanup already implemented. https://claude.ai/code/session_01SoNUGBEWbJwWWws3F2fuzh
This commit is contained in:
@@ -51,12 +51,14 @@ const storeMappingFromEnvelope = async (
|
||||
|
||||
export const NO_MESSAGE_FOUND_ERROR_TEXT = 'Message absent from node'
|
||||
export const MISSING_KEYS_ERROR_TEXT = 'Key used already or never filled'
|
||||
export const BAD_MAC_ERROR_TEXT = 'Bad MAC'
|
||||
|
||||
// Retry configuration for failed decryption
|
||||
export const DECRYPTION_RETRY_CONFIG = {
|
||||
maxRetries: 3,
|
||||
baseDelayMs: 100,
|
||||
sessionRecordErrors: ['No session record', 'SessionError: No session record']
|
||||
sessionRecordErrors: ['No session record', 'SessionError: No session record'],
|
||||
corruptedSessionErrors: ['Bad MAC', 'MessageCounterError', MISSING_KEYS_ERROR_TEXT]
|
||||
}
|
||||
|
||||
export const NACK_REASONS = {
|
||||
@@ -72,7 +74,8 @@ export const NACK_REASONS = {
|
||||
UnhandledError: 500,
|
||||
UnsupportedAdminRevoke: 550,
|
||||
UnsupportedLIDGroup: 551,
|
||||
DBOperationFailed: 552
|
||||
DBOperationFailed: 552,
|
||||
CorruptedSession: 553
|
||||
}
|
||||
|
||||
type MessageType =
|
||||
@@ -326,16 +329,28 @@ export const decryptMessageNode = (
|
||||
fullMessage.message = msg
|
||||
}
|
||||
} catch (err: any) {
|
||||
const isCorrupted = isCorruptedSessionError(err)
|
||||
const isSessionRecord = isSessionRecordError(err)
|
||||
|
||||
const errorContext = {
|
||||
key: fullMessage.key,
|
||||
err,
|
||||
messageType: tag === 'plaintext' ? 'plaintext' : attrs.type,
|
||||
sender,
|
||||
author,
|
||||
isSessionRecordError: isSessionRecordError(err)
|
||||
decryptionJid,
|
||||
isSessionRecordError: isSessionRecord,
|
||||
isCorruptedSession: isCorrupted
|
||||
}
|
||||
|
||||
logger.error(errorContext, 'failed to decrypt message')
|
||||
if (isCorrupted) {
|
||||
logger.error(
|
||||
errorContext,
|
||||
'⚠️ Corrupted session detected - Bad MAC or MessageCounter error. Session may need to be recreated.'
|
||||
)
|
||||
} else {
|
||||
logger.error(errorContext, 'failed to decrypt message')
|
||||
}
|
||||
|
||||
fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT
|
||||
fullMessage.messageStubParameters = [err.message.toString()]
|
||||
@@ -359,3 +374,12 @@ function isSessionRecordError(error: any): boolean {
|
||||
const errorMessage = error?.message || error?.toString() || ''
|
||||
return DECRYPTION_RETRY_CONFIG.sessionRecordErrors.some(errorPattern => errorMessage.includes(errorPattern))
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to check if an error indicates a corrupted session
|
||||
* (Bad MAC, MessageCounterError, Key already used)
|
||||
*/
|
||||
export function isCorruptedSessionError(error: any): boolean {
|
||||
const errorMessage = error?.message || error?.toString() || ''
|
||||
return DECRYPTION_RETRY_CONFIG.corruptedSessionErrors.some(errorPattern => errorMessage.includes(errorPattern))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user