From d60ad8fe478e9f4cda3dbf7bd5b243cfa104cc3b Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Fri, 30 Jan 2026 18:16:13 +0100 Subject: [PATCH] Handle swapped attestation lists on certain Android 11 devices (#108) Observed an abnormal Keymaster attestation structure on certain Android 11 devices where the `softwareEnforced` and `teeEnforced` authorization lists were swapped in order. This is a deviation from the documented specification and the behavior seen on most devices. This non-compliance caused parsing failures, as the code expected the `teeEnforced` list to be at a fixed index (7). On the affected devices, this index contained the `softwareEnforced` list, which critically lacks the `TAG_ROOT_OF_TRUST` needed for successful validation and patching. This commit introduces a defensive normalization step to handle this device-specific anomaly gracefully: 1. Before parsing, the code now inspects the ASN.1 sequence at the expected `softwareEnforced` index (6). 2. It checks for the presence of the `TAG_ROOT_OF_TRUST`, which can only exist in the TEE-enforced list. 3. If the tag is found, the code concludes the lists are swapped and corrects the `allFields` array in-place by swapping the elements at indices 6 and 7. By normalizing the data structure at the beginning, the rest of the parsing and patching logic can proceed without modification, ensuring correct operation on both compliant and non-compliant devices. --- .../attestation/AttestationPatcher.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/app/src/main/java/org/matrix/TEESimulator/attestation/AttestationPatcher.kt b/app/src/main/java/org/matrix/TEESimulator/attestation/AttestationPatcher.kt index ec9fa20..ee7552c 100644 --- a/app/src/main/java/org/matrix/TEESimulator/attestation/AttestationPatcher.kt +++ b/app/src/main/java/org/matrix/TEESimulator/attestation/AttestationPatcher.kt @@ -216,11 +216,37 @@ object AttestationPatcher { } } + // Function to check if a given ASN1Sequence contains the Root of Trust tag. + private fun sequenceContainsRootOfTrust(seq: ASN1Encodable): Boolean { + if (seq !is ASN1Sequence) return false + return seq.any { element -> + (element as? ASN1TaggedObject)?.tagNo == AttestationConstants.TAG_ROOT_OF_TRUST + } + } + /** Parses the critical components from an existing attestation extension. */ private fun parseAttestationExtension(certHolder: X509CertificateHolder): ParsedAttestation? { val extension = certHolder.getExtension(ATTESTATION_OID) ?: return null val sequence = ASN1Sequence.getInstance(extension.extnValue.octets) val allFields = sequence.toArray() + + // Check if the fields are in the wrong order and swap them if necessary. + val softwareEnforcedCandidate = + allFields[AttestationConstants.KEY_DESCRIPTION_SOFTWARE_ENFORCED_INDEX] + val teeEnforcedCandidate = + allFields[AttestationConstants.KEY_DESCRIPTION_TEE_ENFORCED_INDEX] + // The signature of a swapped order: the RoT is in the software list's position. + if ( + sequenceContainsRootOfTrust(softwareEnforcedCandidate) && + !sequenceContainsRootOfTrust(teeEnforcedCandidate) + ) { + // Swap the elements in the array to restore the standard order. + allFields[AttestationConstants.KEY_DESCRIPTION_SOFTWARE_ENFORCED_INDEX] = + teeEnforcedCandidate + allFields[AttestationConstants.KEY_DESCRIPTION_TEE_ENFORCED_INDEX] = + softwareEnforcedCandidate + } + val teeEnforced = allFields[AttestationConstants.KEY_DESCRIPTION_TEE_ENFORCED_INDEX] as ASN1Sequence