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.
This commit is contained in:
@@ -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. */
|
/** Parses the critical components from an existing attestation extension. */
|
||||||
private fun parseAttestationExtension(certHolder: X509CertificateHolder): ParsedAttestation? {
|
private fun parseAttestationExtension(certHolder: X509CertificateHolder): ParsedAttestation? {
|
||||||
val extension = certHolder.getExtension(ATTESTATION_OID) ?: return null
|
val extension = certHolder.getExtension(ATTESTATION_OID) ?: return null
|
||||||
val sequence = ASN1Sequence.getInstance(extension.extnValue.octets)
|
val sequence = ASN1Sequence.getInstance(extension.extnValue.octets)
|
||||||
val allFields = sequence.toArray()
|
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 =
|
val teeEnforced =
|
||||||
allFields[AttestationConstants.KEY_DESCRIPTION_TEE_ENFORCED_INDEX] as ASN1Sequence
|
allFields[AttestationConstants.KEY_DESCRIPTION_TEE_ENFORCED_INDEX] as ASN1Sequence
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user