HMAC verification failed for page nnnn

I have a signal-desktop sqlite database that was running on Ubuntu 22.04. I backed up the entire signal folder (~/.config/Signal) after quitting Signal, upgraded my OS to Ubuntu 24.04 (clean install), restored my backup. When I try to open Signal, it throws the following error:

2024-09-06 15:41:34.201: ERROR CORE sqlcipher_page_cipher: hmac check failed for pgno=1
2024-09-06 15:41:34.201: ERROR CORE sqlite3Codec: error decrypting page 1 data: 1
2024-09-06 15:41:34.201: ERROR CORE sqlcipher_codec_ctx_set_error 1
{"level":40,"time":"2024-09-06T22:41:34.201Z","msg":"MainSQL: Database log code=26: file is not a database in \"PRAGMA journal_mode = WAL\""}
{"level":30,"time":"2024-09-06T22:41:34.201Z","msg":"MainSQL: migrateDatabase: Migration without cipher change failed"}
2024-09-06 15:41:34.235: ERROR CORE sqlcipher_page_cipher: hmac check failed for pgno=1
2024-09-06 15:41:34.235: ERROR CORE sqlite3Codec: error decrypting page 1 data: 1
2024-09-06 15:41:34.235: ERROR CORE sqlcipher_codec_ctx_set_error 1
{"level":40,"time":"2024-09-06T22:41:34.235Z","msg":"MainSQL: Database log code=26: statement aborts at 2: [PRAGMA user_version] file is not a database"}
{"level":50,"time":"2024-09-06T22:41:34.236Z","msg":"MainSQL: Database startup error: SqliteError: file is not a database\n    at Database.pragma ([REDACTED]/node_modules/@signalapp/better-sqlite3/lib/methods/pragma.js:11:31)\n    at getUserVersion ([REDACTED]/ts/sql/util.js:132:13)\n    at migrateSchemaVersion ([REDACTED]/ts/sql/Server.js:397:54)\n    at openAndMigrateDatabase ([REDACTED]/ts/sql/Server.js:429:5)\n    at openAndSetUpSQLCipher ([REDACTED]/ts/sql/Server.js:451:14)\n    at initialize ([REDACTED]/ts/sql/Server.js:489:10)\n    at MessagePort.<anonymous> ([REDACTED]/ts/sql/mainWorker.js:69:41)\n    at [nodejs.internal.kHybridDispatch] (node:internal/event_target:820:20)\n    at MessagePort.<anonymous> (node:internal/per_context/messageport:23:28)"}
{"level":50,"time":"2024-09-06T22:41:34.236Z","msg":"Failed to get zoom factor {\"name\":\"SqliteError\"}"}
{"level":30,"time":"2024-09-06T22:41:34.539Z","msg":"got fast theme-setting value system"}
{"level":50,"time":"2024-09-06T22:41:34.978Z","msg":"sql.initialize was unsuccessful; returning early"}
{"level":30,"time":"2024-09-06T22:41:34.978Z","msg":"close event {\"readyForShutdown\":false,\"shouldQuit\":false}"}
{"level":30,"time":"2024-09-06T22:41:34.979Z","msg":"maybeRequestCloseConfirmation: Checking to see if close confirmation is needed"}
{"level":50,"time":"2024-09-06T22:41:36.515Z","msg":"onDatabaseError: Quitting application"}
{"level":30,"time":"2024-09-06T22:41:36.516Z","msg":"main window closed event"}
{"level":30,"time":"2024-09-06T22:41:36.517Z","msg":"quit event {\"hasEventBeenPrevented\":false,\"windowCount\":0,\"mainWindowExists\":false}"}
{"level":50,"time":"2024-09-06T22:41:36.518Z","msg":"Error occurred in handler for 'sql-channel:read': {\"name\":\"SqliteError\"}"}

I attempted to open the ~/.config/Signal/sql/db.sqlite database using SQLCipher. After specifying the key using PRAGMA key = "x'{very long string of letters and numbers}'", if I run .tables, I get the following error:

2024-09-06 15:44:08.714: sqlcipher_page_cipher: hmac check failed for pgno=1
2024-09-06 15:44:08.714: sqlite3Codec: error decrypting page 1 data: 1
2024-09-06 15:44:08.714: sqlcipher_codec_ctx_set_error 1
Error: file is not a database

If I run PRAGMA cipher_integrity_check, it throws several HMAC verification failed for page nnnn.

Does this mean the database is corrupt? Is there any way I can recover the db? Any advise would very much appreciated.

It was an issue with the key. I corrected that, and now I’m able to access the database using sqlcipher. However, Signal still throws the following:

