@@ -22,6 +22,7 @@ import com.android.keyattestation.verifier.provider.KeyAttestationProvider
2222import com.android.keyattestation.verifier.provider.ProvisioningMethod
2323import com.android.keyattestation.verifier.provider.RevocationChecker
2424import com.google.common.collect.ImmutableList
25+ import com.google.common.flogger.GoogleLogger
2526import com.google.common.util.concurrent.ListenableFuture
2627import com.google.errorprone.annotations.ThreadSafe
2728import com.google.protobuf.ByteString
@@ -42,28 +43,44 @@ import kotlinx.coroutines.guava.future
4243import kotlinx.coroutines.runBlocking
4344
4445/* * The result of verifying an Android Key Attestation certificate chain. */
46+ @ThreadSafe
4547sealed interface VerificationResult {
48+ @ThreadSafe
4649 data class Success (
47- val publicKey : PublicKey ,
50+ @field:ThreadSafe. Suppress (reason = " PublicKey is immutable") val publicKey : PublicKey ,
4851 val challenge : ByteString ,
4952 val securityLevel : SecurityLevel ,
5053 val verifiedBootState : VerifiedBootState ,
5154 val deviceInformation : ProvisioningInfoMap ? ,
55+ @field:ThreadSafe.Suppress (reason = "DeviceIdentity is deeply immutable")
5256 val attestedDeviceIds : DeviceIdentity ,
5357 ) : VerificationResult
5458
55- data object ChallengeMismatch : VerificationResult
59+ @ThreadSafe data object ChallengeMismatch : VerificationResult
5660
57- data class PathValidationFailure (val cause : CertPathValidatorException ) : VerificationResult
61+ @ThreadSafe
62+ data class PathValidationFailure (
63+ @field:ThreadSafe.Suppress (reason = "Exceptions are generally immutable after creation")
64+ val cause : CertPathValidatorException
65+ ) : VerificationResult
5866
59- data class ChainParsingFailure (val cause : Exception ) : VerificationResult
67+ @ThreadSafe
68+ data class ChainParsingFailure (
69+ @field:ThreadSafe.Suppress (reason = "Exceptions are generally immutable after creation")
70+ val cause : Exception
71+ ) : VerificationResult
6072
61- data class ExtensionParsingFailure (val cause : ExtensionParsingException ) : VerificationResult
73+ @ThreadSafe
74+ data class ExtensionParsingFailure (
75+ @field:ThreadSafe.Suppress (reason = "Exceptions are generally immutable after creation")
76+ val cause : ExtensionParsingException
77+ ) : VerificationResult
6278
79+ @ThreadSafe
6380 data class ExtensionConstraintViolation (val cause : String , val reason : KeyAttestationReason ) :
6481 VerificationResult
6582
66- data object SoftwareAttestationUnsupported : VerificationResult
83+ @ThreadSafe data object SoftwareAttestationUnsupported : VerificationResult
6784}
6885
6986/* *
@@ -141,6 +158,10 @@ open class Verifier(
141158 private val revokedSerialsSource : () -> Set <String >,
142159 private val instantSource : InstantSource ,
143160) {
161+ companion object {
162+ private val logger = GoogleLogger .forEnclosingClass()
163+ }
164+
144165 init {
145166 Security .addProvider(KeyAttestationProvider ())
146167 for (anchor in trustAnchorsSource()) {
@@ -170,17 +191,17 @@ open class Verifier(
170191 challengeChecker : ChallengeChecker ? = null,
171192 log : LogHook ? = null,
172193 ): VerificationResult {
173- val requestLog = log? .createRequestLog()
194+ val requestLog = ( log ? : GoogleLoggerLogHook (logger)) .createRequestLog()
174195 val result =
175196 try {
176197 val certPath = KeyAttestationCertPath (chain)
177198 runBlocking { internalVerify(certPath, challengeChecker, requestLog) }
178199 } catch (e: CertificateException ) {
179- requestLog? .logInputChain(chain.map { it.getEncoded().toByteString() })
200+ requestLog.logInputChain(chain.map { it.getEncoded().toByteString() })
180201 VerificationResult .ChainParsingFailure (e)
181202 }
182- requestLog? .logResult(result)
183- requestLog? .flush()
203+ requestLog.logResult(result)
204+ requestLog.flush()
184205 return result
185206 }
186207
@@ -203,17 +224,17 @@ open class Verifier(
203224 ): ListenableFuture <VerificationResult > {
204225 val immutableChain = ImmutableList .copyOf(chain)
205226 return coroutineScope.future {
206- val requestLog = log? .createRequestLog()
227+ val requestLog = ( log ? : GoogleLoggerLogHook (logger)) .createRequestLog()
207228 val result =
208229 try {
209230 val certPath = KeyAttestationCertPath (immutableChain)
210231 internalVerify(certPath, challengeChecker, requestLog)
211232 } catch (e: CertificateException ) {
212- requestLog? .logInputChain(immutableChain.map { it.getEncoded().toByteString() })
233+ requestLog.logInputChain(immutableChain.map { it.getEncoded().toByteString() })
213234 VerificationResult .ChainParsingFailure (e)
214235 }
215- requestLog? .logResult(result)
216- requestLog? .flush()
236+ requestLog.logResult(result)
237+ requestLog.flush()
217238 result
218239 }
219240 }
@@ -242,6 +263,7 @@ open class Verifier(
242263 try {
243264 certPath.attestationCert().provisioningInfo()
244265 } catch (e: Exception ) {
266+ logger.atWarning().withCause(e).log(" Failed to parse provisioning info map." )
245267 log?.logInfoMessage(" Failed to parse provisioning info map: ${e.message} " )
246268 null
247269 }
@@ -312,10 +334,12 @@ open class Verifier(
312334 }
313335 val rootOfTrust =
314336 keyDescription.hardwareEnforced.rootOfTrust
315- ? : return VerificationResult .ExtensionConstraintViolation (
316- " hardwareEnforced.rootOfTrust is null" ,
317- KeyAttestationReason .ROOT_OF_TRUST_MISSING ,
318- )
337+ ? : run {
338+ return VerificationResult .ExtensionConstraintViolation (
339+ " hardwareEnforced.rootOfTrust is null" ,
340+ KeyAttestationReason .ROOT_OF_TRUST_MISSING ,
341+ )
342+ }
319343 return VerificationResult .Success (
320344 pathValidationResult.publicKey,
321345 keyDescription.attestationChallenge,
@@ -325,4 +349,46 @@ open class Verifier(
325349 DeviceIdentity .parseFrom(keyDescription),
326350 )
327351 }
352+
353+ @ThreadSafe
354+ private class GoogleLoggerLogHook (private val logger : GoogleLogger ) : LogHook {
355+ override fun createRequestLog (): VerifyRequestLog {
356+ return GoogleLoggerRequestLog (logger)
357+ }
358+ }
359+
360+ @ThreadSafe
361+ private class GoogleLoggerRequestLog (private val logger : GoogleLogger ) : VerifyRequestLog {
362+ override fun logInputChain (inputChain : List <ByteString >) {}
363+
364+ override fun logResult (result : VerificationResult ) {
365+ when (result) {
366+ is VerificationResult .Success -> logger.atInfo().log(" Attestation verification succeeded." )
367+ is VerificationResult .ChallengeMismatch ->
368+ logger.atWarning().log(" Attestation challenge mismatch." )
369+ is VerificationResult .PathValidationFailure ->
370+ logger.atWarning().withCause(result.cause).log(" Certificate path validation failed." )
371+ is VerificationResult .ChainParsingFailure ->
372+ logger.atWarning().withCause(result.cause).log(" Failed to parse certificate chain." )
373+ is VerificationResult .ExtensionParsingFailure ->
374+ logger
375+ .atWarning()
376+ .withCause(result.cause)
377+ .log(" Failed to parse key description extension." )
378+ is VerificationResult .ExtensionConstraintViolation ->
379+ logger.atWarning().log(" Constraint violation: %s" , result.cause)
380+ is VerificationResult .SoftwareAttestationUnsupported -> {}
381+ }
382+ }
383+
384+ override fun logKeyDescription (keyDescription : KeyDescription ) {}
385+
386+ override fun logProvisioningInfoMap (provisioningInfoMap : ProvisioningInfoMap ) {}
387+
388+ override fun logCertSerialNumbers (certSerialNumbers : List <String >) {}
389+
390+ override fun logInfoMessage (infoMessage : String ) {}
391+
392+ override fun flush () {}
393+ }
328394}
0 commit comments