Skip to content

CommunicationsSecurity & malware

Signal on Android: signal.db, Keys and Backups

Signal on Android: the SQLCipher signal.db, how its key is wrapped by the Android Keystore, encrypted attachments, .backup files and what survives outside.

Location
/data/data/org.thoughtcrime.securesms/databases/signal.db
Proves
Signal conversations, message text, senders, direction, sent, received and read times, calls, attachments and group membership, once the database is decrypted
Timestamps
Unix milliseconds (UTC): message.date_sent, date_received, date_server; call.timestamp; thread.date
Access
Full file system (root, exploit or agent), AFU only, plus the Android Keystore key on the same device to unwrap the database secret. Logical: only user-created .backup files. Not in ADB backup.
Retention
Until the user deletes messages or chats, disappearing-message timers expire, or the app is removed; local .backup files rotate (usually the two newest are kept)
Collection
Full file system extraction with on-device key unwrapping, adb pull of the user-selected backup folder, Signal device transfer (user)

What it is

Signal (org.thoughtcrime.securesms) encrypts messages end to end like WhatsApp, but unlike WhatsApp it also encrypts its local database. signal.db is a SQLCipher database: every page is AES-encrypted with a random 32-byte secret that Signal generated at install. That secret is stored in the app's preferences, and on every supported Android version it is itself encrypted by a key in the Android Keystore, which on most phones lives in hardware (TEE or StrongBox) and never leaves the device.

The practical result: copying signal.db off the phone, even from a full file system extraction, gives you ciphertext. To read it you need the unwrapped database secret, which can only be obtained by asking the Keystore on that same, running device (usually through privileged code executed during extraction). Commercial tools that support Signal do exactly this on supported models.

Where it lives

ItemPathNotes
Database/data/data/org.thoughtcrime.securesms/databases/signal.db (+ -wal, -shm)SQLCipher, not readable with plain sqlite3
Wrapped secrets/data/data/org.thoughtcrime.securesms/shared_prefs/org.thoughtcrime.securesms_preferences.xmlDatabase secret and attachment secret, each encrypted with a Keystore key
Other databasesdatabases/signal-key-value.db, signal-logs.db, jobmanager.db, megaphone.dbAlso encrypted with the same scheme on current versions
Attachments/data/data/org.thoughtcrime.securesms/app_parts/Encrypted files, one per attachment
Avatars, stickersapp_avatars/, app_stickers/Encrypted
Local backupsUser-chosen folder (often /sdcard/Signal/Backups/ or a folder picked via the storage framework)signal-YYYY-MM-DD-HH-MM-SS.backup, encrypted with a 30-digit passphrase

Signal also offers device-to-device transfer and, on recent versions, encrypted server-side backups. Both need the user's cooperation or credentials and are outside the handset's file system.

What it proves

Once decrypted:

  • Every conversation (thread) and its recipient or group, with group membership (groups).
  • Messages with text, direction, sender, sent time, server time and received time, plus read and delivery receipts.
  • Voice and video calls (call), reactions, quotes, edits and remote deletions.
  • Attachment metadata (content type, size, file name) linked to encrypted files in app_parts.
  • Disappearing-message settings per conversation and message expiry times.

Without decryption, the existence of Signal, its install and update times, its notifications and its usage are still provable from system artifacts.

Key fields

TableColumnsMeaning
message_id, thread_id, from_recipient_id, to_recipient_id, date_sent, date_received, date_server, type, body, read, expires_in, remote_deletedMessages (SMS and MMS tables were merged into message in 2023; older versions have sms and mms)
thread_id, recipient_id, date, snippet, read, archivedConversations
recipient_id, e164, aci, pni, username, system_joined_name, profile_given_namePhone numbers, Signal service IDs, names
attachment (older part)message_id, content_type, data_file, data_size, file_nameAttachment metadata and path in app_parts
callcall_id, message_id, peer, type, direction, event, timestampCalls

The type column is a bit field combining direction, delivery status and flags; decode it with the constants from the Signal source for the version you have.

Timestamps

Unix milliseconds in UTC. date_sent is the sender's clock, date_server the Signal server's receipt time, and date_received when this device stored the message.

-- After decryption to a plain SQLite copy
SELECT m._id, m.thread_id, r.e164, r.system_joined_name,
       datetime(m.date_sent/1000, 'unixepoch')     AS sent_utc,
       datetime(m.date_received/1000, 'unixepoch') AS received_utc,
       m.body
FROM message m
LEFT JOIN recipient r ON r._id = m.from_recipient_id
ORDER BY m.date_received;

Retention

Messages remain until deleted by the user, removed by a disappearing-message timer, or deleted for everyone by the sender. Signal enables secure delete behaviour in SQLCipher, so freelist carving yields far less than with unencrypted apps. Local .backup files, if enabled, are written on a schedule and older copies rotated.

Collection

  • Full file system extraction with key unwrapping on the device: the database, preferences and app_parts must be decrypted using that phone's Keystore while the phone is AFU. Verify your tool supports the exact Signal version and chipset.
  • Local backups: pull the .backup files and obtain the 30-digit passphrase (the user set it and it cannot be recovered from the phone). signalbackup-tools decrypts and exports them.
  • Screen capture: as a last resort, documented photography or screen recording of the unlocked app. Signal's Screen security blocks screenshots and recent-apps previews.

Parsing

  • signalbackup-tools decrypts a .backup file with its passphrase and exports it to a SQLite database, HTML or other formats.
  • SQLCipher (or sqlcipher builds of the SQLite CLI) opens signal.db once you have the raw key; the cipher settings depend on the Signal version, so take them from the app's source for that release.
  • Commercial suites combine the on-device unwrapping with a parser.

Investigator tips

  • If decryption is impossible, pivot to what Signal leaves outside its sandbox: notification history (unless the user hides message content in notifications), usagestats, and recent tasks snapshots when screen security is off.
  • Check whether Screen security and notification content were enabled: they decide which of those pivots can exist.
  • A Signal PIN or registration lock is not the database key and does not decrypt anything local.
  • See the messaging apps guide for how Signal compares with WhatsApp and Telegram.

See also