Skip to content

Comments

Enforce 128-byte limit for attestation challenge#70

Merged
JingMatrix merged 1 commit intoJingMatrix:mainfrom
XiaoTong6666:pr2
Jan 20, 2026
Merged

Enforce 128-byte limit for attestation challenge#70
JingMatrix merged 1 commit intoJingMatrix:mainfrom
XiaoTong6666:pr2

Conversation

@XiaoTong6666
Copy link
Contributor

@XiaoTong6666 XiaoTong6666 commented Dec 31, 2025

This commit aligns the simulator's behavior with the Android Keymaster/KeyMint specification by enforcing a maximum length of 128 bytes for the attestation challenge.
Previously, the simulator accepted attestation challenges of arbitrary length during generateKey. This behavior differed from real implementations and allowed detection tools to identify whether the current TEE / KeyMint / Keymaster environment was emulated or tampered with by intentionally sending an oversized challenge (e.g., > 128 bytes) and observing that it was accepted instead of rejected.
The implementation now validates the size of the TAG_ATTESTATION_CHALLENGE parameter. If the challenge exceeds the limit, the transaction is intercepted, and a ServiceSpecificException is constructed manually via Binder (using the EX_SERVICE_SPECIFIC header) to return the INVALID_INPUT_LENGTH (-21) error code. This matches the error code and Binder-visible behavior defined by the KeyMint specification.

这个 commit 是通过将 attestation challenge 的最大长度限制为 128 字节,让模拟的行为更加符合 Keymaster / KeyMint 的规范。
在此之前,模拟器在 generateKey 过程中会接受任意长度的 attestation challenge。这种行为与真实实现不一致,导致一些检测工具可以通过刻意发送超长 challenge(比如超过 128 字节),并观察是否被接受,来判断当前 TEE/ KeyMint / Keymaster 是否模拟被篡改。
现在的实现会对 TAG_ATTESTATION_CHALLENGE 的长度进行检查。如果 challenge 超过限制,请求会被直接拦截,并通过 Binder 手动构造一个 ServiceSpecificException(使用 EX_SERVICE_SPECIFIC 头),返回 INVALID_INPUT_LENGTH(-21)错误码,这样可以保证返回的错误码和 Binder 层可观察到的行为与 Keymaster / KeyMint 规范保持一致

See AOSP source for the length constraint and error definition:
https://cs.android.com/android/platform/superproject/main/+/main:system/keymaster/android_keymaster/android_keymaster.cpp;l=330
https://cs.android.com/android/platform/superproject/main/+/main:system/keymaster/km_openssl/attestation_record.cpp;l=257
https://cs.android.com/android/platform/superproject/main/+/main:hardware/interfaces/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl;l=48

@XiaoTong6666
Copy link
Contributor Author

The duck detector already uses this method for detection.

Screenshot_2025-12-31-13-14-48-343_com studio duckdetector

@Stillhard
Copy link

Stillhard commented Jan 4, 2026

Tried in A11, dd still detecting, the enforced is not working in A11 device?

photo_2026-01-04_22-06-52

@XiaoTong6666
Copy link
Contributor Author

@Stillhard Could you try this commit, please?
b5c0ae2

@Stillhard
Copy link

@Stillhard Could you try this commit, please? b5c0ae2

Tested!
photo_2026-01-06_10-14-54

Wonderful result 😘

Thank you @XiaoTong6666

@JingMatrix
Copy link
Owner

I cannot reproduce the detection on my device, which version of the detector were you using?

Moreover, I think that we don't need to write an exception manually. Hard-coding exception codes is a bad practice, and not elegant.

We can simply raise an error inside the runCatching block of the function handleGenerateKey, which forces the catch part (.getOrElse) to log the error and return TransactionResult.ContinueAndSkipPost

@XiaoTong6666
Copy link
Contributor Author

XiaoTong6666 commented Jan 12, 2026

I cannot reproduce the detection on my device, which version of the detector were you using?

I was using this version of the detector:
https://github.com/XiaoTong6666/KeyDetector/releases/latest

@Stillhard
Copy link

New results:

photo_2026-01-12_07-13-05
photo_2026-01-12_07-13-01

@XiaoTong6666
Copy link
Contributor Author

Moreover, I think that we don't need to write an exception manually. Hard-coding exception codes is a bad practice, and not elegant.

We can simply raise an error inside the runCatching block of the function handleGenerateKey, which forces the catch part (.getOrElse) to log the error and return TransactionResult.ContinueAndSkipPost

Got it, thanks for the clarification. I've updated the code and force-pushed.

It now just throws inside the runCatching path and relies on the existing .getOrElse fallback, without hard-coding any error codes. Let me know if this looks OK?

@JingMatrix
Copy link
Owner

@XiaoTong6666 What should return by your detector ? In my device, it is always Normal (1), with or without this PR.

@XiaoTong6666
Copy link
Contributor Author

XiaoTong6666 commented Jan 20, 2026

@XiaoTong6666 What should return by your detector ? In my device, it is always Normal (1), with or without this PR.

The detection is based on the incorrect behavior of software emulation (Force Generation Mode) when handling oversized challenges (>128 bytes): real TEE hardware naturally rejects them, while the old simulator accepted them. Which is exactly what this PR fixes !

@JingMatrix
Copy link
Owner

JingMatrix commented Jan 20, 2026

How do you detect it? This was my question. Till now, I haven't seen any detector real detects it on my device.

If certain device, probably like my Pixel 6, can actually accept long challenge, then this PR is introducing a detection point for these devices.

@XiaoTong6666
Copy link
Contributor Author

XiaoTong6666 commented Jan 20, 2026

Regarding the reference implementation, the logic in attestation_record.cpp explicitly enforces this limit:

https://cs.android.com/android/platform/superproject/main/+/main:system/keymaster/km_openssl/attestation_record.cpp;l=257

bool is_valid_attestation_challenge(const keymaster_blob_t& attestation_challenge) {
    // TODO(171864369): Limit apps targeting >= API 30 to attestations in the range of
    // [0, 128] bytes.
    return (attestation_challenge.data_length <= kMaximumAttestationChallengeLength);
}

(kMaximumAttestationChallengeLength is defined as 128 in the same file).
Furthermore, Duck Detector is already actively using this discrepancy as a detection method.

@JingMatrix
Copy link
Owner

As said several time, I cannot get detected by these mentioned detectors.

I am asking for a reliable PoC to really detect it.

@XiaoTong6666
Copy link
Contributor Author

Yes, the detection logic is fully open source. You can see the checkAttestationChallenge implementation here:
https://github.com/XiaoTong6666/KeyDetector/blob/main/app/src/main/java/com/xiaotong/keydetector/checker/AttestationComplianceChecker.java

@JingMatrix
Copy link
Owner

No, as I have emphasized many times, I doesn't detect on my device, as seen from the logs and screenshot:
screenshot_2026-01-20T17:20:57

@XiaoTong6666
Copy link
Contributor Author

Please add this to /data/adb/tricky_store/target.txt:

com.xiaotong.keydetector!

(Note the ! suffix to force Generation Mode)

@JingMatrix
Copy link
Owner

I see, it is a design flaw of these detectors.
You must first have an attestation key to invoke the attestation verification with challenge:
https://cs.android.com/android/platform/superproject/main/+/main:cts/tests/tests/keystore/src/android/keystore/cts/AttestKeyTest.java;l=111?q=testAttestationWithNonAttestKey

@XiaoTong6666
Copy link
Contributor Author

The 128-byte limit is a hard constraint of the KeyMint/Keymaster HAL enforced during record construction, so it applies to all Key Attestation flows (both standard factory attestation and Android 12+ app-provisioned attestation) regardless of the signing key used.

Regarding the reference implementation, the logic in attestation_record.cpp explicitly enforces this limit:

https://cs.android.com/android/platform/superproject/main/+/main:system/keymaster/km_openssl/attestation_record.cpp;l=257

bool is_valid_attestation_challenge(const keymaster_blob_t& attestation_challenge) {
    // TODO(171864369): Limit apps targeting >= API 30 to attestations in the range of
    // [0, 128] bytes.
    return (attestation_challenge.data_length <= kMaximumAttestationChallengeLength);
}

(kMaximumAttestationChallengeLength is defined as 128 in the same file). Furthermore, Duck Detector is already actively using this discrepancy as a detection method.

Throws IllegalArgumentException if the challenge exceeds 128 bytes, per Android specs. Also fixes a duplicate assignment typo in KeystoreInterceptor.

Reference: https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])
@JingMatrix JingMatrix merged commit 6e05579 into JingMatrix:main Jan 20, 2026
1 check passed
@XiaoTong6666
Copy link
Contributor Author

XiaoTong6666 commented Jan 20, 2026

The implementation looks perfect. Moving the check to CertificateGenerator is a much cleaner solution as it covers both KeyStore and KeyMint paths automatically. Thanks for merging and fixing the duplicate assignment typo!
By the way, with these fixes applied in my fork , it can now fully bypass my open-source KeyDetector in Generation Mode.

@JingMatrix
Copy link
Owner

@XiaoTong6666 You may create another pull-request when you feel confident. Thanks for the contribution.

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.

3 participants