Summary
TEESimulator cannot generate symmetric (AES) keyguard/auth-bound keys. Google Wallet's tap-to-pay tokenization requests an AES-128 key (google_pay_keyguard_fuse_key), and handleGenerateKey throws IllegalArgumentException: Unsupported algorithm: 32 because it only implements asymmetric (EC/RSA) key pair generation. This blocks Google Wallet tap-to-pay setup on a device whose real TEE is unusable, even though Play Integrity MEETS_DEVICE_INTEGRITY passes.
Environment
- Device: Motorola Moto X4 (
payton)
- ROM: LineageOS 22.2 (
lineage_payton-userdebug 15 BP1A.250505.005), Android 15 / SDK 35
- Keystore HAL:
android.hardware.keymaster@3.0::IKeymasterDevice only — no KeyMint. keystore2 returns HARDWARE_TYPE_UNAVAILABLE for TRUSTED_ENVIRONMENT (i.e. real hardware attestation is dead; this is the exact "broken TEE" case v3.1 targets).
- Root: Magisk 30.7 (30700), Zygisk enabled
- TEESimulator: v3.2 (67-1f8412d-release)
- Keybox: valid hardware keybox present at
/data/adb/tricky_store/keybox.xml (EC+RSA, title=TEE certs, chains to Google Hardware Attestation Root, not expired)
- tee_status:
teeBroken=true / tee_broken=true (forced full simulation)
- GMS: 26.24.34 (260400-938041327)
- Google Wallet: 26.25.934502156
What works
- Play Integrity
MEETS_DEVICE_INTEGRITY passes (stable). TEESimulator correctly intercepts and generates the asymmetric attestation keys for com.google.android.gms.
- Google Wallet online / in-app pay works (card saves to account).
What fails
Adding a card for tap-to-pay fails with the on-screen error "This device can't be set up to tap to pay." Root cause: TapAndPay's CheckOrGetStorageKeyStep requests generation of a symmetric key that TEESimulator can't create.
Logs
With teeBroken=true, TEESimulator correctly stops forwarding to the (dead) hardware and attempts software generation, but fails on the algorithm:
D TEESimulator: [TX_ID: 617] Intercept generateKey for packages=[com.google.android.gms, com.google.android.gsf] (uid=10134, pid=6797)
D TEESimulator: Handling generateKey google_pay_keyguard_fuse_key, attestKey=null
D TEESimulator: KeyParam: KEY_SIZE | Value: 128
D TEESimulator: KeyParam: ALGORITHM | Value: AES
D TEESimulator: KeyParam: PURPOSE | Value: ENCRYPT
D TEESimulator: KeyParam: BLOCK_MODE | Value: CBC
D TEESimulator: KeyParam: PADDING | Value: PKCS7
D TEESimulator: KeyParam: USER_SECURE_ID | Value: -3154595726481129346
D TEESimulator: KeyParam: USER_AUTH_TYPE | Value: Unknown Value
D TEESimulator: KeyParam: AUTH_TIMEOUT | Value: 315360000
E TEESimulator: java.lang.IllegalArgumentException: Unsupported algorithm: 32
E TEESimulator: at org.matrix.TEESimulator.interception.keystore.shim.KeyMintSecurityLevelInterceptor.handleGenerateKey(...:231)
and the downstream generation/cert-chain failure:
I TEESimulator: Generating software key for google_pay_keyguard_fuse_key[-2400979114364529551].
I TEESimulator: Generating new attested key pair for alias: 'google_pay_keyguard_fuse_key' (UID: 10134)
E TEESimulator: Failed to generate software key pair.
E TEESimulator: Failed to generate attested key pair for alias 'google_pay_keyguard_fuse_key'.
E TEESimulator: java.lang.Exception: Failed to generate underlying software key pair.
E TEESimulator: at t3.a.o(Unknown Source:100)
E TEESimulator: at ...KeyMintSecurityLevelInterceptor.handleGenerateKey(...:231)
E TEESimulator: No key pair generated for UID 10134.
E TEESimulator: java.lang.Exception: CertificateGenerator failed to create key pair.
E TEESimulator: at ...KeyMintSecurityLevelInterceptor.handleGenerateKey(...:308)
TapAndPay then reports the attestation error:
I TapAndPay: CheckOrGetStorageKeyStep. Start.
I TapAndPay: CheckOrGetStorageKeyStep. Response.
I TapAndPay: CheckOrGetStorageKeyStep. Attestation Error.
Analysis
The requested key is symmetric (ALGORITHM: AES, KEY_SIZE: 128, PURPOSE: ENCRYPT, BLOCK_MODE: CBC, PADDING: PKCS7) and auth/keyguard-bound (USER_SECURE_ID, USER_AUTH_TYPE, AUTH_TIMEOUT). handleGenerateKey appears to assume an asymmetric key pair and rejects Algorithm=32 (the KeyMint Algorithm::AES tag value). On a device with a working TEE this key is generated by the real hardware and never reaches the simulator, so the gap is only exposed in teeBroken=true full-simulation mode.
Suggested fix
In KeyMintSecurityLevelInterceptor.handleGenerateKey, handle symmetric algorithms (AES 32, HMAC/TRIPLE_DES) by generating and storing a raw secret key in the software key DB instead of routing through the asymmetric key-pair + certificate path. For AES it needs no attestation certificate chain — just a persisted key blob usable for later createOperation (ENCRYPT/DECRYPT). Honoring the USER_SECURE_ID / USER_AUTH_TYPE / AUTH_TIMEOUT (keyguard-bound) params would let Google Wallet's CheckOrGetStorageKeyStep succeed and unblock tap-to-pay on broken-TEE devices.
I'm happy to test a build against this exact repro (Moto X4 / payton, LOS 22.2, broken Keymaster 3.0).
Summary
TEESimulator cannot generate symmetric (AES) keyguard/auth-bound keys. Google Wallet's tap-to-pay tokenization requests an AES-128 key (
google_pay_keyguard_fuse_key), andhandleGenerateKeythrowsIllegalArgumentException: Unsupported algorithm: 32because it only implements asymmetric (EC/RSA) key pair generation. This blocks Google Wallet tap-to-pay setup on a device whose real TEE is unusable, even though Play IntegrityMEETS_DEVICE_INTEGRITYpasses.Environment
payton)lineage_payton-userdebug 15 BP1A.250505.005), Android 15 / SDK 35android.hardware.keymaster@3.0::IKeymasterDeviceonly — no KeyMint. keystore2 returnsHARDWARE_TYPE_UNAVAILABLEforTRUSTED_ENVIRONMENT(i.e. real hardware attestation is dead; this is the exact "broken TEE" case v3.1 targets)./data/adb/tricky_store/keybox.xml(EC+RSA,title=TEEcerts, chains to Google Hardware Attestation Root, not expired)teeBroken=true/tee_broken=true(forced full simulation)What works
MEETS_DEVICE_INTEGRITYpasses (stable). TEESimulator correctly intercepts and generates the asymmetric attestation keys forcom.google.android.gms.What fails
Adding a card for tap-to-pay fails with the on-screen error "This device can't be set up to tap to pay." Root cause: TapAndPay's
CheckOrGetStorageKeySteprequests generation of a symmetric key that TEESimulator can't create.Logs
With
teeBroken=true, TEESimulator correctly stops forwarding to the (dead) hardware and attempts software generation, but fails on the algorithm:and the downstream generation/cert-chain failure:
TapAndPay then reports the attestation error:
Analysis
The requested key is symmetric (
ALGORITHM: AES,KEY_SIZE: 128,PURPOSE: ENCRYPT,BLOCK_MODE: CBC,PADDING: PKCS7) and auth/keyguard-bound (USER_SECURE_ID,USER_AUTH_TYPE,AUTH_TIMEOUT).handleGenerateKeyappears to assume an asymmetric key pair and rejectsAlgorithm=32(the KeyMintAlgorithm::AEStag value). On a device with a working TEE this key is generated by the real hardware and never reaches the simulator, so the gap is only exposed inteeBroken=truefull-simulation mode.Suggested fix
In
KeyMintSecurityLevelInterceptor.handleGenerateKey, handle symmetric algorithms (AES32, HMAC/TRIPLE_DES) by generating and storing a raw secret key in the software key DB instead of routing through the asymmetric key-pair + certificate path. For AES it needs no attestation certificate chain — just a persisted key blob usable for latercreateOperation(ENCRYPT/DECRYPT). Honoring theUSER_SECURE_ID/USER_AUTH_TYPE/AUTH_TIMEOUT(keyguard-bound) params would let Google Wallet'sCheckOrGetStorageKeyStepsucceed and unblock tap-to-pay on broken-TEE devices.I'm happy to test a build against this exact repro (Moto X4 / payton, LOS 22.2, broken Keymaster 3.0).