Skip to content

Add KeepSafe (photo vault) support: vault items, albums, account/PIN security - #2026

Open
Gear-I wants to merge 1 commit into
abrignoni:mainfrom
Gear-I:KeepSafe
Open

Add KeepSafe (photo vault) support: vault items, albums, account/PIN security#2026
Gear-I wants to merge 1 commit into
abrignoni:mainfrom
Gear-I:KeepSafe

Conversation

@Gear-I

@Gear-I Gear-I commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a new keepsafe.py artifact module covering KeepSafe (com.keepsafe.KeepSafe), a "photo vault" app that copies user media into an app-private, encrypted store and hides it from the stock Photos app. KeepSafe is not currently covered by any artifact in this repo.

This module was written and validated against Josh Hickman's iOS 14.3 "thisisdfir" test image (KeepSafe 10.2.4) and additionally validated against a real device extraction, which surfaced and fixed a filename-parsing bug the test image alone did not exercise (see "Testing" below).

It adds three artifacts:

keepsafe_vault_items - every item KeepSafe's RocksDB store has a live record for, matched to its on-disk encrypted content file. Recovers the item's original (pre-vault) filename, content/added dates, album, MIME type, dimensions, dominant color, SHA-1 and byte count as KeepSafe itself recorded them for the original file, GPS coordinates, the original Photos.app asset id it was imported from, and tombstone (deleted) state.
keepsafe_albums - album/folder id-to-name mapping decoded from the AppGroup's NSKeyedArchiver-encoded shared-folders plist value.
keepsafe_account_security - account tracking id, PIN (stored in plain text), PIN type, invalid-PIN count, install/version metadata, and join/last-close dates from the app and AppGroup preference plists.
Why this needed custom handling

KeepSafe's Documents/rdb/ store is RocksDB, not SQLite or classic LevelDB. Its *.sst tables use a different on-disk format than the LevelDB tables scripts/ccl_leveldb.py's LdbFile/RawLevelDb reader supports (ValueError: Invalid magic number). This module works around that by using only ccl_leveldb.LogFile against the *.log write-ahead-log files, since RocksDB's WAL framing is unchanged from LevelDB's - no new dependency needed, but it means data already compacted out of the WAL into a .sst is invisible to this module (documented in the module docstring; item counts should be read as a floor, not a ceiling).

Record values are MessagePack-encoded. Rather than add a PyPI dependency for one module, I wrote a small decoder (_msgpack_unpack) for the specific type subset actually observed, validated byte-identical against the reference msgpack package across all 791 non-empty values in the test image.

Record keys have no field names on disk (KeepSafe is closed-source); the integer field mapping used here is inferred from value shape and cross-checked against ground truth where possible (e.g. recorded byte count vs. the file's actual on-disk size; SHA-1 magnitude class). Fields without a confident interpretation are left out rather than guessed. Full methodology and evidence-tier notes (data-proven vs. code-present-but-unexercised, e.g. the breakin_alert/fake vault tables which were present but empty in the test image) are documented in the module docstring.

Validation (KeepSafe 10.2.4, iOS 14.3, hickman_ios14 corpus)
keepsafe_vault_items: 5/5 rows recovered from the "primary" table; every item id matched a real on-disk file; recorded byte count and SHA-1 cross-checked against the on-disk file for all 5; 2 items carried a Photos-asset id, 2 carried non-zero GPS.
keepsafe_albums: 5/5 rows, every album id cross-referenced against the vault items in both directions.
keepsafe_account_security: 1 row, all fields populated including the plaintext PIN.
Multi-container scoping test (per the artifact contribution guidance): a synthetic second KeepSafe install was added to the test tree and confirmed to add exactly its own rows (2x, not 1x/3x) with no cross-contamination between installs.

Not covered (documented in the module docstring): Documents/accountlog/storageV3.data and commonLogin.data (high-entropy, not examined), skey.data/skey-bak.data (key material, not decoded), .thumb/.preview/.md sidecars, and actual decryption/rendering of vault media.

Co-authored with Claude Sonnet 5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant