fix(shim): match real gen-mode auth shape
Real keystore2 (captured on-device, MediaTek SDK 35) emits 11 EC authorizations in the generateKey KeyMetadata: no VENDOR_PATCHLEVEL or BOOT_PATCHLEVEL, and USER_ID tagged at SecurityLevel.SOFTWARE. The shim emitted 13 with both patchlevels and USER_ID at KEYSTORE. Duck-Detector's generate-mode parcel fingerprint stride-walks the reply and keys on the 13-entry layout, so the two extra entries were the tell. Drop both patchlevels from the authorization list (they remain in the attestation extension via AttestationBuilder, so attestation content is unchanged) and move USER_ID to SOFTWARE to mirror the captured device. On-device 23106RN0DA: generate-mode fingerprint signal gone (0 local), TEE tamper score 18 -> 8. Refs Phase 7 .omc/plans/tee-fingerprint-phase-7-generate-mode-coherence.md
This commit is contained in:
+18
-9
@@ -1359,14 +1359,13 @@ private fun KeyMintAttestation.toAuthorizations(
|
||||
if (osPatch != AndroidDeviceUtils.DO_NOT_REPORT) {
|
||||
authList.add(createAuth(Tag.OS_PATCHLEVEL, KeyParameterValue.integer(osPatch)))
|
||||
}
|
||||
val vendorPatch = AndroidDeviceUtils.getVendorPatchLevelLong(callingUid)
|
||||
if (vendorPatch != AndroidDeviceUtils.DO_NOT_REPORT) {
|
||||
authList.add(createAuth(Tag.VENDOR_PATCHLEVEL, KeyParameterValue.integer(vendorPatch)))
|
||||
}
|
||||
val bootPatch = AndroidDeviceUtils.getBootPatchLevelLong(callingUid)
|
||||
if (bootPatch != AndroidDeviceUtils.DO_NOT_REPORT) {
|
||||
authList.add(createAuth(Tag.BOOT_PATCHLEVEL, KeyParameterValue.integer(bootPatch)))
|
||||
}
|
||||
// Real keystore2 (captured on-device: MediaTek, Android 15) does NOT surface
|
||||
// VENDOR_PATCHLEVEL or BOOT_PATCHLEVEL in the generateKey KeyMetadata.authorizations
|
||||
// — they exist only in the attestation extension. Emitting them yielded a
|
||||
// 13-authorization EC reply where the genuine HAL emits 11, which is precisely the
|
||||
// structural tell Duck-Detector's generate-mode parcel fingerprint keys on (its
|
||||
// stride-walk lands on the 13-entry layout). Both values remain in the attestation
|
||||
// extension via AttestationBuilder, so attestation content is unchanged.
|
||||
|
||||
/**
|
||||
* Keystore-enforced authorizations (CREATION_DATETIME, ACTIVE_DATETIME,
|
||||
@@ -1408,7 +1407,17 @@ private fun KeyMintAttestation.toAuthorizations(
|
||||
authList.add(createKeystoreAuth(Tag.UNLOCKED_DEVICE_REQUIRED, KeyParameterValue.boolValue(true)))
|
||||
}
|
||||
|
||||
authList.add(createKeystoreAuth(Tag.USER_ID, KeyParameterValue.integer(callingUid / 100000)))
|
||||
// Captured real keystore2 tags USER_ID at SecurityLevel.SOFTWARE (0), even though
|
||||
// CREATION_DATETIME above is KEYSTORE (100). Mirror that split exactly.
|
||||
authList.add(
|
||||
Authorization().apply {
|
||||
this.keyParameter = KeyParameter().apply {
|
||||
this.tag = Tag.USER_ID
|
||||
this.value = KeyParameterValue.integer(callingUid / 100000)
|
||||
}
|
||||
this.securityLevel = SecurityLevel.SOFTWARE
|
||||
},
|
||||
)
|
||||
|
||||
return authList.toTypedArray()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user