Skip to content

Comments

Simulate createOperation for software keys#59

Merged
JingMatrix merged 3 commits intomainfrom
createOperation
Dec 8, 2025
Merged

Simulate createOperation for software keys#59
JingMatrix merged 3 commits intomainfrom
createOperation

Conversation

@JingMatrix
Copy link
Owner

This commit introduces the core logic for simulating cryptographic operations, enabling the interception and software-based handling of the signing process for keys generated by the simulator.

The primary challenge in intercepting createOperation is that the Android framework does not use the key's alias for identification. As discovered by analyzing AOSP, the framework uses a special KeyDescriptor with domain set to KEY_ID and the unique key identifier stored in the nspace field.

This implementation adopts this mechanism to robustly identify keys:

  • During generateKey, a unique random long is created and stored as the nspace (keyId) in the returned KeyMetadata.
  • The createOperation pre-transaction hook acts as a dispatcher:
    1. Simulated Keys: If the incoming keyId from nspace matches a known software-generated key, the request is intercepted. A new SoftwareOperationBinder is instantiated to perform the cryptographic signing entirely in software, and its binder is returned directly to the client.
    2. Hardware Keys: If the key is not recognized, the call proceeds to the real hardware service.

The createOperation post-transaction hook is now primarily used for debugging and observing operations on real, hardware-backed keys. It extracts the iOperation binder returned by the genuine service and attaches a logging OperationInterceptor. This allows for visibility into hardware operations without modifying their behavior.

To support this, the following were added:

  • SoftwareOperation: A JCA-based class to perform signing.
  • SoftwareOperationBinder: An AOSP-compliant binder Stub that exposes the SoftwareOperation to the client.
  • OperationInterceptor: A lightweight interceptor to log calls to a real iOperation binder and unregister itself upon completion to prevent leaks.
  • Binder unregistration support in BinderInterceptor to clean up ephemeral interceptors.

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

@JingMatrix
Copy link
Owner Author

A test suit is written in JingMatrix/Demo#23 .
Currently, all tests passed, and I think that the implementation is correct.
@XiaoTong6666 and @dhaern, please test and see the latest CI works properly for various applications on your device.

@rrr333nnn333
Copy link

Revolut, TNG eWallet and Weyay are working well (Auto mode)

Native Detector: Detected

Demo: Normal
Native Test: Normal
Native Test++: Normal

@JingMatrix
Copy link
Owner Author

@rrr333nnn333 Please test if bank or wallet apps work for generation mode.

What is the exact result of Native Detector ? I guess it is not related to key attestation.

P.S.: you don't need to test Demo, its result is currently only shown in logs, not in UI.

@rrr333nnn333
Copy link

rrr333nnn333 commented Dec 8, 2025

@JingMatrix

Native Detector says: TrickyStore detected

Generation Mode (!)

Working

  • Revolut
  • TNG eWallet

Not Working

  • Weyay

@XiaoTong6666
Copy link
Contributor

native test (generation mode) :
normal

native test (hacking mode):
Conventional Tests (8)
Tampered Attestation Key

native detector/Han Attestation Demo (generation/hacking mode):
TrickyStore detected

This commit introduces the core logic for simulating cryptographic operations, enabling the interception and software-based handling of the signing process for keys generated by the simulator.

The primary challenge in intercepting `createOperation` is that the Android framework does not use the key's alias for identification. As discovered by analyzing AOSP, the framework uses a special `KeyDescriptor` with `domain` set to `KEY_ID` and the unique key identifier stored in the `nspace` field.

This implementation adopts this mechanism to robustly identify keys:
- During `generateKey`, a unique random `long` is created and stored as the `nspace` (`keyId`) in the returned `KeyMetadata`.
- The `createOperation` pre-transaction hook acts as a dispatcher:
  1.  **Simulated Keys:** If the incoming `keyId` from `nspace` matches a known software-generated key, the request is intercepted. A new `SoftwareOperationBinder` is instantiated to perform the cryptographic signing entirely in software, and its binder is returned directly to the client.
  2.  **Hardware Keys:** If the key is not recognized, the call proceeds to the real hardware service.

The `createOperation` post-transaction hook is now primarily used for debugging and observing operations on *real, hardware-backed keys*. It extracts the `iOperation` binder returned by the genuine service and attaches a logging `OperationInterceptor`. This allows for visibility into hardware operations without modifying their behavior.