{"level":40,"time":"2024-09-07T03:47:13.760Z","msg":"MainSQL: Database log code=283: recovered 359 frames from WAL file [REDACTED]/sql/db.sqlite-wal"}
{"level":40,"time":"2024-09-07T03:47:13.765Z","msg":"MainSQL: Database log code=11: database corruption at line 72957 of [c9c2ab54ba]"}
{"level":40,"time":"2024-09-07T03:47:13.765Z","msg":"MainSQL: Database log code=11: database corruption at line 73107 of [c9c2ab54ba]"}
{"level":40,"time":"2024-09-07T03:47:13.765Z","msg":"MainSQL: Database log code=11: statement aborts at 11: [] database disk image is malformed"}
{"level":30,"time":"2024-09-07T03:47:13.765Z","msg":"MainSQL: updateSchema:\n  Current user_version: 1150;\n  Most recent db schema: 1170;\n  SQLite version: 3.46.1;\n  SQLCipher version: 4.6.1 community;\n  (deprecated) schema_version: 490;\n"}
{"level":30,"time":"2024-09-07T03:47:13.767Z","msg":"got fast theme-setting value system"}
{"level":30,"time":"2024-09-07T03:47:13.768Z","msg":"got fast spellcheck setting true"}
{"level":30,"time":"2024-09-07T03:47:13.768Z","msg":"Initializing BrowserWindow config: {\"show\":false,\"width\":1850,\"height\":1016,\"minWidth\":300,\"minHeight\":200,\"autoHideMenuBar\":true,\"titleBarStyle\":\"default\",\"backgroundColor\":\"#121212\",\"webPreferences\":{\"devTools\":false,\"spellcheck\":true,\"enableBlinkFeatures\":\"CSSPseudoDir,CSSLogical\",\"enablePreferredSizeMode\":true,\"nodeIntegration\":false,\"nodeIntegrationInWorker\":false,\"sandbox\":false,\"contextIsolation\":true,\"preload\":\"[REDACTED]/preload.bundle.js\",\"backgroundThrottling\":true,\"disableBlinkFeatures\":\"Accelerated2dCanvas,AcceleratedSmallCanvases\"},\"icon\":\"[REDACTED]/images/signal-logo-desktop-linux.png\",\"x\":70,\"y\":1144}"}
{"level":30,"time":"2024-09-07T03:47:13.950Z","msg":"spellcheck: user locales: [\"en-US\",\"en\"]"}
{"level":30,"time":"2024-09-07T03:47:13.950Z","msg":"spellcheck: available spellchecker languages: [\"af\",\"bg\",\"ca\",\"cs\",\"cy\",\"da\",\"de\",\"de-DE\",\"el\",\"en\",\"en-AU\",\"en-CA\",\"en-GB\",\"en-GB-oxendict\",\"en-US\",\"es\",\"es-419\",\"es-AR\",\"es-ES\",\"es-MX\",\"es-US\",\"et\",\"fa\",\"fo\",\"fr\",\"fr-FR\",\"he\",\"hi\",\"hr\",\"hu\",\"hy\",\"id\",\"it\",\"it-IT\",\"ko\",\"lt\",\"lv\",\"nb\",\"nl\",\"pl\",\"pt\",\"pt-BR\",\"pt-PT\",\"ro\",\"ru\",\"sh\",\"sk\",\"sl\",\"sq\",\"sr\",\"sv\",\"ta\",\"tg\",\"tr\",\"uk\",\"vi\"]"}
{"level":30,"time":"2024-09-07T03:47:13.950Z","msg":"spellcheck: setting languages to: [\"en-US\",\"en\"]"}
{"level":30,"time":"2024-09-07T03:47:13.950Z","msg":"MainSQL: updateToSchemaVersion1160: success!"}
{"level":40,"time":"2024-09-07T03:47:13.950Z","msg":"MainSQL: Database log code=11: database corruption at line 76284 of [c9c2ab54ba]"}
{"level":40,"time":"2024-09-07T03:47:13.950Z","msg":"MainSQL: Database log code=11: statement aborts at 26: [DROP INDEX IF EXISTS messages_callHistory_markReadBefore;] database disk image is malformed"}
{"level":50,"time":"2024-09-07T03:47:13.950Z","msg":"MainSQL: Database startup error: SqliteError: database disk image is malformed\n    at Database.exec ([REDACTED]/node_modules/@signalapp/better-sqlite3/lib/methods/wrappers.js:9:14)\n    at [REDACTED]/ts/sql/migrations/1170-update-call-history-unread-index.js:39:8\n    at sqliteTransaction ([REDACTED]/node_modules/@signalapp/better-sqlite3/lib/methods/transaction.js:65:24)\n    at updateToSchemaVersion1170 ([REDACTED]/ts/sql/migrations/1170-update-call-history-unread-index.js:41:5)\n    at updateSchema ([REDACTED]/ts/sql/migrations/index.js:1824:5)\n    at initialize ([REDACTED]/ts/sql/Server.js:494:42)\n    at MessagePort.<anonymous> ([REDACTED]/ts/sql/mainWorker.js:69:41)\n    at [nodejs.internal.kHybridDispatch] (node:internal/event_target:820:20)\n    at MessagePort.<anonymous> (node:internal/per_context/messageport:23:28)"}
{"level":50,"time":"2024-09-07T03:47:14.008Z","msg":"Failed to get zoom factor {\"name\":\"SqliteError\"}"}
{"level":30,"time":"2024-09-07T03:47:14.372Z","msg":"got fast theme-setting value system"}
{"level":50,"time":"2024-09-07T03:47:14.852Z","msg":"sql.initialize was unsuccessful; returning early"}
{"level":30,"time":"2024-09-07T03:47:14.853Z","msg":"close event {\"readyForShutdown\":false,\"shouldQuit\":false}"}
{"level":30,"time":"2024-09-07T03:47:14.853Z","msg":"maybeRequestCloseConfirmation: Checking to see if close confirmation is needed"}
{"level":50,"time":"2024-09-07T03:47:18.309Z","msg":"onDatabaseError: Quitting application"}
{"level":30,"time":"2024-09-07T03:47:18.311Z","msg":"main window closed event"}
{"level":30,"time":"2024-09-07T03:47:18.312Z","msg":"quit event {\"hasEventBeenPrevented\":false,\"windowCount\":0,\"mainWindowExists\":false}"}
{"level":50,"time":"2024-09-07T03:47:18.315Z","msg":"Error occurred in handler for 'sql-channel:read': {\"name\":\"SqliteError\"}"}

