JingMatrix / JingMatrix/TEESimulator
Canary 67: expired EC attestation intermediate in keybox causes Play Integrity to become UNEVALUATED in patch mode
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 2.4k
- Forks
- 312
- Avg merge
- 2d 8h
- Merged PRs (30d)
- 1
Description
Summary
On a Pixel 8 Pro (husky) running GrapheneOS / Android 17, enabling TEESimulator v4 Canary 67 changes Play Integrity from a working MEETS_BASIC_INTEGRITY result to a completely UNEVALUATED response.
I isolated the behavior to TEESimulator:
- Play Integrity Fix is disabled during reproduction.
- TrickyStore is installed but disabled.
- Without TEESimulator, Play Integrity returns
MEETS_BASIC_INTEGRITY. - With TEESimulator enabled, the complete Play Integrity evaluation becomes
UNEVALUATED.
The configured keybox contains an expired EC attestation intermediate certificate.
TEESimulator nevertheless uses that EC chain in patch mode and reports:
patch_attestation -> 3 cert(s) [patched leaf + keybox EC chain]
OpenSSL independently confirms that the EC intermediate certificate expired on January 8, 2026.
Environment
- Device: Google Pixel 8 Pro (
husky) - OS: GrapheneOS
- Android: 17
- SDK: 37
- Root: Magisk
- Zygisk implementation: NeoZygisk
- TEESimulator:
v4.0 (67-4f42350-debug) - versionCode:
67 - commit:
4f42350 - TEESimulator mode:
patch - TrickyStore: installed but disabled
- Play Integrity Fix: disabled during reproduction
- Secure App Spawning for Google Play Services: disabled
com.google.android.gms.unstableis confirmed to be spawned fromzygote64
TEESimulator module information:
id=teesim
name=TEESimulator
version=v4.0 (67-4f42350-debug)
versionCode=67
author=JingMatrix
TEESimulator configuration
{
"version": 1,
"profiles": {
"default": {
"keybox": "keybox.xml",
"mode": "patch",
"patchLevel": {
"system": "today",
"vendor": "YYYY-MM-05",
"boot": "YYYY-MM-05"
},
"osVersion": "",
"brand": "",
"device": "",
"product": "",
"manufacturer": "",
"model": "",
"serial": "",
"imei": "",
"meid": "",
"imei2": "",
"apps": [
"com.google.android.gms",
"com.android.vending",
"io.github.vvb2060.keyattestation"
]
}
}
}
Reproduction
- Disable Play Integrity Fix.
- Disable TEESimulator.
- Reboot.
- Run a Play Integrity test.
- BASIC integrity is returned normally.
- Enable TEESimulator with the configuration shown above.
- Reboot.
- Run the same Play Integrity test again.
- The complete Play Integrity evaluation becomes
UNEVALUATED.
This behavior is reproducible.
Result with TEESimulator disabled
The same Play Integrity checker receives:
{
"requestDetails": {
"requestPackageName": "gr.nikolasspyr.integritycheck"
},
"appIntegrity": {
"appRecognitionVerdict": "PLAY_RECOGNIZED"
},
"deviceIntegrity": {
"deviceRecognitionVerdict": [
"MEETS_BASIC_INTEGRITY"
],
"recentDeviceActivity": {
"deviceActivityLevel": "UNEVALUATED"
},
"deviceAttributes": {
"sdkVersion": 37
}
},
"accountDetails": {
"appLicensingVerdict": "LICENSED"
},
"environmentDetails": {
"playProtectVerdict": "POSSIBLE_RISK"
}
}
The important part is:
MEETS_BASIC_INTEGRITY
Result with only TEESimulator enabled
{
"requestDetails": {
"requestPackageName": "gr.nikolasspyr.integritycheck"
},
"appIntegrity": {
"appRecognitionVerdict": "UNEVALUATED"
},
"deviceIntegrity": {
"recentDeviceActivity": {
"deviceActivityLevel": "UNEVALUATED"
},
"deviceAttributes": {}
},
"accountDetails": {
"appLicensingVerdict": "UNEVALUATED"
},
"environmentDetails": {
"playProtectVerdict": "UNEVALUATED",
"appAccessRiskVerdict": {}
}
}
There is no BASIC / DEVICE / STRONG verdict at all.
Even appRecognitionVerdict, licensing and Play Protect change to UNEVALUATED.
Relevant TEESimulator log
During the failing Play Integrity request TEESimulator logs:
RKP: denying TE IRemoteProvisioning transact code=1 for target uid=10147
(irpcName=android.hardware.security.keymint.IRemotelyProvisionedComponent/default,
remote_provisioning.tee.rkp_only=<unset>;
keystore2 will append no real attest-key chain;
our generation stays keybox-rooted)
keystore2:
Failed to get rkpd key
Status(-129, EX_TRANSACTION_FAILED): 'FAILED_TRANSACTION: '
patch: real HAL returned 4 cert(s);
re-signing only the leaf under the keybox
patch_attestation applying profile:
os_version=170000
os_patchlevel=202609
vendor_patchlevel=20260905
boot_patchlevel=20260905
ids=profile
patch_attestation -> 3 cert(s) [patched leaf + keybox EC chain]
patch: emitted 3-cert chain
(real key blob kept, leaf re-rooted at keybox)
This suggests that the real hardware attestation itself is working:
real HAL returned 4 cert(s)
The failure appears after TEESimulator re-signs / re-roots the attestation using the configured EC keybox chain.
Keybox certificate validation
I extracted only the public certificates from the configured keybox.
No private key material is included below.
EC intermediate
subject=
C=US,
ST=California,
O=Google, Inc.,
OU=Android,
CN=Android Keystore Software Attestation Intermediate
issuer=
C=US,
ST=California,
L=Mountain View,
O=Google, Inc.,
OU=Android,
CN=Android Keystore Software Attestation Root
serial=1001
notBefore=Jan 11 00:46:09 2016 GMT
notAfter=Jan 8 00:46:09 2026 GMT
sha256 Fingerprint=
B9:8B:2D:68:7C:24:43:4B:0F:FA:20:EB:88:9E:18:0F:
E2:D5:14:88:3D:69:54:84:EB:B4:FF:83:A6:63:9B:9B
Public-Key: (256 bit)
The intermediate certificate is expired on the current date.
EC root
subject=
C=US,
ST=California,
L=Mountain View,
O=Google, Inc.,
OU=Android,
CN=Android Keystore Software Attestation Root
issuer=
C=US,
ST=California,
L=Mountain View,
O=Google, Inc.,
OU=Android,
CN=Android Keystore Software Attestation Root
serial=A2059ED10E435B57
notBefore=Jan 11 00:43:50 2016 GMT
notAfter=Jan 6 00:43:50 2036 GMT
sha256 Fingerprint=
51:D4:96:AD:46:64:19:0F:BD:AF:1A:D9:87:27:8E:FA:
35:D6:FB:F5:2E:50:A9:C3:4D:ED:83:95:47:7E:DE:5C
Public-Key: (256 bit)
The root itself is still valid.
OpenSSL verification
Verification of the EC intermediate against the EC root:
C=US, ST=California, O=Google, Inc., OU=Android,
CN=Android Keystore Software Attestation Intermediate
error 10 at 0 depth lookup: certificate has expired
error cert-1.pem: verification failed
So the EC certificate chain used by TEESimulator fails normal X.509 validity checking because the intermediate expired on:
2026-01-08
Key types present in the keybox
cert-1.pem: Public-Key: (256 bit)
cert-2.pem: Public-Key: (256 bit)
cert-3.pem: Public-Key: (1024 bit)
cert-4.pem: Public-Key: (1024 bit)
The two 256-bit certificates are the EC intermediate/root shown above.
TEESimulator explicitly reports that it emits:
[patched leaf + keybox EC chain]
so the expired EC intermediate appears to be part of the chain actually used for the patched attestation.
RKP / KeyMint availability
The device itself exposes the normal KeyMint and Remote Key Provisioning services:
android.hardware.security.keymint.IKeyMintDevice/default
android.hardware.security.keymint.IKeyMintDevice/strongbox
android.hardware.security.keymint.IRemotelyProvisionedComponent/default
android.hardware.security.keymint.IRemotelyProvisionedComponent/strongbox
android.security.rkp.IRemoteProvisioning
Relevant properties:
remote_provisioning.hostname=remoteprovisioning.googleapis.com
ro.product.brand_for_attestation=google
ro.product.device_for_attestation=husky
ro.product.manufacturer_for_attestation=Google
ro.product.model_for_attestation=Pixel 8 Pro
ro.strongbox.manufacturer=Google
ro.strongbox.model=Titan-M2
Therefore this does not appear to be a device where RKP is simply unavailable.
Difference from issue #292
I also checked issue #292.
My case appears to be different.
In #292, the affected device already fails its native attestation path / KeyMaster operation and does not expose:
android.hardware.security.keymint.IRemotelyProvisionedComponent/default
In my case:
- the Pixel exposes both TEE and StrongBox RKP components,
- the real HAL successfully returns a 4-certificate attestation chain,
- TEESimulator patch mode successfully receives that chain,
- the failure appears after the leaf is re-rooted using the configured EC keybox chain,
- that EC chain contains an intermediate certificate that is already expired.
Therefore I don't think this is the same "broken TEE" situation discussed in #292.
Additional observations
TrickyStore is installed but disabled:
/data/adb/modules/tricky_store/disable
The TrickyStore and TEESimulator keybox files happen to be byte-identical:
57ff10ee68b76aecf7760da9bb3bba621d8509d8196e10a22d89aa8371cbb849 /data/adb/teesim/keybox.xml
57ff10ee68b76aecf7760da9bb3bba621d8509d8196e10a22d89aa8371cbb849 /data/adb/tricky_store/keybox.xml
However, because TrickyStore is disabled, it should not be participating in the failing test.
Secure App Spawning for Google Play Services is also disabled.
The GMS unstable process is confirmed to be spawned through Zygote:
u0_a147 ... com.google.android.gms.unstable
PPid: zygote64
This also appears different from the GrapheneOS Secure App Spawning issue discussed in #261.
Expected behavior
If a certificate in a configured keybox signing chain is expired, I would expect TEESimulator to do one of the following:
- Reject the keybox/profile as invalid.
- Display a clear warning in the WebUI.
- Log that one of the certificates in the selected chain has expired.
- Avoid replacing an otherwise working hardware attestation with an already-invalid certificate chain.
Currently the operation is reported as successful:
patch_attestation -> 3 cert(s) [patched leaf + keybox EC chain]
even though the resulting EC chain fails ordinary X.509 validity checking.
Questions
-
Does TEESimulator currently validate
notBefore/notAfterfor every certificate in the selected keybox chain? -
Should an expired intermediate certificate make the keybox or profile invalid?
-
Could the WebUI display certificate validity dates and an
expired/validstatus when importing or selecting a keybox? -
Is the EC chain selected automatically in patch mode for this type of request?
-
Is there any fallback behavior if the EC chain is expired but another certificate chain is present in the keybox?
-
Could the expired EC intermediate explain why Play Integrity changes from
MEETS_BASIC_INTEGRITYto a completelyUNEVALUATEDresponse?
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the failure in TEESimulator patch mode with the supplied keybox.xml and inspect the patch_attestation logs. Use OpenSSL to confirm the EC intermediate's validity dates and trace where the selected certificate chain is accepted. Done means establishing whether expired chains are validated and documenting or implementing the expected rejection, warning, or fallback behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- cryptography, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100