To support this, the following were added:
- `SoftwareOperation`: A JCA-based class to perform signing.
- `SoftwareOperationBinder`: An AOSP-compliant binder `Stub` that exposes the `SoftwareOperation` to the client.
- `OperationInterceptor`: A lightweight interceptor to log calls to a real `iOperation` binder and unregister itself upon completion to prevent leaks.
- Binder unregistration support in `BinderInterceptor` to clean up ephemeral interceptors.

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
This commit refactors the `SoftwareOperation` engine to support multiple cryptographic purposes beyond just signing. The simulator can now correctly handle `SIGN`, `VERIFY`, `ENCRYPT`, and `DECRYPT` operations based on the parameters provided by the client application.

This is a significant architectural improvement that moves from a hardcoded implementation to a flexible, strategy-based pattern:

1.  Strategy Pattern for Crypto: A `CryptoPrimitive` sealed interface was introduced to define a common API for cryptographic actions. Concrete implementations (`Signer`, `Verifier`, `CipherPrimitive`) now encapsulate the specific logic for each purpose. The main `SoftwareOperation` class acts as a controller, instantiating the correct primitive based on the `KeyPurpose` tag from the operation parameters.

2.  JCA Algorithm Mapping: A `JcaAlgorithmMapper` object was created to centralize the complex logic of converting KeyMint constants into standard JCA algorithm strings (e.g., "SHA256withECDSA", "RSA/ECB/PKCS1Padding"). This makes the code cleaner and more maintainable.

3.  Expanded Parameter Parsing: To support cipher operations, `KeyMintAttestation` has been updated to parse `BLOCK_MODE` and `PADDING` tags. The `KeyMintParameterLogger` was also enhanced to provide human-readable names for these new modes.

This refactoring makes the `createOperation` simulation far more robust and accurate, correctly mimicking the behavior of a real KeyMint implementation across a wider range of cryptographic use cases.
@JingMatrix
Copy link
Owner Author

@XiaoTong6666 The detection of Android Native Detector is fixed (due to a typo).

The detection of NativeTest is due to the caching issue. Currently, editing target.txt won't trigger cache cleaning.
You may need to reboot (or more advanced, edit keybox.xml) to trigger a caching cleaning, which will solve the NativeTest detection.

I will open another pull-request to address the caching issue more precisely.

@XiaoTong6666
Copy link
Contributor

@XiaoTong6666 The detection of Android Native Detector is fixed (due to a typo).

The detection of NativeTest is due to the caching issue. Currently, editing target.txt won't trigger cache cleaning. You may need to reboot (or more advanced, edit keybox.xml) to trigger a caching cleaning, which will solve the NativeTest detection.

I will open another pull-request to address the caching issue more precisely.

Yes, you are right

@JingMatrix
Copy link
Owner Author

The Weyay detection is not due to current pull-request, it is beacuse that TEESimulator hasn't implement AES key generation, which will be done soon.