It seems there is some corruption in the database. Running pragma integrity_check; outputs the following:

*** in database main ***
Tree 3 page 3 cell 26: Child page depth differs
Tree 3 page 3 cell 25: Child page depth differs
2nd reference to page 49481
Page 141: never used
Runtime error: database disk image is malformed (11)

Any suggestions on how to fix this?

bump

Any advice would be greatly appreciated. Thank you.

Hello @ms3318 - thanks for getting in touch. I’m sorry to hear about the trouble. While Signal does use SQLCipher, we don’t support it as an application.

It sounds like the database is corrupted. If you want to try to recover your data, you could try to use the .recover command (see Recovering Data From A Corrupt SQLite Database) to get data out.

Otherwise, I would contact Signal for support on how to reset the database.

Thank you for your response.

Would you be so kind as to provide me the steps to perform a recover on an encrypted db? I assume I cannot just use sqlite3 to attempt a .recover since the database is encrypted. Attempting to run a .recover within an interactive sqlcipher session results in a

sqlite> .recover
2024-09-12 21:38:28.242: sqlcipher_page_cipher: hmac check failed for pgno=1
2024-09-12 21:38:28.242: sqlite3Codec: error decrypting page 1 data: 1
2024-09-12 21:38:28.242: sqlcipher_codec_ctx_set_error 1
sql error: file is not a database (26)

However, within the same interactive session, if I run a .tables command, I can see the list of tables, which seems to indicate that I entered the correct key. In addition, a .dump works, although unlike recover, it quits somewhere half way through when it runs into an error.

Hello @ms3318 - we’ve improved support for running .recover directly on an encrypted database in SQLCipher 4.7.0.

In order to perform recovery on a database, you must configure the entire first page of the database to be plaintext, by specifying a PRAGMA cipher_plaintext_header_size equal to the full non-reserved page size (e.g. 4016 bytes for a 4096 byte page size with HMAC-SHA512 enabled). This makes a permanent change to the database, so make a copy of the database before doing so.

Assuming you have a SQLCipher 4.7.0 shell compiled with recovery extension support, here is an example:

    $ cp corrupted.db corrupted.temp
    $ sqlcipher corrupted.temp
    sqlite> PRAGMA key = '...'; -- key database
    sqlite> SELECT count(*) FROM sqlite_master; -- load header and schema (must be intact)
    sqlite> PRAGMA cipher_plaintext_header_size = 4016; -- set full non-reserved page size as plaintext
    sqlite> PRAGMA user_version = 1; -- force 1st page write
    sqlite> PRAGMA cipher_salt; -- record salt value for future use
    sqlite> .out recover.sql -- specify output file for recovery
    sqlite> .recover
    sqlite> .quit

The recoverable contents would be written to recover.sql, which could then be loaded back into a new database for use.