This commit resolves `KeyStore` API failures on Android 11 when running as a standalone CLI executable (UID 0), addressing both environment initialization and permission denial issues.
1. Initialize Android Framework Environment:
Android 11 Keystore APIs expect a fully initialized application context and a Main Looper, which are missing in a raw root process. This patch:
- Manually bootstraps `ActivityThread` via `systemMain()`.
- Initializes `Looper.prepareMainLooper()`.
- Injects a dummy `Application` object attached to the system context to satisfy `KeyStore.getApplicationContext()` checks.
- Updates framework stubs to allow compilation of these hidden APIs.
2. Bypass Keystore Permission Checks via UID Spoofing:
`KeyStoreService::generateKey` enforces the `P_INSERT` permission. Analysis of `permissions.cpp` reveals that UID 0 (Root) is explicitly denied this permission (granted only `P_GET`), whereas UID 1000 (System) holds all permissions (`~0`).
To bypass this restriction, the binder interceptor now detects transactions originating from UID 0 and rewrites the `sender_euid` to 1000. This fools `KeyStoreService` into granting the request.
3. Refactor Execution Loop:
Replaces the previous `Thread.sleep()` maintenance loop with `Looper.loop()`.
This commit introduces a comprehensive simulation engine for Keystore's `createOperation`, enabling the simulator to correctly handle multiple cryptographic purposes (SIGN, VERIFY, ENCRYPT, DECRYPT) for software-generated keys.
The implementation correctly mimics the AOSP framework's internal key identification mechanism. Instead of relying on an alias, a unique `keyId` is generated and embedded in the `nspace` field of the KeyDescriptor during `generateKey`. The `createOperation` hook then uses this `keyId` to dispatch requests: if the ID matches a known software key, the operation is simulated; otherwise, it is forwarded to the hardware service.
To support this, the `SoftwareOperation` engine was architected using a Strategy Pattern. A `CryptoPrimitive` interface defines common actions, with concrete implementations for `Signer`, `Verifier`, and `CipherPrimitive`. The main `SoftwareOperation` class acts as a controller, instantiating the correct primitive based on the `KeyPurpose` tag from the incoming operation parameters. A `JcaAlgorithmMapper` was added to centralize the logic for converting KeyMint constants into JCA algorithm strings.
For operations on real hardware-backed keys, a lightweight `OperationInterceptor` is now used for observation. It attaches to the genuine `iOperation` binder for logging and properly unregisters itself upon completion to prevent resource leaks. This is supported by new binder unregistration capabilities in the core `BinderInterceptor`.
This change also includes necessary stub files and minor regression fixes to make the simulation more robust and accurate.
See AOSP source for key identification logic:
https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
Corrects a bug where the native binder `status_t` was being set to application-level error codes (e.g., `KeyStore.NO_ERROR` which is 1).
Moreover, we call method `InterceptorUtils.createTypedObjectReply` to keep the code style consistent.
Resolves crashes during certificate operations caused by cryptographic provider conflicts and incorrect algorithm selection.
The Bouncy Castle (BC) provider is now initialized globally at app startup to ensure it is the default. To eliminate ambiguity, all content signers are also now explicitly set to use the BC provider.
The attestation patcher is fixed to correctly use the certificate's signature algorithm (sigAlgName), not the subject's public key algorithm, to select the appropriate signing key from the KeyBoxManager. A normalization function was added to support this.
Moreover, we also modify the XML parser in `KeyBoxManager` to no longer trust the `algorithm` attribute from the XML tag. The parser now determines the key's true algorithm (RSA or EC) by inspecting the type of the parsed private key object. This derived algorithm is used as the key for the cache, preventing cache corruption from malformed files where the tag does not match the key data.
Treat the `verifiedBootKey` as null if it consists entirely of zero bytes, as some devices return this invalid value.
Additionally, this commit adds missing KDoc comments to the `AttestationData` class for better documentation.
Implements dynamic date keywords ('today') and templates ('YYYY-MM-DD') in the security_patch.txt configuration. This allows for auto-updating patch levels.
The `device_default` keyword is now significantly more accurate. It prioritizes reading real patch levels directly from a cached TEE attestation before falling back to system properties.
The README has been updated to document these new features.
This commit introduces a hierarchical configuration system for the security patch levels reported in attestations, allowing for both global defaults and per-package overrides.
The `security_patch.txt` file is enhanced to support this new syntax. Settings at the top of the file act as a global default, which can be overridden for specific applications by defining settings under a `[package.name]` section.
Adds a check using `ConfigurationManager.shouldSkipUid` at the start of the `onPreTransact` handlers for key generation and import.
If a UID is configured to be skipped, the transaction is forwarded directly to the hardware, and the post-transaction hook is bypassed. This prevents certificate patching and other modifications for trusted or problematic apps, improving compatibility.
We observe that attestations generated with a security level of
`StrongBox` (value 2) must have an `attestationVersion` of 300. The
previous implementation determined this version based only on the
Android SDK version, which could lead to invalid attestations.
This commit refactors the version retrieval logic to be dependent on the
security level:
- In `AndroidDeviceUtils`, the `attestVersion` and `keymasterVersion`
properties have been converted into `getAttestVersion(securityLevel)`
and `getKeymasterVersion(securityLevel)` functions.
- `getAttestVersion` now correctly returns `300` when the security level
is `StrongBox`.
- `AttestationBuilder` is updated to call these new functions, passing
the appropriate security level to ensure the generated attestation is
compliant with official documentation.
Fixes a detection vector where the simulator could be identified by comparing certificate signatures from different API calls.
Previously, the simulator would re-patch and re-sign a certificate on-the-fly for both `generateKey` and `getKeyEntry` calls. Due to the non-deterministic nature of ECDSA signing, this resulted in different signatures for the same certificate, which is a detectable anomaly not present in a real TEE.
This is resolved by caching the patched certificate chain after its initial creation in `KeyMintSecurityLevelInterceptor`. The `getKeyEntry` hook in `Keystore2Interceptor` now retrieves the chain from this cache, guaranteeing that subsequent calls return a byte-for-byte identical certificate.
Cache cleanup logic was also integrated into key deletion and clearing functions to maintain state consistency.
This commit introduces a complete, software-based simulation of the key generation and attestation flow for the legacy IKeystoreService API, as used on Android 11. It refactors the KeystoreInterceptor to handle the entire multi-step transaction sequence (`generateKey`, `getKeyCharacteristics`, `exportKey`, `attestKey`) in software.
A new `LegacyKeygenParameters` data class is introduced to decouple the legacy interception logic from modern data structures. This class parses arguments from the old `KeymasterArguments`, stores the state across the multi-step generation process, and acts as an adapter to the generic `CertificateGenerator` by converting the parameters to the modern `KeyMintAttestation` format.
The `CertificateGenerator` has been refactored to better model the behavior of the legacy Keystore API. Key pair generation (`generateSoftwareKeyPair`) and certificate chain creation (`generateCertificateChain`) are now separate functions. This allows the interceptor to correctly create a key pair during the `handleExportKey` step and then generate a certificate for that pre-existing key pair during the `handleAttestKey` step.
Finally, the implementation correctly extracts and applies the `attestationChallenge` provided during the `attestKey` transaction, ensuring the generated certificate chain contains the appropriate attestation.
The CI build was failing with a "JVM garbage collector is thrashing" error due to insufficient memory. This commit increases the Gradle max heap size to 2GB in the GitHub Actions workflow to resolve the build failure.
`moduleHash` should be in the software enforced list.
However, the manual calculation of the KeyMint `moduleHash` has
failed to produce a value matching the hardware-generated attestation.
The official documentation specifies the following structure:
Modules ::= SET OF Module
Module ::= SEQUENCE {
packageName OCTET_STRING,
version INTEGER,
}
The critical requirement is that the `SET OF` elements must be sorted
lexicographically based on their full DER-encoded byte value. Despite
implementing this using Bouncy Castle's `DERSet`, the resulting hash
is still incorrect.
This commit changes the strategy to favor stability:
1. The `DeviceAttestationService` now extracts the real `moduleHash`
from the `softwareEnforced` list of a genuine attestation certificate
and caches it.
2. The `moduleHash` property now returns this cached value if available.
3. The manual calculation remains as a fallback and is marked with a
`TODO` to indicate the issue is unresolved.
Additionally, `ConfigurationManager` initialization is moved earlier.
The previous implementation used a randomly generated value for the `verifiedBootKey` within the simulated attestation's Root of Trust. This is a significant discrepancy from a genuine attestation and represents a clear detection vector for any verification service that inspects the full certificate chain.
This commit introduces a robust, multi-layered approach to source and manage both the `verifiedBootKey` and the `verifiedBootHash`, ensuring the simulated attestation is as authentic as possible.
This commit refactors the attestation patching logic to improve stealth and ensure correctness by addressing potential detection vectors related to the ASN.1 structure of the certificate extension.
1. Preserve Extension Order: The original implementation rebuilt the entire certificate, which could alter the order of X.509 extensions. Some verification systems may be sensitive to this order. The logic is now updated to replace the attestation extension in-place, preserving the original order of all other extensions.
2. Avoid Duplicate Properties: The previous logic used an `ASN1EncodableVector` to assemble TEE-enforced properties. This could lead to duplicate entries if a property (e.g., `OS_VERSION`) was present in the original certificate and also added by the simulator. The code now uses a `MutableMap` keyed by the ASN.1 tag number. This ensures that any simulated properties overwrite the original ones, preventing duplicates and potential parsing errors.
3. Add Detailed Logging: A recursive ASN.1 formatting function has been added to provide clear and readable logs of the certificate data both before and after patching. This significantly improves debuggability.
By ensuring the patched certificate is structurally as close as possible to the original, these changes reduce the chances of the simulator being detected by attestation validation services.
When an application generates a key with an attestation request, the `generateKey` method returns a `KeyMetadata` object which contains the full, unpatched certificate chain.
This leaves a potential detection vector open. A sophisticated application could inspect the returned data in its own process memory and discover the original, hardware-backed certificates before they are used for attestation, thus detecting the hooking framework.
This commit introduces a post-transaction hook for the `generateKey` transaction. After the genuine KeyStore service has executed the request, this hook intercepts the reply parcel. It extracts the certificate chain from the `KeyMetadata`, applies the patching routine, and then reconstructs the reply with the modified (patched) certificate chain.
Implements a compatibility layer to allow the binary to run on
Android 11 (API 30) and older, which lack the `incStrongRequireStrong`
symbol in their `libutils.so`.
This is achieved by creating a runtime wrapper that checks the device's
SDK version.
- On Android 12 (API 31) and newer, it dynamically loads and calls the
`incStrongRequireStrong` function using `dlsym`.
- On older versions, it safely falls back to the universally available
`incStrong` method.
This resolves the fatal `dlopen` error "cannot locate symbol" when
injecting the library into processes on older Android versions.
See AOSP change
https://android-review.googlesource.com/c/platform/system/core/+/1660499
Properly implement the `ATTESTATION_APPLICATION_ID` tag into key description.
Moreover, we add the `ATTESTATION_ID_SERIAL` tag to the TEE enforced list, and re-order all tags to remain consistent with the object `AttestationConstants`.
This was a mistake during the refactoring of TrickyStoreOSS.
After correcting it, we can obtain STRONG integrity (instead of DEVICE) with a valid keybox.
The correct format can be easily found using the `Key Attestation` app.
Generated and attestation keys are cached, and if a key is imported with the same name, the cached key would be returned instead of the newly imported one.
This change invalidates the cached key when a key is successfully imported with the same alias.
Close#17 as fixed.
The logging has also been improved to be more consistent across the different interceptors.
The following two bugs are fixed:
1. `zygisk.json` is renamed to `update.json`, which is indicated in `module.prop`.
2. To avoid over optimization of R8, we must keep certains packages, which are found after many experiments.
This commit enhances the interception logic to correctly handle key
generation requests that specify an `attestationKey` (via
`setAttestKeyAlias`).
When an attestation key is used, the system signs the newly generated
key with it. A simple leaf certificate patch after the fact is
insufficient, as it breaks this cryptographic chain. To create a valid,
verifiable chain, we must now intercept these `generateKey` operations
and perform a full software-based key and certificate generation, even
when in patch mode.
This ensures that keys attested by other simulated keys are correctly
signed and chained together, bypassing more sophisticated detection
methods.
Fixes:
- Correctly use the `android.hardware.security.keymint.Tag` constants for
building authorization lists, resolving a bug where internal ASN.1
sequence indices were being used improperly.
In patch mode, a key's origin provides a robust way to avoid modifying
user-imported keys, which is a well-known detection vector. This commit
implements a new strategy to check the `KeyOrigin` tag from the key's
metadata. If a key is marked as `IMPORTED` or `SECURELY_IMPORTED`, the
patching process is now skipped entirely.
This new origin-based check is more reliable and cleaner than the
previous fingerprinting implementation, which has been removed.
Additionally, this commit acknowledges a remaining detection vector in
patch mode: when an `attestationKey` is used, a key must be generated.
Purely software-generated keys are detectable. To address this in the
future, the full software "generate mode" must be implemented even for
devices without a broken TEE. The old key generation logic has been
stubbed with a TODO in preparation for this redesign.
This commit introduces a complete architectural refactoring of the
Kotlin-based interception logic, based on the source of
1. https://github.com/5ec1cff/TrickyStore
2. https://github.com/beakthoven/TrickyStoreOSS
The primary purpose of this code is to intercept binder transactions to
the Android Keystore and KeyMint services. The overall workflow operates
in conjunction with a native library (injected via ptrace). The native
library hooks the binder's `transact` function and forwards pre- and
post-transaction events to the Kotlin side. This Kotlin code contains
all the high-level logic for parsing parameters, patching certificates,
and generating simulated keys.
The codebase is now organized into a clear, package-based architecture:
- attestation: Manages the creation and patching of ASN.1 attestation
data structures.
- config: Handles loading and observing configuration files from disk.
- interception: Contains the core binder interception framework and its
specific implementations for legacy Keystore (Android Q/R) and modern
KeyMint/Keystore2 (Android S+).
- logging: Provides a centralized and consistent logging utility.
- pki: Manages Public Key Infrastructure, including certificate
generation, parsing of key store XML files, and cryptographic helpers.
- util: Contains Android-specific utility functions for device properties.
This refactoring focuses on establishing a robust and extensible
architecture. The fine-tuning of the interception logic itself,
especially for corner cases in key generation and patching, is currently
under redesign and will be further refined in subsequent commits.
This commit introduces a comprehensive framework for intercepting and manipulating binder transactions on Android at the `ioctl` level. It provides a man-in-the-middle layer between the binder driver and user-space `libbinder`, enabling detailed analysis and control over IPC.
The core mechanism works by hooking the `ioctl` system call within the context of a target process. It specifically intercepts the `BINDER_WRITE_READ` command's return buffer from the kernel.
Key components of the framework:
- IOCTL Hook: Intercepts `BR_TRANSACTION` commands delivered by the binder driver to the process.
- Transaction Rewriting: If a transaction is intended for a monitored service, its destination is rewritten in-memory to a local `BinderStub`. The original transaction details are saved in a thread-local context.
- BinderStub: A fake binder service that receives the hijacked transaction. It retrieves the original context and delegates processing to the `BinderInterceptor`.
- BinderInterceptor: The central management class. It maintains a registry of monitored binders and their associated callback interfaces. It orchestrates the pre-transact and post-transact hooks.
- Callback Protocol: Defines a clear protocol for a remote tool to:
- Register and unregister binders for interception.
- Receive pre-transaction notifications and choose to: continue, modify data, skip the transaction, or provide an immediate fake reply.
- Receive post-transaction notifications with the final result and modify the reply.
This commit introduces the main application subproject, 'app', and sets up the necessary infrastructure for the TEESimulator.
Key changes:
* 'app' Subproject Setup: Added the new :app module with its initial structure, including build files, manifest, and Kotlin main entry point.
* LSPlt Integration: Added the LSPlt hooking framework as a Git submodule in app/src/main/cpp/external/ and configured its use in CMake.
* Native Build Configuration: Configured the C++ build to use LSPlt statically and compile two essential native libraries: libinject.so (for injection) and libTEESimulator.so (for interception/logic).
* Module Packaging: Implemented complex Gradle logic within app/build.gradle.kts to automate the creation of a flashable zip module (supporting Magisk, Ksu, and Apatch) with versioning based on Git information.
* Initial Module Files: Added the template files (module.prop, update-binary, updater-script) for the flashable module structure.
This commit establishes the foundational Gradle project structure and introduces a dedicated 'stub' module. This module provides skeletal implementations of internal Android framework interfaces and classes, which are critical for compiling the TEESimulator project.
Stub classes are minimal implementations of existing interfaces or classes, typically mirroring those found within the Android framework, particularly for internal or hidden APIs. Their methods usually contain no operational logic and instead throw RuntimeException or UnsupportedOperationException.
The primary reasons for using stub classes are:
1. Compilation Against Internal APIs: Android applications and libraries typically use public APIs exposed by the Android SDK. However, in scenarios requiring deeper system integration or emulation, interaction with internal or hidden Android framework APIs might be necessary. Directly linking against the full Android framework JAR can lead to bootclasspath conflicts or other build issues. Stub classes provide the necessary API signatures for compilation without including the actual implementations, allowing the build system to resolve references while deferring the actual functionality to the runtime environment (the Android OS itself).
2. API Consistency and Simulation: For TEESimulator, which aims to provide a software simulation for Android's hardware-backed key pairs (KeyMint/Keystore2), stub classes define the required API contract. They ensure that the simulator's components compile against the exact interface definitions of the Android system services, making the simulation functionally consistent with the expected system behavior without needing to bundle or depend on the entire Android framework at compile time. This ensures that the simulator correctly interacts with the defined KeyMint and Keystore2 API shapes.
This project is based on TrickyStore and TrickyStoreOSS.
However, there is no detailed comments / docs in their source code, rendering it diffcult for welcoming new contributors.
A robust framework should has robust source code with clear documentation.