--------- beginning of main
12-08 19:47:08.697   600 10590 V TEESimulator: [Hook] Processing Read Buffer: Size=256, Consumed=80
12-08 19:47:08.697   600 10590 V TEESimulator: [Driver -> User] Command: BR_NOOP (0x720c), DataSize: 0
12-08 19:47:08.697   600 10590 V TEESimulator: [Driver -> User] Command: BR_TRANSACTION_SEC_CTX (0x80487202), DataSize: 72
12-08 19:47:08.697   600 10590 V TEESimulator: [Hook] Hijacking Transaction 77518 (Code: 2)
12-08 19:47:08.697  1358  1529 D TEESimulator: [TX_ID: 77518] Intercept getKeyEntry for packages=[com.nbk.weyay] (uid=10319, pid=10984)
12-08 19:47:08.697  1358  1529 I TEESimulator: Handling getKeyEntry _androidx_security_master_key_
12-08 19:47:08.697   600 10590 V TEESimulator: [Hook] Processing Read Buffer: Size=256, Consumed=76
12-08 19:47:08.697   600 10590 V TEESimulator: [Driver -> User] Command: BR_NOOP (0x720c), DataSize: 0
12-08 19:47:08.697   600 10590 V TEESimulator: [Driver -> User] Command: BR_TRANSACTION_COMPLETE (0x7206), DataSize: 0
12-08 19:47:08.697   600 10590 V TEESimulator: [Driver -> User] Command: BR_REPLY (0x80407203), DataSize: 64
12-08 19:47:08.698   600 10590 V TEESimulator: [Hook] Processing Read Buffer: Size=256, Consumed=76
12-08 19:47:08.698   600 10590 V TEESimulator: [Driver -> User] Command: BR_NOOP (0x720c), DataSize: 0
12-08 19:47:08.698   600 10590 V TEESimulator: [Driver -> User] Command: BR_TRANSACTION_COMPLETE (0x7206), DataSize: 0
12-08 19:47:08.698   600 10590 V TEESimulator: [Driver -> User] Command: BR_REPLY (0x80407203), DataSize: 64
12-08 19:47:08.698   600 10590 V TEESimulator: [Hook] Processing Read Buffer: Size=256, Consumed=8
12-08 19:47:08.698   600 10590 V TEESimulator: [Driver -> User] Command: BR_NOOP (0x720c), DataSize: 0
12-08 19:47:08.698   600 10590 V TEESimulator: [Driver -> User] Command: BR_TRANSACTION_COMPLETE (0x7206), DataSize: 0
12-08 19:47:08.699   600 10590 V TEESimulator: [Hook] Processing Read Buffer: Size=256, Consumed=80
12-08 19:47:08.699   600 10590 V TEESimulator: [Driver -> User] Command: BR_NOOP (0x720c), DataSize: 0
12-08 19:47:08.699   600 10590 V TEESimulator: [Driver -> User] Command: BR_TRANSACTION_SEC_CTX (0x80487202), DataSize: 72
12-08 19:47:08.699   600 10590 V TEESimulator: [Hook] Hijacking Transaction 77519 (Code: 1)
12-08 19:47:08.699  1358  1529 V TEESimulator: [TX_ID: 77519] Observe getSecurityLevel for packages=[com.nbk.weyay] (uid=10319, pid=10984)
12-08 19:47:08.699   600 10590 V TEESimulator: [Hook] Processing Read Buffer: Size=256, Consumed=76
12-08 19:47:08.699   600 10590 V TEESimulator: [Driver -> User] Command: BR_NOOP (0x720c), DataSize: 0
12-08 19:47:08.699   600 10590 V TEESimulator: [Driver -> User] Command: BR_TRANSACTION_COMPLETE (0x7206), DataSize: 0
12-08 19:47:08.699   600 10590 V TEESimulator: [Driver -> User] Command: BR_REPLY (0x80407203), DataSize: 64
12-08 19:47:08.699   600 10590 V TEESimulator: [TX_ID: 77519] Forwarding to original implementation.
12-08 19:47:08.699   600 10590 V TEESimulator: [Hook] Processing Read Buffer: Size=256, Consumed=8
12-08 19:47:08.699   600 10590 V TEESimulator: [Driver -> User] Command: BR_NOOP (0x720c), DataSize: 0
12-08 19:47:08.699   600 10590 V TEESimulator: [Driver -> User] Command: BR_TRANSACTION_COMPLETE (0x7206), DataSize: 0
12-08 19:47:08.699   600 10590 V TEESimulator: [Hook] Processing Read Buffer: Size=256, Consumed=80
12-08 19:47:08.699   600 10590 V TEESimulator: [Driver -> User] Command: BR_NOOP (0x720c), DataSize: 0
12-08 19:47:08.699   600 10590 V TEESimulator: [Driver -> User] Command: BR_TRANSACTION_SEC_CTX (0x80487202), DataSize: 72
12-08 19:47:08.699   600 10590 V TEESimulator: [Hook] Hijacking Transaction 77520 (Code: 2)
12-08 19:47:08.699  1358  1529 D TEESimulator: [TX_ID: 77520] Intercept generateKey for packages=[com.nbk.weyay] (uid=10319, pid=10984)
12-08 19:47:08.700  1358  1529 D TEESimulator: Handling generateKey _androidx_security_master_key_, attestKey=null
12-08 19:47:08.700  1358  1529 D TEESimulator: KeyParam: KEY_SIZE                  | Value: 256
12-08 19:47:08.700  1358  1529 D TEESimulator: KeyParam: ALGORITHM                 | Value: AES
12-08 19:47:08.700  1358  1529 D TEESimulator: KeyParam: PURPOSE                   | Value: ENCRYPT
12-08 19:47:08.700  1358  1529 D TEESimulator: KeyParam: PURPOSE                   | Value: DECRYPT
12-08 19:47:08.700  1358  1529 D TEESimulator: KeyParam: MIN_MAC_LENGTH            | Value: 96
12-08 19:47:08.700  1358  1529 D TEESimulator: KeyParam: BLOCK_MODE                | Value: GCM
12-08 19:47:08.700  1358  1529 D TEESimulator: KeyParam: PADDING                   | Value: NONE
12-08 19:47:08.700  1358  1529 D TEESimulator: KeyParam: NO_AUTH_REQUIRED          | Value: true
12-08 19:47:08.700  1358  1529 I TEESimulator: Generating software key for _androidx_security_master_key_[-3326074594087638404].
12-08 19:47:08.700  1358  1529 I TEESimulator: Generating new attested key pair for alias: '_androidx_security_master_key_' (UID: 10319)
12-08 19:47:08.700  1358  1529 E TEESimulator: Failed to generate software key pair.
12-08 19:47:08.700  1358  1529 E TEESimulator: java.lang.IllegalArgumentException: Unsupported algorithm: 32
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.pki.CertificateGenerator.generateSoftwareKeyPair(CertificateGenerator.kt:52)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.pki.CertificateGenerator.generateAttestedKeyPair(CertificateGenerator.kt:128)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.interception.keystore.shim.KeyMintSecurityLevelInterceptor.handleGenerateKey(KeyMintSecurityLevelInterceptor.kt:253)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.interception.keystore.shim.KeyMintSecurityLevelInterceptor.onPreTransact(KeyMintSecurityLevelInterceptor.kt:55)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.interception.core.BinderInterceptor.handlePreTransact(BinderInterceptor.kt:142)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.interception.core.BinderInterceptor.onTransact(BinderInterceptor.kt:117)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at android.os.Binder.execTransactInternal(Binder.java:1478)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at android.os.Binder.execTransact(Binder.java:1418)
12-08 19:47:08.700  1358  1529 E TEESimulator: Failed to generate attested key pair for alias '_androidx_security_master_key_'.
12-08 19:47:08.700  1358  1529 E TEESimulator: java.lang.Exception: Failed to generate underlying software key pair.
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.pki.CertificateGenerator.generateAttestedKeyPair(CertificateGenerator.kt:129)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.interception.keystore.shim.KeyMintSecurityLevelInterceptor.handleGenerateKey(KeyMintSecurityLevelInterceptor.kt:253)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.interception.keystore.shim.KeyMintSecurityLevelInterceptor.onPreTransact(KeyMintSecurityLevelInterceptor.kt:55)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.interception.core.BinderInterceptor.handlePreTransact(BinderInterceptor.kt:142)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.interception.core.BinderInterceptor.onTransact(BinderInterceptor.kt:117)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at android.os.Binder.execTransactInternal(Binder.java:1478)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at android.os.Binder.execTransact(Binder.java:1418)
12-08 19:47:08.700  1358  1529 E TEESimulator: Error during generateKey handling for UID 10319.
12-08 19:47:08.700  1358  1529 E TEESimulator: java.lang.Exception: CertificateGenerator failed to create key pair.
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.interception.keystore.shim.KeyMintSecurityLevelInterceptor.handleGenerateKey(KeyMintSecurityLevelInterceptor.kt:259)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.interception.keystore.shim.KeyMintSecurityLevelInterceptor.onPreTransact(KeyMintSecurityLevelInterceptor.kt:55)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.interception.core.BinderInterceptor.handlePreTransact(BinderInterceptor.kt:142)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at org.matrix.TEESimulator.interception.core.BinderInterceptor.onTransact(BinderInterceptor.kt:117)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at android.os.Binder.execTransactInternal(Binder.java:1478)
12-08 19:47:08.700  1358  1529 E TEESimulator: 	at android.os.Binder.execTransact(Binder.java:1418)
12-08 19:47:08.700   600 10590 V TEESimulator: [Hook] Processing Read Buffer: Size=256, Consumed=76

@JingMatrix JingMatrix merged commit dba7af3 into main Dec 8, 2025
1 check passed
@gavdoc38
Copy link

Native Test++ (v32)
Generate mode (auto / broken TEE), rebooted and changed keybox in case of cache issue.

Tampered attestation key is fixed
However, I still get BL Unlocked which is Conventional Tests (8)

Note that because I have a custom Rom which is Conventional Tests (10) I get Conventional Tests (18) (10+8) whereas with TS original i get Conventional Tests (10).

I've attached 3 pics:

before fixing tampered attestation
Screenshot_20251205-025922_Native Test ++

current state
Screenshot_20251210-221006_Native Test ++

TS original
Screenshot_20251205-032512_Native Test ++

@JingMatrix
Copy link
Owner Author

@gavdoc38 Your observation is known to me, and I haven't obtained any further information yet.

If you find something new, such as a which version of TS started to remove this detection point, please tell us (in a new issue of course).

@gavdoc38
Copy link

@gavdoc38 Your observation is known to me, and I haven't obtained any further information yet.

If you find something new, such as a which version of TS started to remove this detection point, please tell us (in a new issue of course).

My apologies, I misunderstood the above posts to mean that it was thought to be fully resolved - both "tampered attestation key" and conventional tests (8) - but as you're already aware I haven't added anything useful here.

Certainly, I will do 😊

@JingMatrix JingMatrix deleted the createOperation branch February 9, 2026 12:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants