Releases: Enginex0/TEESimulator-RS
Release list
TEESimulator-RS v6.0.1-307
Fixes five gaps in the module's TEE key-operation and attestation emulation. Two of them fix crashes in real app crypto on a broken-TEE device: any app using an AndroidKeyStore HMAC key or an RSA-OAEP-SHA256 key was throwing. This is a beta; the confirmation logs are in the debug build only, and nothing is field-verified yet.
App crypto correctness
- HMAC operations now run instead of throwing. An AndroidKeyStore HMAC key is symmetric, so an HMAC SIGN fell into the asymmetric SIGN path and failed on a null key pair, and no MAC primitive existed. SIGN and VERIFY now work: the tag is computed with Mac (HmacSHA256/384/512), truncated to the requested MAC_LENGTH (full digest when unspecified), and checked with a constant-time compare.
- RSA-OAEP-SHA256 decrypt no longer fails with BadPaddingException. The cipher ran with no OAEPParameterSpec, so JCA fell back to SHA-1 and rejected SHA-256 ciphertext. It now applies the correct main and MGF1 digests. A key that authorizes several MGF1 digests uses the one the operation requested, not the key's first.
- Grant-domain attestation keys now resolve. A self-granted PURPOSE_ATTEST_KEY (a Domain.GRANT descriptor) could not resolve its signer alias, so generateKey failed and the subject key was never stored, returning KEY_NOT_FOUND on readback. The grant now resolves to the owner key's alias, and the subject key stays readable under Domain.APP.
Supplementary attestation
- MODULE_HASH now comes from the framework's own getSupplementaryAttestationInfo, so it matches the value a verifier computes. It falls back to local re-derivation when that API is unreachable.
Diagnostics (debug builds only)
- New per-operation (oaep-op, hmac-op, attest-grant) and device-level (module-hash, vintf-version) source logs. R8 strips them from release builds.
Testers: install the Debug ZIP to capture the confirmation logs (adb logcat -s TEESimulator); the Release build strips them under R8.
中文说明
修复本模块在 TEE 密钥操作与证明模拟中的五处缺陷。其中两处修复的是真实应用在 TEE 损坏设备上的加密崩溃:任何使用 AndroidKeyStore HMAC 密钥或 RSA-OAEP-SHA256 密钥的应用此前都会抛出异常。本版本为测试版;确认日志仅存在于 debug 构建中,且尚未经过真机验证。
应用加密正确性
- HMAC 操作现在可正常执行,不再抛出异常。AndroidKeyStore 的 HMAC 密钥是对称密钥,因此 HMAC SIGN 此前落入了非对称的 SIGN 分支,并因 key pair 为空而失败,当时也没有 MAC 原语。现在 SIGN 与 VERIFY 均可工作:用 Mac (HmacSHA256/384/512) 计算标签,按请求的 MAC_LENGTH 截断(未指定时取完整摘要长度),并用恒定时间比较进行校验。
- RSA-OAEP-SHA256 解密不再抛出 BadPaddingException。此前 cipher 未传入 OAEPParameterSpec,JCA 因而回退到 SHA-1 并拒绝 SHA-256 密文。现在会应用正确的主摘要与 MGF1 摘要。若密钥授权了多个 MGF1 摘要,将使用本次操作请求的那个,而非密钥的第一个。
- Grant 域证明密钥现在可以解析。自授权的 PURPOSE_ATTEST_KEY(Domain.GRANT 描述符)此前无法解析其签名者别名,导致 generateKey 失败且从不存储主体密钥,读取时返回 KEY_NOT_FOUND。现在该 grant 会解析为属主密钥的别名,主体密钥在 Domain.APP 下仍可读取。
补充证明
- MODULE_HASH 现在取自框架自带的 getSupplementaryAttestationInfo,因而与验证方计算出的值一致。当该 API 不可用时,回退到本地重新推导。
诊断(仅 debug 构建)
- 新增按操作 (oaep-op、hmac-op、attest-grant) 与设备级 (module-hash、vintf-version) 的来源日志。R8 会在 release 构建中将其剥离。
TEESimulator-RS v6.0.1-282
TEESimulator-RS v6.0.1-282
AUTO-mode key attestation now forges plain attestation from the keybox instead of deferring to the real TEE.
Detection coverage
- AUTO dispatch probed the device with
checkTeeFunctionality, which only proves the TEE can mint one EC key. It says nothing about RSA attestation, device-ID attestation, or whether a patched chain survives RSA verify. Plain attestation requests (attest-key OFF, challenge present) were routed to PATCH and deferred to hardware, so devices that can't back that surfaced KeyAttestation reds:ATTESTATION_KEYS_NOT_PROVISIONED(-49) andBLOCK_TYPE_IS_NOT_01. - AUTO targets carrying an attestation challenge now take the FORGE path, the same one attest-key-ON already used: a synthetic chain built from the keybox and rooted under the Google root key. Requests with no challenge still pass through to real hardware, so KeyDetector's hardware-backed checks are unaffected.
Verified
- Offline conformance against real FORGE captures: uid10389 and uid10154 chains are GREEN; the root SPKI byte-matches
GOOGLE_ROOT_PUBLIC_KEY.
TEESimulator-RS v6.0.1-280
TEESimulator-RS v6.0.1-280
Clears the Duck Detector generate-mode parcel fingerprint that real Android 16 hardware also trips, fixes RSA attestation under an EC-only keybox, and restores device-property attestation for Play Integrity hardware apps such as BHIM and UPI. Generate-mode fix field-confirmed on Android 16.
Detection coverage
- Generate-mode fingerprint: Duck reads the reply at a flat 12-byte stride and flags the sentinel tuple at positions 12 and 13 that the device's native ALGORITHM-first authorization order lands on. Real A16 silicon trips the same probe, so faithful mirroring stayed flagged.
normalizeAuthorizationLayoutmarshals the auth array, runs Duck's exact predicate, and applies a minimal deterministic reorder only when it would match. Count, values, security levels, and the cert chain are untouched, and the reorder keys on the byte condition, never on a package. Applied on both the patch and forge reply paths. (#33) updateAadon a non-AEAD operation now answers per vendor: Samsung and Xiaomi-MTK TEEs return success, others return INVALID_TAG, matching Duck's OperationErrorPathProbe on both the sign/verify and cipher paths.
Attestation correctness (Android 16, EC and RSA)
- RSA leaf under an EC-only keybox: patching used to catch the no-RSA-key throw and return the chain untouched, leaking the device's real unlocked Root of Trust for RSA keys while EC keys patched cleanly. It now falls back to any keybox key (EC preferred) and signs the patched leaf with the keybox key's own algorithm, so the RSA leaf re-roots to the Google keybox under a forged locked RoT.
- RSA attest-key forge on an EC-only keybox: the forge path matched the algorithm exactly and threw -75 ATTESTATION_KEYS_NOT_PROVISIONED on a miss, so an RSA ATTEST_KEY request never rooted and verifiers reported an unknown certificate. It now falls back to any attestation key, since an EC key validly ECDSA-signs an RSA-subject leaf. No-op on a dual keybox.
- A16 attestVersion: the device's KeyMint reports version 100 and the lazy cache shadowed the BAKLAVA-to-400 map, so the forge presented 100. It now caches the AOSP value per SDK and presents the correct 400.
- Algorithm-split key on restore: a persisted record holding an EC private key under an RSA leaf failed every signature as DATA_TOO_LARGE_FOR_MODULUS. Restore now drops the record when the private key and served leaf disagree, so the next generateKey rebuilds a coherent key.
- Stale chain on regenerate: reusing an alias in generateKey now evicts the cached chain, matching keystore2, so getKeyEntry serves the current key instead of a stale forge from an earlier generation.
App compatibility
- Device-property attestation (BRAND, MODEL, and the rest) now forges unconditionally. The old gate probed the live TEE, which is dead on every device the module serves, so it rejected GMS Play Integrity's hardware path and broke BHIM and other UPI and Play-Integrity apps. Device-ID attestation (IMEI, serial) stays governed by the real KeyMint caller-permission rule: privileged callers get it, ordinary apps do not.
- getKeyEntry now reaches the owned-key lookup for skipped privileged UIDs, so framework attestKeyAlias resolution no longer returns "Invalid attestKeyAlias" for Key Attestation over Shizuku. Non-owned keys still skip post-processing, so a real app's key is never patched.
- Device-ID attestation over Shizuku (a privileged UID absent from target.txt) now takes the forge path instead of hitting the real TEE's CANNOT_ATTEST_IDS (-66). "Use attest key" no longer double-roots: a reused persistent attest key is resolved by KEY_ID as well as alias, and an unresolved designated attest key refuses to emit a leaf rather than silently re-rooting under the keybox.
Diagnostics (debug builds only)
- Per-UID attestation dossier for targeted UIDs at /data/local/tmp/teesim/, recording the decoded chain on both forge and patch paths, key params, the keybox pick (including EC fail-safe), prop sources, forge failures, the emitted authorization shape, and served-versus-verified chains. Release builds strip this through R8 and stay silent. Keybox certificate serials log on every fetch for revocation triage.
Verified
- Android 16: generate-mode fingerprint signal gone, confirmed on device 2026-06-19.
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