Andrea-lyz 9e1b459b74 fix: persist symmetric keys + byte-identical metadata; stop wiping keys on keybox edits
Five issues that together caused keystore-pinned apps to be silently
logged out across reboots and config changes. All flow from the same
root cause: GeneratedKeyPersistence loses information on save -> reload.

1. Symmetric keys (AES, HMAC, 3DES) were never persisted at all
   - GeneratedKeyPersistence.save only accepted KeyPair, ignoring SecretKey
   - AndroidX security MasterKey (AES-GCM-256) regenerated on every
     reboot, making EncryptedSharedPreferences undecryptable
   - Apps that wrap session tokens in EncryptedSharedPreferences
     interpret this as session expiry and force a relogin

2. Restored KeyMetadata authorizations differed from generation-time bytes
   - loadPersistedKeys rebuilt KeyMintAttestation with mostly null/empty
     fields, so toAuthorizations emitted a different tag set after
     reboot vs. at generateKey time
   - Apps that fingerprint metadata across keystore calls saw a
     "changed key"

3. certificate / certificateChain split could shift after restore
   - buildKeyEntryResponse called updateCertificateChain on the rebuilt
     metadata, which is allowed to repartition leaf vs. chain bytes
   - Apps with strict leaf fingerprint checks saw a "changed cert"

4. Touching ANY .xml under /data/adb/tricky_store wiped every cached key
   - ConfigObserver called clearAllGeneratedKeys() which also calls
     GeneratedKeyPersistence.deleteAll()
   - Editing keybox.xml (or any unrelated .xml) thus deleted every
     persisted key on disk
   - Even the keybox-cache argument does not justify wiping per-app keys:
     patched chains alone are stale, raw keypairs are not

5. SoftwareOperation NPE when restored keyParams missed PURPOSE tag
   - Init dereferenced keyPair!! before checking purpose, so a
     half-restored record crashed instead of producing a clean error

Single on-disk format (FORMAT_VERSION = 3) covers everything: PKCS8
private key bytes for asymmetric, raw secret bytes for symmetric, plus
the byte-identical KeyMetadata parcel snapshot so authorizations
restore exactly. Earlier dev-only formats are silently skipped by the
loader; the next generateKey for those aliases re-creates them in v3.

ConfigObserver now calls invalidatePatchedChains() instead of
clearAllGeneratedKeys() on .xml edits - only the chain cache is
stale, not the underlying keypairs.

Tested on OnePlus 13 (Android 16, KSU 3.2.4):
- Apps survive force-stop + cold reboot without losing keystore state
- Apps survive keybox.xml edits / replacements (touch, sed, cp -mv)
- Tamper score still 4 (CONSISTENT) on Duck Detector
- KeyAttestation chain output unchanged
2026-05-16 18:43:09 +02:00
2026-01-11 16:24:34 +01:00
2025-11-22 10:34:59 +01:00
2025-11-22 10:34:59 +01:00

TEESimulator-RS

Full TEE Emulation for Rooted Android

Build Android 10+ Telegram


Note

Fork of JingMatrix/TEESimulator with native Rust certificate generation, key persistence, and AOSP-compliant attestation behavior. For the upstream project, see the original repo.

What It Does

TEESimulator intercepts Binder IPC at the ioctl level inside the keystore2 process and generates entire certificate chains from scratch, signed by your keybox, with correct attestation extensions. Apps that verify hardware attestation see a legitimate device.

This is not TrickyStore. TEESimulator replaces TrickyStore and its forks entirely. It shares the same config paths for drop-in compatibility, but the internals are different: native Rust cert generation, binder-level interception via lsplt, per-UID rate limiting, key persistence, and AOSP-spec attestation behavior.

Requirements

Important

A valid keybox.xml is required for hardware-level attestation. Without one, the module generates software-level certificates that won't pass strict hardware checks.

  1. Android 10+
  2. Root manager: KernelSU, Magisk, or APatch
  3. keybox.xml at /data/adb/tricky_store/keybox.xml

Quick Start

  1. Download the latest ZIP from Releases
  2. Install via your root manager and reboot
  3. Place your keybox at /data/adb/tricky_store/keybox.xml
  4. Configure targets in /data/adb/tricky_store/target.txt
  5. Verify with Play Integrity or Key Attestation Demo

Architecture

Native Cert Generationlibcertgen.so generates X.509 chains in Rust using ring and manual DER encoding. BouncyCastle fallback for unsupported curves (P-224, P-521, Curve25519).

Binder Interception — PLT hook on ioctl() in libc.so via lsplt inside keystore2. Intercepts generateKey, importKey, and getKeyEntry transactions.

AOSP Compliance — Self-signed certs for non-attested keys (matching ta/src/keys.rs), correct AuthorizationList tag ordering, version-guarded extension fields, authorize_create enforcement.

Key Persistence — Generated keys survive reboots. File-backed with file-level locking.

Rate Limiting — Per-UID hardware keygen cap (2/30s window, 2 concurrent). Overflow falls to software certs.

Configuration

All config files live at /data/adb/tricky_store/ and are hot-reloaded via FileObserver.

target.txt

Controls which apps get intercepted and the simulation mode.

Suffix Mode
! Force software key generation
? Force leaf certificate patching (real TEE key, patched cert)
(none) Automatic selection

Multi-keybox support via [filename.xml] headers:

com.google.android.gms!
io.github.vvb2060.keyattestation?

[aosp_keybox.xml]
com.google.android.gsf

security_patch.txt

Override patch levels reported in attestation certificates. Global defaults at top, per-package overrides with [package.name].

Key Scope
system OS patch level
vendor Vendor patch level
boot Boot/kernel patch level
all Sets all three

Special values: today, YYYY-MM-DD templates, no (omit tag), device_default, prop (read from system property).

system=YYYY-MM-05
vendor=device_default
boot=no

[com.google.android.gms]
system=2025-10-01

Building from Source

Prerequisites: JDK 21, Android SDK/NDK 27, Rust stable with aarch64-linux-android target, cargo-ndk.

git clone --recursive https://github.com/Enginex0/TEESimulator-RS.git
cd TEESimulator-RS
./gradlew zipRelease zipDebug

Output ZIPs in out/. Gradle invokes cargo ndk automatically to cross-compile libcertgen.so.

Push to main or use Actions > Build > Run workflow to trigger CI.

Compatibility

Root Manager Status
KernelSU Tested (Action button + lifecycle scripts)
Magisk Supported
APatch Supported

Community

Telegram

Credits

  • JingMatrix — original TEESimulator and interception architecture
  • 5ec1cff — TrickyStore, the project that pioneered keystore interception
  • LSPlt — PLT hook library
  • ring — Rust cryptography library
  • MhmRdd — AOSP compliance work via upstream PR #157
  • fatalcoder524 — contributor and collaborator
  • huguangares — collaborator and tester

License

GNU General Public License v3.0

S
Description
Software simulation for Android hardware-backed key pairs with key attestation | https://t.me/superpowers9
Readme GPL-3.0
2.9 MiB
Languages
Kotlin 61.3%
C++ 18.2%
Rust 9.2%
Java 6.7%
Shell 4.4%
Other 0.2%