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)
Tools
Compare all toolsWhat 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
| Item | Path | Notes |
|---|---|---|
| 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.xml | Database secret and attachment secret, each encrypted with a Keystore key |
| Other databases | databases/signal-key-value.db, signal-logs.db, jobmanager.db, megaphone.db | Also encrypted with the same scheme on current versions |
| Attachments | /data/data/org.thoughtcrime.securesms/app_parts/ | Encrypted files, one per attachment |
| Avatars, stickers | app_avatars/, app_stickers/ | Encrypted |
| Local backups | User-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
| Table | Columns | Meaning |
|---|---|---|
message | _id, thread_id, from_recipient_id, to_recipient_id, date_sent, date_received, date_server, type, body, read, expires_in, remote_deleted | Messages (SMS and MMS tables were merged into message in 2023; older versions have sms and mms) |
thread | _id, recipient_id, date, snippet, read, archived | Conversations |
recipient | _id, e164, aci, pni, username, system_joined_name, profile_given_name | Phone numbers, Signal service IDs, names |
attachment (older part) | message_id, content_type, data_file, data_size, file_name | Attachment metadata and path in app_parts |
call | call_id, message_id, peer, type, direction, event, timestamp | Calls |
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_partsmust 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
.backupfiles and obtain the 30-digit passphrase (the user set it and it cannot be recovered from the phone).signalbackup-toolsdecrypts 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
.backupfile with its passphrase and exports it to a SQLite database, HTML or other formats. - SQLCipher (or
sqlcipherbuilds of the SQLite CLI) openssignal.dbonce 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.