feat(keystore): probe trail for generateKey
Add a debug-only structured log line per generateKey, emitted at every outcome (SKIP, the four REJECTs, FORWARD_HAL, FORGE, PATCH, PASSTHROUGH). Each line carries the resolving package (via the cached ConfigurationManager.getPackagesForUid), alias, algorithm, StrongBox flag, the attestation tag set, and the verdict, so triaging "app X broke" becomes a single logcat grep instead of decoding parcel dumps. Gated on SystemLogger.isDebugBuild: release builds return before any string is built, keeping the path silent and artifact-free. Logs to logcat only, never to files.
This commit is contained in:
+45
-10
@@ -443,8 +443,39 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
var parsedParams = KeyMintAttestation(params)
|
var parsedParams = KeyMintAttestation(params)
|
||||||
val isAttestKeyRequest = parsedParams.isAttestKey()
|
val isAttestKeyRequest = parsedParams.isAttestKey()
|
||||||
|
|
||||||
|
val hasDeviceIdAttestation = params.any {
|
||||||
|
it.tag == Tag.ATTESTATION_ID_IMEI ||
|
||||||
|
it.tag == Tag.ATTESTATION_ID_MEID ||
|
||||||
|
it.tag == Tag.ATTESTATION_ID_SERIAL ||
|
||||||
|
it.tag == Tag.DEVICE_UNIQUE_ATTESTATION ||
|
||||||
|
it.tag == Tag.ATTESTATION_ID_SECOND_IMEI
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug-only probe trail: one greppable line per generateKey carrying the resolving
|
||||||
|
// package and the outcome. Release builds short-circuit before any string is built,
|
||||||
|
// so this is silent and artifact-free in production.
|
||||||
|
fun logProbe(outcome: String) {
|
||||||
|
if (!SystemLogger.isDebugBuild) return
|
||||||
|
val pkg = ConfigurationManager.getPackagesForUid(callingUid).firstOrNull()
|
||||||
|
?: "uid:$callingUid"
|
||||||
|
val tags = buildList {
|
||||||
|
if (parsedParams.attestationChallenge != null) add("challenge")
|
||||||
|
if (parsedParams.brand != null || parsedParams.device != null ||
|
||||||
|
parsedParams.product != null || parsedParams.manufacturer != null ||
|
||||||
|
parsedParams.model != null) add("props")
|
||||||
|
if (hasDeviceIdAttestation) add("ids")
|
||||||
|
if (params.any { it.tag == Tag.INCLUDE_UNIQUE_ID }) add("unique_id")
|
||||||
|
}.joinToString(",")
|
||||||
|
SystemLogger.debug(
|
||||||
|
"[probe] tx=$txId uid=$callingUid pkg=$pkg alias=${keyDescriptor.alias} " +
|
||||||
|
"algo=${parsedParams.algorithm} sb=${securityLevel == SecurityLevel.STRONGBOX} " +
|
||||||
|
"tags=[$tags] -> $outcome"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (ConfigurationManager.shouldSkipUid(callingUid)
|
if (ConfigurationManager.shouldSkipUid(callingUid)
|
||||||
&& attestationKey == null && !isAttestKeyRequest) {
|
&& attestationKey == null && !isAttestKeyRequest) {
|
||||||
|
logProbe("SKIP")
|
||||||
return TransactionResult.ContinueAndSkipPost
|
return TransactionResult.ContinueAndSkipPost
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,27 +487,22 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
val challenge = parsedParams.attestationChallenge
|
val challenge = parsedParams.attestationChallenge
|
||||||
if (challenge != null && challenge.size > AttestationConstants.CHALLENGE_LENGTH_LIMIT) {
|
if (challenge != null && challenge.size > AttestationConstants.CHALLENGE_LENGTH_LIMIT) {
|
||||||
SystemLogger.warning("[TX_ID: $txId] Rejecting oversized attestation challenge: ${challenge.size} bytes (max ${AttestationConstants.CHALLENGE_LENGTH_LIMIT})")
|
SystemLogger.warning("[TX_ID: $txId] Rejecting oversized attestation challenge: ${challenge.size} bytes (max ${AttestationConstants.CHALLENGE_LENGTH_LIMIT})")
|
||||||
|
logProbe("REJECT:challenge_len")
|
||||||
return InterceptorUtils.createErrorReply(KEYMINT_INVALID_INPUT_LENGTH)
|
return InterceptorUtils.createErrorReply(KEYMINT_INVALID_INPUT_LENGTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.any { it.tag == Tag.CREATION_DATETIME }) {
|
if (params.any { it.tag == Tag.CREATION_DATETIME }) {
|
||||||
SystemLogger.warning("[TX_ID: $txId] Rejecting CREATION_DATETIME in generateKey params")
|
SystemLogger.warning("[TX_ID: $txId] Rejecting CREATION_DATETIME in generateKey params")
|
||||||
|
logProbe("REJECT:creation_datetime")
|
||||||
return InterceptorUtils.createErrorReply(RESPONSE_INVALID_ARGUMENT)
|
return InterceptorUtils.createErrorReply(RESPONSE_INVALID_ARGUMENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.any { it.tag == Tag.DEVICE_UNIQUE_ATTESTATION } && !AndroidPermissionUtils.hasUniqueIdAttestationPermission(callingUid)) {
|
if (params.any { it.tag == Tag.DEVICE_UNIQUE_ATTESTATION } && !AndroidPermissionUtils.hasUniqueIdAttestationPermission(callingUid)) {
|
||||||
SystemLogger.warning("[TX_ID: $txId] Rejecting DEVICE_UNIQUE_ATTESTATION for uid=$callingUid")
|
SystemLogger.warning("[TX_ID: $txId] Rejecting DEVICE_UNIQUE_ATTESTATION for uid=$callingUid")
|
||||||
|
logProbe("REJECT:cannot_attest_unique")
|
||||||
return InterceptorUtils.createErrorReply(KEYMINT_CANNOT_ATTEST_IDS)
|
return InterceptorUtils.createErrorReply(KEYMINT_CANNOT_ATTEST_IDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
val hasDeviceIdAttestation = params.any {
|
|
||||||
it.tag == Tag.ATTESTATION_ID_IMEI ||
|
|
||||||
it.tag == Tag.ATTESTATION_ID_MEID ||
|
|
||||||
it.tag == Tag.ATTESTATION_ID_SERIAL ||
|
|
||||||
it.tag == Tag.DEVICE_UNIQUE_ATTESTATION ||
|
|
||||||
it.tag == Tag.ATTESTATION_ID_SECOND_IMEI
|
|
||||||
}
|
|
||||||
|
|
||||||
// Device-ID attestation mirrors a real KeyMint: privileged callers (GMS/system)
|
// Device-ID attestation mirrors a real KeyMint: privileged callers (GMS/system)
|
||||||
// get it, ordinary apps get CANNOT_ATTEST_IDS. The permission check below is that
|
// get it, ordinary apps get CANNOT_ATTEST_IDS. The permission check below is that
|
||||||
// rule. Device-property attestation (BRAND/MODEL/...) is honored unconditionally —
|
// rule. Device-property attestation (BRAND/MODEL/...) is honored unconditionally —
|
||||||
@@ -484,6 +510,7 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
// path needs. Capability is keyed to the device we present, not the real (dead) TEE.
|
// path needs. Capability is keyed to the device we present, not the real (dead) TEE.
|
||||||
if(hasDeviceIdAttestation && !AndroidPermissionUtils.hasDeviceAttestationPermission(callingUid)) {
|
if(hasDeviceIdAttestation && !AndroidPermissionUtils.hasDeviceAttestationPermission(callingUid)) {
|
||||||
SystemLogger.warning("[TX_ID: $txId] Rejecting DEVICE_ID_ATTESTATION for uid=$callingUid")
|
SystemLogger.warning("[TX_ID: $txId] Rejecting DEVICE_ID_ATTESTATION for uid=$callingUid")
|
||||||
|
logProbe("REJECT:cannot_attest_ids")
|
||||||
return InterceptorUtils.createErrorReply(KEYMINT_CANNOT_ATTEST_IDS)
|
return InterceptorUtils.createErrorReply(KEYMINT_CANNOT_ATTEST_IDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,6 +544,7 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
|
|
||||||
if (securityLevel == SecurityLevel.STRONGBOX && !isStrongBoxCapable(parsedParams)) {
|
if (securityLevel == SecurityLevel.STRONGBOX && !isStrongBoxCapable(parsedParams)) {
|
||||||
SystemLogger.info("[TX_ID: $txId] StrongBox-unsupported params (algo=${parsedParams.algorithm} size=${parsedParams.keySize}) → forwarding to HAL for rejection")
|
SystemLogger.info("[TX_ID: $txId] StrongBox-unsupported params (algo=${parsedParams.algorithm} size=${parsedParams.keySize}) → forwarding to HAL for rejection")
|
||||||
|
logProbe("FORWARD_HAL")
|
||||||
return TransactionResult.ContinueAndSkipPost
|
return TransactionResult.ContinueAndSkipPost
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,9 +559,16 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
SystemLogger.trace { "[TRACE-$txId] dispatch: forceGen=$forceGenerate hasChallenge=${challenge != null} isSymmetric=$isSymmetric isAttestKey=$isAttestKeyRequest" }
|
SystemLogger.trace { "[TRACE-$txId] dispatch: forceGen=$forceGenerate hasChallenge=${challenge != null} isSymmetric=$isSymmetric isAttestKey=$isAttestKeyRequest" }
|
||||||
|
|
||||||
when {
|
when {
|
||||||
forceGenerate -> doSoftwareKeyGen(callingUid, keyDescriptor, attestationKey, parsedParams, keyId, isAttestKeyRequest)
|
forceGenerate -> {
|
||||||
parsedParams.attestationChallenge != null -> TransactionResult.Continue
|
logProbe("FORGE")
|
||||||
|
doSoftwareKeyGen(callingUid, keyDescriptor, attestationKey, parsedParams, keyId, isAttestKeyRequest)
|
||||||
|
}
|
||||||
|
parsedParams.attestationChallenge != null -> {
|
||||||
|
logProbe("PATCH")
|
||||||
|
TransactionResult.Continue
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
logProbe("PASSTHROUGH")
|
||||||
cleanupKeyData(keyId)
|
cleanupKeyData(keyId)
|
||||||
TransactionResult.Continue
|
TransactionResult.Continue
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user