Privacy Tools Guide

Modern messaging apps like Signal use forward secrecy protocols that automatically rotate encryption keys with each message, so you won’t lose chat history. To manually rotate your safety numbers in Signal, simply reinstall the app or add a new device—the app automatically regenerates new identity keys while keeping your local message history intact. For custom implementations, implement the Double Ratchet Algorithm which rotates keys per-message while storing decryption material for old messages.

Understanding the Key Rotation Challenge

End-to-end encrypted messaging apps typically use the Signal Protocol or similar cryptographic frameworks. These protocols generate new key pairs for each device and periodically create new session keys. When you reinstall an app or switch devices, the app must re-establish secure sessions with your contacts.

The core challenge: old messages were encrypted with old keys. If those keys are no longer available after rotation, decryption becomes impossible without proper key management.

Rotating Keys in Signal

Signal provides the most mature key rotation mechanism while preserving history. Here’s how it works:

Safety Number Rotation

Signal automatically rotates safety numbers when you reinstall or add a new device. To manually trigger a verification after rotation:

  1. Open the conversation in Signal
  2. Tap the conversation header → “View safety number”
  3. Verify the new fingerprint through a trusted channel

Device Re-Installation

When you reinstall Signal on a new device:

# Export encrypted backup (if enabled)
# On Android: Settings → Chat and media → Backup
# On iOS: Settings → Chats → Backup

# After reinstall, Signal will prompt to restore from backup
# Messages remain decryptable because the backup includes
# necessary key material, encrypted with your backup password

The backup file contains encrypted session states. Your messages aren’t lost—they’re just re-associated with new session keys that you can restore from the backup.

Rotating Keys in WhatsApp

WhatsApp handles key rotation differently. Here’s the practical approach:

Account Transfer Method

WhatsApp doesn’t store chat history on their servers—the encryption keys live only on your device. To rotate keys without losing history:

  1. Create a local backup (WhatsApp automatically does this daily on iOS/Android)
  2. Uninstall WhatsApp or clear data
  3. Reinstall WhatsApp and restore from backup
# Android: Google Drive backup
# iOS: iCloud backup (enable in Settings → Chats → Chat Backup)

# After reinstall:
# WhatsApp → Enter phone number → Restore from backup

Manual Key Verification

After restoration, verify your contact’s identity:

# WhatsApp uses AES-GCM for message encryption
# Each message has a unique message key derived from session keys
# After reinstall, you'll see "Number changed" notification
# This indicates new key generation—re-verify safety numbers

Custom Implementation: Building Key Rotation for Developers

For developers building encrypted messaging systems, here’s a practical approach to key rotation with history preservation:

Key Hierarchy Design

import os
import hashlib
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import x25519
from cryptography.hazmat.primitives.kdf.hkdf import HKDF

class KeyRotationManager:
    def __init__(self):
        self.root_key = os.urandom(32)
        self.message_keys = {}  # message_id -> key

    def derive_message_key(self, root_key, message_index):
        """HKDF-based message key derivation"""
        hkdf = HKDF(
            algorithm=hashes.SHA256(),
            length=32,
            salt=b'message-key-salt',
            info=f'message-{message_index}'.encode()
        )
        return hkdf.derive(root_key)

    def rotate_session_key(self, old_root_key):
        """Generate new root key while maintaining key hierarchy"""
        # Use key ratcheting: new key depends on old key
        hkdf = HKDF(
            algorithm=hashes.SHA256(),
            length=32,
            salt=b'root-rotation-salt',
            info=b'session-rotation'
        )
        new_root_key = hkdf.derive(old_root_key + os.urandom(32))

        # Store mapping for old message decryption
        return new_root_key

Preserving Chat History During Rotation

class SecureMessageStore:
    def __init__(self):
        self.messages = []
        self.key_archive = {}  # Store old keys for decryption

    def rotate_keys(self, new_public_key):
        """Rotate encryption keys while preserving history"""
        old_messages = self.messages

        # Archive old session key material (not the messages themselves)
        # These enable decryption of old messages
        self.key_archive['previous_session'] = {
            'messages': old_messages,  # Already encrypted with old keys
            'session_id': self.current_session_id
        }

        # Start new session with fresh keys
        self.current_session_id = os.urandom(16)
        self.messages = []

        return {
            'status': 'rotated',
            'archived_messages_count': len(old_messages)
        }

    def decrypt_message(self, encrypted_data, session_context):
        """Decrypt messages using appropriate session key"""
        if session_context == 'previous_session':
            # Use archived key material
            return self._decrypt_with_archive(encrypted_data)
        else:
            return self._decrypt_current(encrypted_data)

Best Practices for Key Rotation

1. Implement Forward Secrecy

Your system should generate new keys for each session or message, ensuring that compromising one key doesn’t expose historical messages:

# Double Ratchet pattern (simplified)
class DoubleRatchet:
    def __init__(self, shared_secret):
        self.root_key = shared_secret
        self.chain_key = shared_secret

    def ratchet_message(self):
        """Advance chain for each message (symmetric ratchet)"""
        self.chain_key = hashlib.sha256(self.chain_key + b'message').digest()
        message_key = hashlib.sha256(self.chain_key + b'key').digest()
        return message_key

2. Use Key Derivation Functions Properly

Never store raw keys. Use HKDF or similar KDFs to derive session keys from master secrets:

from cryptography.hazmat.primitives.kdf.hkdf import HKDF

def derive_session_key(master_secret, purpose, counter):
    return HKDF(
        algorithm=hashes.SHA256(),
        length=32,
        salt=purpose.encode(),
        info=str(counter).encode()
    ).derive(master_secret)

3. Maintain Key Archives Securely

Archive old key material with the same protection as active keys. Consider:

4. Communicate Rotation Events to Users

Always notify users when key rotation occurs—unexpected notifications indicate potential man-in-the-middle attacks:

Signal: "Safety number changed" notification
WhatsApp: "Number changed" for new device installations
Custom apps: Display explicit "Key rotated" banner with verification option

Built by theluckystrike — More at zovo.one