Add KeepSafe (photo vault) support: vault items, albums, account/PIN security - #2026
Open
Gear-I wants to merge 1 commit into
Open
Add KeepSafe (photo vault) support: vault items, albums, account/PIN security#2026Gear-I wants to merge 1 commit into
Gear-I wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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