Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sqlite-ctrsee

A small command-line tool that decrypts (or encrypts, for test purposes) a SQLite database that uses a non-standard, per-page AES counter-mode construction — one that is sometimes labeled "CCM" by the software that produced it, but is not actually compliant RFC 3610 CCM.

Why this exists

SQLite's official Encryption Extension (SEE) is commercial software sold by the SQLite maintainer (Hwaci). It documents a handful of supported ciphers: AES-128-OFB, AES-256-OFB, AES-128-CCM, AES-256-GCM, and RC4. If you already know the raw database key (for example, because you extracted it yourself from a device or application you have the legal right to access), you don't need a commercial SEE license to open the file — you just need something that reimplements the same page-cipher math against the raw key, the same way sqlite3_key_v2() would.

SqliteSEECLI already does this well for the OFB and RC4 modes. It explicitly does not support CCM or GCM.

This tool fills a narrower, adjacent gap: some real-world software labels its SQLite encryption as "CCM" (and lays pages out with a CCM-shaped tag+nonce region) while actually using a much simpler, non-compliant construction — a single AES-ECB block encryption of a per-page nonce with an embedded little-endian counter, XORed directly against the page content. This is not real CCM (no authentication tag is verified, and the "counter" only touches a 4-byte sub-field of the nonce, not a fully-specified CCM counter block). It looks like a from-scratch, simplified reimplementation of counter mode by someone who labeled it "CCM" out of convenience, not a licensed SEE build.

This tool implements that construction so files using it can be decrypted (or, symmetrically, re-encrypted) once you already have the key.

No vendor source code, proprietary key material, or device-identifying information is included in this repository. The construction described here was independently derived by analyzing an embedded device's proprietary backup format; this repository intentionally does not name the device or software it came from, since the algorithm itself — not its origin — is the reusable part. You must supply your own key, obtained legally from a file or device you have the right to access.

How the cipher works

Each page is page_size bytes, laid out as:

content [0 : page_size - reserve)                       ciphertext, in 16-byte AES blocks
tag     [page_size - reserve : page_size - nonce_size)   unverified / unused by this tool
nonce   [page_size - nonce_size : page_size)             unique per page

Content block i (0-indexed, 16 bytes each) decrypts as:

counter_i   = (nonce[counter_offset : counter_offset + counter_width] as uint, little-endian) + i
keystream_i = AES_ECB_encrypt(key, nonce with that field replaced by counter_i)
plaintext_i = keystream_i XOR ciphertext_i

Because XOR is its own inverse, the exact same routine encrypts and decrypts — see --mode below.

The default --page-size (4096), --reserve-size (32), --tag-size (16), --nonce-size (16), --counter-offset (4), and --counter-width (4) match one real-world instance of this construction. They are ordinary parameters, not identifying fingerprints — pass different values on the command line if your file uses a different layout.

Installation

pip install pycryptodome

No other dependencies. Tested with Python 3.9+.

Usage

Decrypt a file once you know the raw key:

python3 sqlite_ctrsee.py encrypted.db -o decrypted.db --key "your-ascii-key-here"

Or supply the key as hex or from a file:

python3 sqlite_ctrsee.py encrypted.db -o decrypted.db --key-hex 30313233343536373839616263646566
python3 sqlite_ctrsee.py encrypted.db -o decrypted.db --key-file ./key.bin

If the source file corrupts the standard SQLite header fields at offset 16–24 (some implementations stash proprietary metadata there instead of the real page_size/version/reserve fields SQLite expects), add --fix-header to detect and repair just that span so the output opens in stock sqlite3:

python3 sqlite_ctrsee.py encrypted.db -o decrypted.db --key "..." --fix-header

--fix-header only touches those bytes if they don't already look canonical — it won't silently overwrite a file that doesn't need it.

Non-default page layout:

python3 sqlite_ctrsee.py encrypted.db -o decrypted.db --key "..." \
  --page-size 4096 --reserve-size 48 --tag-size 32 --nonce-size 16 \
  --counter-offset 0 --counter-width 4

Encrypt mode

--mode encrypt runs the identical transform in the other direction. This is provided to build synthetic test vectors (see build_test_vector.py) — it will not produce a file compatible with whatever real software you're targeting unless you also replicate its own nonce-generation scheme (this tool does not attempt to guess or reproduce that).

Testing

python3 build_test_vector.py   # writes testdata/sample.plain.db and testdata/sample.encrypted.db
python3 test_roundtrip.py      # unit tests against synthetic, randomly generated page content

All test fixtures are generated synthetically at test time — nothing here is extracted from a real proprietary file. Note the tests validate the cryptographic transform itself (encrypt/decrypt round-trip, wrong-key rejection, reserve-region preservation); they do not attempt to fabricate a byte-accurate "real" SQLite database with non-zero reserved bytes, since Python's standard sqlite3 module always writes reserved_bytes=0 and doesn't expose a way to override that without a real codec. If you have legitimate access to a real file using this construction, that's the only way to fully validate --fix-header and end-to-end openability in sqlite3.

Legal note

This tool is an independent reimplementation of an observed cryptographic construction, in the same spirit as SqliteSEECLI's reimplementation of the OFB/RC4 modes: raw-key access via something equivalent to sqlite3_key_v2() is a documented, public API surface, and no commercial SEE license or vendor source code is required to implement compatible page decryption once the key is known. This repository does not include, and has never included, any proprietary key extracted from any specific device or product — you are responsible for supplying your own key and for ensuring you have the legal right to decrypt the file you're using it on.

License

MIT — see LICENSE.

About

Decrypt SQLite databases using a non-standard per-page AES counter-mode construction mislabeled as CCM. Complements SqliteSEECLI for the CCM gap it doesn't cover.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages