Releases: Enginex0/TEESimulator-RS
TEESimulator-RS v6.0.1-251
TEESimulator-RS v6.0.1-251
14 commits since v6.0.0-235. Clears the remaining Duck Detector grant-domain rows (incl. the Android 16 OnePlus report), restores Google Wallet and fingerprint compatibility, and removes the in-module patch-level/bulletin resolvers. Test device (SDK 35) TEE tamper score 28 → 8.
Detection coverage
- Grant plane virtualized: owner read and cross-app
Domain.GRANTread return one identical chain. 6 RED rows cleared. (28 → 18) - Generate-mode fingerprint: dropped 2 surplus authorizations (both patchlevels), USER_ID moved to SOFTWARE to mirror a captured device. (18 → 8)
- Android 16 grant: patch-mode keys now served on the grant plane, so owner and grant reads match — fixes CHAIN_SPLIT.
- Grant gated to SDK ≥ 36: Android 15 answers PERMISSION_DENIED, no synthetic over-capability.
- Stale-chain eviction: import and updateSubcomponent drop the cached attestation; no pre-mutation chain replays.
- Lifecycle coherence: clearNamespace / deleteAllKeys / migrateKeyNamespace mirror synthetic key and grant state — defeats delete-then-read probes.
- Device-ID attestation mirrors the real TEE: returns CANNOT_ATTEST_IDS where silicon can't attest, instead of forging it.
App compatibility
- Google Wallet: INCLUDE_UNIQUE_ID stripped (not rejected) when the caller lacks the permission; card binding works. (PR #27)
- Fingerprint / vendor keys: KEY_ID miss skips the post-handler, so real HAL operations are no longer wrapped and broken. (PR #26)
Removed
- PatchLevelManager — auto-resolved the security-patch date from an installed PlayIntegrityFix module (with hot-reload) and applied it to props.
- BulletinPoller — scheduled security-bulletin refresh.
Other
- Release builds purge stale
teesim-*.bindiagnostics from/data/local/tmpat boot. - Vol-key confirmation rewritten to 1s
geteventbursts (piped stream missed single presses on Magisk).
Verified
- SDK 35, Xiaomi 23106RN0DA: tamper 28 → 8; generate-mode signal gone; 4 grant rows UNAVAILABLE (correct for Android 15); no regressions.
- Android 16 grant fix built but unconfirmed on SDK 36 — needs an affected OnePlus user to confirm the grant rows clear.
TEESimulator-RS v6.0.0-235
11 commits since v6.0.0-224. Duck Detector generate-mode fingerprint cleared. Shizuku-routed BYO attestation fixed. Vol-key confirmation restored on Magisk.
Detection Coverage
- Duck Detector "TEE Simulator generate-mode fingerprint" cleared.
toAuthorizationsreordered to AOSP keymint reference order; KEY_SIZE moves from auth#4 to auth#2, breaking the byte-224 anchor the probe relied on. 0/31 matches on fresh self-probes (was 15/36). persist.logd.sizevariants blanked at boot viaservice.sh. Removes a logd-tuning side-channel.
BYO & Shizuku Routing
- Shizuku-routed BYO attestation no longer fails with
-49 UNSUPPORTED_TAG.shouldSkipUidmoved intohandleGenerateKey, evaluated after BYO parameters are parsed. createOperationparallel fix: outer UID gate removed; the cache-or-forward lookup is the sole gate. BYO keys created under Shizuku UID can now be used for signing under the same UID.forceGeneratesimplified: any attest-key or BYO request routes to software unconditionally.- BYO attest-key miss returns the full keybox chain instead of a malformed depth-1 chain.
- AUTO TEE race dispatch removed. Resolution uses
DeviceAttestationService.isTeeFunctionalonly. - Symmetric gen rejects
attestationKey != nullearly withINVALID_ARGUMENT. Unsupported-algorithm branch returns-38instead of-49.
Action Button
- Vol+ / Vol- confirmation restored on Magisk. Streaming
getevent -lqmatched inline againstKEY_VOLUMEUP DOWN/KEY_VOLUMEDOWN DOWN, wrapped in/system/bin/timeout 10. The prior polled approach timed out on six-events-per-keypress kernels.
Verified
- Android 15 (SDK 35), daemon PID 1466.
- Cross-device confirmation pending on OnePlus PKX110 and Samsung SM-S928B.
v6.0.0-224: Action button hardened + 22-language i18n
Action button hardened: vol+ confirm + 22-language i18n
Builds on v6.0.0-222 (TEE probe cleared). Two commits address a
user-reported UX issue where the root manager's Action button was
being triggered by accident, wiping persistent_keys.
ff5eb79 feat(action): require vol+ confirm to clear keys
6455903 feat(action): i18n the clear-keys confirmation
Action flow:
- Vol+ confirms clear, Vol- cancels, 10-second timeout defaults to
cancel (destructive op stays explicit-only). - Every echoed string resolves through _msg() against the device
locale.
Languages shipped (22 + English default):
ar, az, bn, de, el, es-ES, fa, fr, id, it, ja, ko, pl, pt-BR, ru,
th, tl, tr, uk, vi, zh-CN, zh-TW.
Verified on Xiaomi Android 16 (en) v6.0.0-224-Release: vol+ clears,
vol- cancels, 10s timeout cancels. Daemon alive PID 1392.
Changelog: https://github.com/Enginex0/TEESimulator-RS/blob/main/module/changelog.md
v6.0.0-222: TEE probe cleared on Xiaomi
TEE probe cleared on Xiaomi (AOSP-like behavior)
Three commits land the full fix for Duck Detector's TamperScore-4
TimingSideChannelProbe needle across all configured modes:
6bc1e43 fix(config): isAutoMode reads raw package mode
599fd39 feat(intercept): resolve KEY_ID via teeResponses
be91ea7 fix(intercept): cache non-attested keys for parity
Verified on Xiaomi Android 16 (SDK 35) v6.0.0-222-Release:
TEE verdict: WARNING -> CLEAR
'Local TEE attestation checks aligned'
Timing ratio: 1.555x Positive -> 1.055x Not positive
(threshold > 1.1x)
Attested: 0.976ms -> 0.846ms
Non-attested: 1.517ms -> 0.802ms
Pending cross-device confirmation on OnePlus PKX110 (qcom sun) and
Samsung SM-S928B (pineapple), both of which showed the same needle
on v6.0.0-211.
v6.0.0-162
Repository consolidation release. All tee-rebuild work merged as the new main branch.
AOSP Self-Signed Cert Compliance
- No-challenge keys now generate self-signed certs (subject == issuer, depth 1), matching AOSP
ta/src/keys.rs:451-478 - Both Kotlin (BouncyCastle) and Rust (native-certgen) paths corrected
- Eliminates attestation behavioral probes that detect keybox issuer on non-attested keys
Stability
- Binder stress crash hardening for concurrent generateKey calls
- AUTO mode TEE race for consistent attestation on devices with working G10
- Oversized transactions routed to software gen instead of crashing
- Operation-time params (BLOCK_MODE, PADDING, DIGEST) passed through to CipherPrimitive
Banking App Compatibility
- Bare
target.txtentries now default to AUTO mode, resolved at config level to PATCH (working TEE) or GENERATE (broken TEE) - Fixes BHIM and similar banking apps that require TEE-backed attestation keys
- Restores v5.0 behavior where AUTO was resolved before the interceptor dispatch, avoiding the non-deterministic
raceTeePatchpath
Infrastructure
- Version scheme changed to semver (v6.0.0)
- Repository moved to TEESimulator-RS as canonical source
v6.0.0-156
Repository consolidation release. All tee-rebuild work merged as the new main branch.
AOSP Self-Signed Cert Compliance
- No-challenge keys now generate self-signed certs (subject == issuer, depth 1), matching AOSP
ta/src/keys.rs:451-478 - Both Kotlin (BouncyCastle) and Rust (native-certgen) paths corrected
- Eliminates attestation behavioral probes that detect keybox issuer on non-attested keys
Stability
- Binder stress crash hardening for concurrent generateKey calls
- AUTO mode TEE race for consistent attestation on devices with working G10
- Oversized transactions routed to software gen instead of crashing
- Operation-time params (BLOCK_MODE, PADDING, DIGEST) passed through to CipherPrimitive
Infrastructure
- Version scheme changed to semver (v6.0.0)
- Repository moved to TEESimulator-RS as canonical source
v5.1.1-164
Restores all custom hardening fixes that were lost during the PR JingMatrix#157 migration. These were working in pre-stash builds but never carried over to the post-stash codebase, causing user-reported regressions (boot hash instability, DuckDetector score regression, config crash on file deletion).
- Boot hash persistence restored: 4-step fallback (sysprop, TEE, file, random) with file writes at every step. Fixes "Boot: Unavailable" where boot hash randomized every reboot on devices without
ro.boot.vbmeta.digest - Presence-based findBoolean for KeyMint tags: boolean tags are presence-based per AIDL spec,
.boolValuefield isn't reliably populated across Android versions - noAuthRequired defaults to true when not explicitly false, matching AOSP KeyMint behavior
- callerNonce tag now flows through to software-enforced attestation list
- CTR block mode restored in cipher algorithm mapping (was dropped in PR JingMatrix#157)
- AEAD guard on updateAad: non-GCM operations throw INVALID_TAG
- Error code resolution via lazy reflection with correct KeyMint AIDL fallback values
- Latency floor on SoftwareOperation.finish() for StrongBox timing simulation
- FileObserver NPE fixed: null-safe handling on config file DELETE events
- system=prop consistency: forces boot/vendor patch levels to derive from device props
- StrongBox simulation restored: capability checks (RSA<=2048, EC=P256), concurrent op limits (4 max), keygen latency floor (250ms), op latency floor (80ms)
- Binder buffer guard: MAX_ALIAS_LENGTH (256KB) rejects oversized aliases before processing
- Key lifecycle tracking: deletedSoftwareKeys set prevents ghost key responses after deletion
- Per-UID operation limits: 15 TEE, 4 StrongBox with LRU eviction
- EC+DECRYPT rejection in createOperation, matching AOSP unsupported purpose check
- Attest key nspace update aligned with upstream PR JingMatrix#169
v5.1-153
Major release. 27 files changed, 2300 lines rewritten. The entire Kotlin interception layer has been rebuilt with a clean architecture, proper AIDL alignment, and significantly lower binder overhead.
Interception Layer Rewrite
- KeyMintSecurityLevelInterceptor completely restructured: GeneratedKeyInfo now carries full KeyMintAttestation instead of nullable stub, eliminating scattered null checks across every operation path
- SoftwareOperation rewritten with sealed CryptoPrimitive interface separating Signer, Verifier, Encryptor, and Decryptor into isolated implementations with proper JCA algorithm mapping
- KeystoreErrorCode centralized object replaces scattered magic numbers for all KeyMint and Keystore2 error codes
- listEntries moved from pre-transact parameter caching to post-transact injection, eliminating a race condition where cached params could go stale
- deleteKey now handles both APP domain (by alias) and KEY_ID domain (by nspace) resolution paths correctly
- AuthorizeCreate and AndroidPermissionUtils removed, authorization logic consolidated into the operation dispatch path
- DeviceAttestationService removed, attestation routing simplified into the main interceptor
Software Crypto Operations
- GCM nonce returned in CreateOperationResponse.parameters for encrypt operations, matching real KeyMint HAL behavior
- updateAad correctly throws INVALID_TAG on non-AEAD operations instead of silently succeeding
- Cipher algorithm mapping cleaned up: dropped CTR block mode and RSA_PKCS1_1_5_SIGN padding that caused JCA provider mismatches
- All crypto exceptions wrapped as ServiceSpecificException with correct KeyMint error codes instead of raw exceptions
Attestation & Certificate Generation
- CertificateGenerator rewritten with clean Kotlin Pair return type instead of Android's util.Pair
- AttestationBuilder field ordering aligned with AOSP KeyDescription ASN.1 schema
- Unique ID computation follows KeyMint HAL spec: HMAC-SHA256(temporal_counter || AAID || reset_flag, HBK) truncated to 128 bits
- Patch level logging removed from hot path to reduce logcat noise on every attestation
Configuration & Device Properties
- ConfigurationManager target package parsing refactored: mode/package extraction deduplicated across GENERATE/PATCH/AUTO branches
- system=prop forced boot/vendor override removed, now respects explicit per-component patch level configuration
- FileObserver delete handler simplified with direct file access instead of defensive null-checks
- AndroidDeviceUtils expanded with additional device property accessors for attestation fields
Binder Performance
- Safe parcel reads at 6 deserialization sites, replacing force-unwrap NPE paths with early-return on null. A single NPE generates a full stack trace that blocks the binder thread for ~2ms
- teeResponses cache populated on generateKey/importKey post-transact, reducing getKeyEntry from 2+ binder round-trips to 1
- pingBinder liveness check removed from pre-transact failure path, eliminating a synchronous IPC call on every failed transaction
- Native transaction code filtering at C++ level, skipping JNI entirely for PING/INTERFACE/DUMP
Dynamic SecurityLevel Binder Registration
- Intercepts getSecurityLevel replies to register hooks on every new BBinder instance keystore2 returns, not just the initial one from setup
- Identity hash deduplication prevents double-hooking when keystore2 returns the same binder across multiple calls
- Resolves apps that call getSecurityLevel independently and receive a different binder than the one registered at startup
Build & Packaging
- Rust native build task integrated into Gradle with cargo-ndk for aarch64/armv7/x86/x86_64
- Module ZIP includes all 4 native libraries (libTEESimulator, libsupervisor, libcertgen, libinject)
- customize.sh extraction restored for supervisor daemon and native cert gen library
- TeeLatencySimulator added as standalone utility for log-normal hardware latency emulation
v5.0-138
Major release integrating 30+ AOSP compliance improvements from upstream PR JingMatrix#157 analysis, layered on top of our StrongBox hardening and native cert gen architecture.
Attestation Extension Alignment
- 17 enforcement tags added to KeyMintAttestation (ACTIVE_DATETIME, ORIGINATION_EXPIRE, USAGE_EXPIRE, USAGE_COUNT_LIMIT, CALLER_NONCE, UNLOCKED_DEVICE_REQUIRED, INCLUDE_UNIQUE_ID, ROLLBACK_RESISTANCE, EARLY_BOOT_ONLY, ALLOW_WHILE_ON_BODY, TRUSTED_USER_PRESENCE_REQUIRED, TRUSTED_CONFIRMATION_REQUIRED, NO_AUTH_REQUIRED, MAX_USES_PER_BOOT, MAX_BOOT_LEVEL, MIN_MAC_LENGTH, RSA_OAEP_MGF_DIGEST)
- BLOCK_MODE encoded as SET OF INTEGER per AOSP attestation_record.h
- Version-guarded tags (RSA_OAEP_MGF_DIGEST >=100, ROLLBACK_RESISTANCE >=3, EARLY_BOOT_ONLY >=4)
- INCLUDE_UNIQUE_ID computed via HMAC-SHA256 per KeyMint HAL spec using device HBK
- AAID gated on attestation challenge presence
- Certificate validity defaults aligned with AOSP (epoch notBefore, 9999-12-31 notAfter)
Binder Infrastructure
- Native transaction code filtering at C++ level, skipping JNI for non-intercepted codes
- getNumberOfEntries includes software-generated key count
- deleteKey resolves KEY_ID domain via generatedKeys lookup
- patchAuthorizations for OS/VENDOR/BOOT patch levels in authorization arrays
Software Operation AOSP Conformance
- updateAad on non-AEAD operations returns INVALID_TAG (-76), matching AOSP operation.rs
- All crypto exceptions wrapped as ServiceSpecificException with correct KeyMint error codes
- GCM IV returned in CreateOperationResponse.parameters for encrypt operations
- SoftwareOperationBinder methods @synchronized, matching AOSP Mutex per operation
- authorize_create enforcement: PURPOSE validation, algorithm-purpose compatibility, temporal constraints, CALLER_NONCE prohibition, WRAP_KEY rejection
Security and Configuration
- SELinux permission checks via /proc/pid/attr/current
- Per-UID permission verification through IPackageManager.checkPermission
- Imported key tracking prevents stale attest-key overrides in getKeyEntry
- nspace consistency fix in attest-key override path
- TeeLatencySimulator with log-normal distribution matching real hardware profiles
- Device-unique HBK seed generated on install (32 bytes from /dev/random)
Preserved from v4.8
- StrongBox op limits (4 concurrent max, TOO_MANY_OPERATIONS rejection)
- LRU operation pruning per security level
- Hardware keygen rate limiting (2/30s sliding window, 2 concurrent cap)
- Native Rust cert generation with BouncyCastle fallback
- Key persistence across reboots
v4.8-132
- StrongBox op limit gate fix —
trackAndEnforceOpLimitwas only called in theDomain.KEY_IDnot-found path, so software-generated keys (found viaDomain.APP) bypassedSTRONGBOX_MAX_CONCURRENT_OPS=4entirely. DuckDetector's concurrent signing handles test created 24+ operations that all succeeded via LRU pruning instead of being rejected withTOO_MANY_OPERATIONS (-29). Now enforced for all StrongBox createOperation paths.
Tested against DuckDetector on OnePlus (Android 16, KSU). Tamper score dropped from 32 to 8.
- LRU operation pruning — Concurrent software operations capped at 15 per UID (TEE) and 4 per UID (StrongBox), with oldest-first eviction. Pruned operations return
INVALID_OPERATION_HANDLE (-28), matching AOSP keystore2 malus-based pruning. - StrongBox param guard — Unsupported StrongBox params (RSA >2048-bit, non-P256 EC curves) forwarded to real HAL for proper rejection instead of generating in software.
- StrongBox timing — Key generation floors at 250ms, signing at 80ms on StrongBox security level to match real secure element latency.
- StrongBox op limit — Sliding-window enforcer caps concurrent StrongBox operations for both software and hardware key paths, returning
TOO_MANY_OPERATIONS (-29)when exceeded. - ECDSA algorithm alias — Accept "ECDSA" in addition to "EC" as JCA private key algorithm name. Fixes SIGSEGV crash on Android 10 devices where the provider reports EC keys as "ECDSA". Closes #4.
- createOperation domain handling — Software-generated keys now found via both
Domain.APP(alias) andDomain.KEY_ID(nspace) lookup paths. - Permission guards — Device ID attestation tags (IMEI, MEID, serial) require caller permission checks.