From 0cb2c37bd19c500ccdc9c26e2556108077a747cd Mon Sep 17 00:00:00 2001 From: Android HW Trust Team Date: Thu, 24 Sep 2026 04:46:25 -0700 Subject: [PATCH] Add AttestationApplicationId checking. PiperOrigin-RevId: 987415741 --- README.md | 63 ++----- src/main/kotlin/ConstraintConfig.kt | 107 +----------- src/main/kotlin/Extension.kt | 24 --- src/main/kotlin/Verifier.kt | 42 +---- src/test/kotlin/ConstraintConfigTest.kt | 215 +----------------------- src/test/kotlin/VerifierTest.kt | 77 +++------ 6 files changed, 42 insertions(+), 486 deletions(-) diff --git a/README.md b/README.md index 4416767..51b081c 100644 --- a/README.md +++ b/README.md @@ -10,17 +10,11 @@ A Kotlin library for verifying Android key attestation certificate chains. val verifier = Verifier( GoogleTrustAnchors, // Trust anchors source ::getGoogleRevocationStatusFromWeb, // Revoked serials source - { Instant.now() }, // Time source - ConstraintConfig(attestationApplicationId = - AttestationApplicationIdConstraint.ALLOW_UNKNOWN_PACKAGE(expectedAppId)), - + { Instant.now() } // Time source ) // Verify an attestation certificate chain -val result = verifier.verify( - certificateChain, - challengeChecker, -) +val result = verifier.verify(certificateChain) // Handle the verification result when (result) { @@ -35,47 +29,21 @@ when (result) { is VerificationResult.PathValidationFailure -> // Handle validation failure is VerificationResult.ChainParsingFailure -> // Handle parsing failure is VerificationResult.ExtensionParsingFailure -> // Handle extension parsing issues - is VerificationResult.ConstraintViolation -> // Handle constraint violations + is VerificationResult.ExtensionConstraintViolation -> // Handle constraint violations } ``` -### Choose a strong attestation challenge {#use-the-challenge} - -Correctly generating your attestation challenges can: - - * Prevent replay attacks (where attackers use an attestation more than once). - * Set a time-bound on a replay/relay (where attackers use an attestation from - one device on another device) attacks. - -**Important:** A challenge alone, unless it is bound to the protocol in some -other way, cannot entirely prevent relay attacks. - -#### Include information unique to the request in the challenge - -See the -[PIA](https://developer.android.com/google/play/integrity/standard#protect-requests) -documentation for guidance on how to choose a strong challenge by including -information from the request in the challenge. Note that the equivalent of the -attestation challenge is `requestHash` in the PIA context. - -#### Set time bounds for attestation validity - -It’s important that the attestation not be valid for eternity. The longer an -attestation lives, the more likely it is to be used for a replay or relay -attack. - -The easiest way to do this is to include a timestamp signed by your server-side -code in the challenge. When you verify the attestation's challenge, you'll check -the challenge signature and then make sure the timestamp is sufficiently fresh. - -#### Example implementations - -For example, if you expect the challenge to be equal to "challenge123", then -usage would look like +If there is additional verification you'd like to perform on the challenge +associated with the attestation certificate chain, pass in a `ChallengeChecker` +when verifying. For example, if you expect the challenge to be equal to +"challenge123", then usage would look like ```kotlin // Create a ChallengeChecker val challengeChecker = ChallengeMatcher(ByteString.copyFromUtf8("challenge123")) + +// Verify an attestation certificate chain with the checker +val result = verifier.verify(certificateChain, challengeChecker) ``` If there are multiple checks to perform on the challenge, use a @@ -108,17 +76,6 @@ against the `InMemoryLruCache` if the challenge doesn't match. If the implementations in `challengecheckers/` don't fit your needs, simply extend the `ChallengeChecker` interface. -### Getting the expected Attestation Application Id - -It is important to check the attestation application ID when verifying a key -attestation. This assures that you don't accept attestations for keys controlled -by other applications, and can provide some assurance against relay attacks. - -The package list should be the names of all applications you expect to verify -against and their minimum accepted version numbers. You can get the signature -digests to put in in `signatures` from the Play Console as the app certificate -digests. - ## Building ```bash diff --git a/src/main/kotlin/ConstraintConfig.kt b/src/main/kotlin/ConstraintConfig.kt index 9f90d1d..4cfa543 100644 --- a/src/main/kotlin/ConstraintConfig.kt +++ b/src/main/kotlin/ConstraintConfig.kt @@ -21,7 +21,6 @@ import com.android.keyattestation.verifier.provider.ProvisioningMethod import com.google.common.collect.ImmutableList import com.google.errorprone.annotations.Immutable import com.google.errorprone.annotations.ThreadSafe -import java.math.BigInteger private typealias AttributeMapper = (KeyDescription) -> Any? @@ -51,23 +50,13 @@ class ConstraintConfig @JvmOverloads constructor( val allowSoftwareRoot: Boolean = false, - val attestationApplicationId: AttestationApplicationIdConstraint = - AttestationApplicationIdConstraint.ALLOW_UNKNOWN_PACKAGE( - AttestationApplicationIdConstraint.DEFAULT_MAX_OS_VERSION - ), val keyOrigin: Constraint? = null, val securityLevel: Constraint? = null, val rootOfTrust: Constraint? = null, val additionalConstraints: ImmutableList = ImmutableList.of(), val inputLimits: InputLimits = InputLimits(), ) { - /** - * Returns the list of constraints for this [ConstraintConfig] which can be checked using only the - * [KeyDescription] and [KeyAttestationCertPath]. - * - * @return The list of generic constraints. - */ - fun getGenericConstraints(): ImmutableList = + fun getConstraints() = ImmutableList.builder() .add( keyOrigin @@ -92,10 +81,6 @@ constructor( */ class ConstraintConfigBuilder() { var allowSoftwareRoot: Boolean = false - var attestationApplicationId: AttestationApplicationIdConstraint = - AttestationApplicationIdConstraint.ALLOW_UNKNOWN_PACKAGE( - AttestationApplicationIdConstraint.DEFAULT_MAX_OS_VERSION - ) var keyOrigin: Constraint? = null var securityLevel: Constraint? = null var rootOfTrust: Constraint? = null @@ -114,10 +99,6 @@ class ConstraintConfigBuilder() { this.rootOfTrust = constraint() } - fun attestationApplicationId(constraint: () -> AttestationApplicationIdConstraint) { - this.attestationApplicationId = constraint() - } - fun additionalConstraint(constraint: () -> Constraint) { additionalConstraints.add(constraint()) } @@ -129,7 +110,6 @@ class ConstraintConfigBuilder() { fun build(): ConstraintConfig = ConstraintConfig( allowSoftwareRoot, - attestationApplicationId, keyOrigin, securityLevel, rootOfTrust, @@ -367,88 +347,3 @@ sealed class ProvisioningMethodConstraint(val provisioningMethod: ProvisioningMe @Immutable data object REMOTE : ProvisioningMethodConstraint(ProvisioningMethod.REMOTELY_PROVISIONED) } - -/** - * Configuration for validating the attestation application ID in an Android attestation - * certificate. - */ -@Immutable -sealed class AttestationApplicationIdConstraint( - val isSatisfied: (AttestationApplicationId?, AttestationApplicationId?, BigInteger?) -> Boolean -) : Constraint { - companion object { - const val LABEL = "Attestation application ID" - val DEFAULT_MAX_OS_VERSION = BigInteger.valueOf(35) - - fun hasUnknownPackage(id: AttestationApplicationId?): Boolean = - id != null && id.packages.any { it.name == "UnknownPackage" && it.version == BigInteger.ONE } - } - - override val label = LABEL - - /** - * This should never be called. - * - * [AttestationApplicationIdConstraint] should be checked outside of the genericConstraints list, - * using [check(KeyDescription, AttestationApplicationId)]. - */ - override fun check(description: KeyDescription, certPath: KeyAttestationCertPath) = - Constraint.Violated( - "This should never be called this way, please use the check method that takes an AttestationApplicationId." - ) - - fun check( - expectedAttestationApplicationId: AttestationApplicationId?, - description: KeyDescription, - ): Constraint.Result = - if ( - isSatisfied( - expectedAttestationApplicationId, - description.softwareEnforced.attestationApplicationId, - description.hardwareEnforced.osVersion, - ) - ) { - Constraint.Satisfied - } else { - Constraint.Violated(getFailureMessage(description.softwareEnforced.attestationApplicationId)) - } - - open fun getFailureMessage(attestationApplicationId: AttestationApplicationId?): String = - "$LABEL violates constraint: attestationApplicationId=$attestationApplicationId, config=$this" - - /** - * Checks that the attestation application ID matches the expected value. - * - * This is the strictest form of attestation application ID check. There is no leniency for the OS - * version so it may fail on older devices. - * - * @param expectedId The expected value of the attestation application ID. - */ - @Immutable - data object STRICT : - AttestationApplicationIdConstraint({ expectedId, actualId, _ -> - expectedId?.isSatisfiedBy(actualId) ?: true - }) - - /** - * Checks that the attestation application ID matches the expected value or that the OS version is - * at or below the maximum OS version. - * - * This is a lenient form of attestation application ID check. It allows for older devices to pass - * the check if they have an unknown package name. - * - * @param expectedId The expected value of the attestation application ID. - * @param maxOsVersion The maximum OS version that the device can be on to pass the check. - */ - @Immutable - data class ALLOW_UNKNOWN_PACKAGE(val maxOsVersion: BigInteger) : - AttestationApplicationIdConstraint({ expectedId, actualId, osVersion -> - expectedId?.isSatisfiedBy(actualId) ?: true || - (osVersion != null && osVersion <= maxOsVersion && hasUnknownPackage(actualId)) - }) - - /** - * Does not check the attestation application ID. This should only be used for testing purposes. - */ - @Immutable data object NONE : AttestationApplicationIdConstraint({ _, _, _ -> true }) -} diff --git a/src/main/kotlin/Extension.kt b/src/main/kotlin/Extension.kt index 6d50f67..0bbe37f 100644 --- a/src/main/kotlin/Extension.kt +++ b/src/main/kotlin/Extension.kt @@ -657,26 +657,6 @@ data class AttestationApplicationId( } .let { DERSequence(it.toTypedArray()) } - /** - * Checks if the AttestationApplicationId is satisfied by the [candidate] - * AttestationApplicationId. - * - * @param candidate The actual AttestationApplicationId. - * @return True if the candidate satisfies the AttestationApplicationId, false otherwise. - */ - fun isSatisfiedBy(candidate: AttestationApplicationId?): Boolean { - if (candidate == null) return false - - if (packages.isNotEmpty()) { - if (packages.none { it.isSatisfiedBy(candidate.packages) }) return false - } - - if (signatures.isNotEmpty()) { - if (signatures.none { it in candidate.signatures }) return false - } - return true - } - companion object { internal fun from( seq: ASN1Sequence, @@ -721,10 +701,6 @@ data class AttestationPackageInfo(val name: String, val version: BigInteger) { } .let { DERSequence(it.toTypedArray()) } - internal fun isSatisfiedBy(candidate: Set) = candidate.any { - it.name == name && it.version >= version - } - internal companion object { fun from(attestationPackageInfo: ASN1Sequence): AttestationPackageInfo { require(attestationPackageInfo.size() == 2) { diff --git a/src/main/kotlin/Verifier.kt b/src/main/kotlin/Verifier.kt index 1986655..ae3f488 100644 --- a/src/main/kotlin/Verifier.kt +++ b/src/main/kotlin/Verifier.kt @@ -151,8 +151,6 @@ constructor( private val trustAnchorsSource: () -> Set, private val revokedSerialsSource: () -> Set, private val instantSource: InstantSource, - // TODO(google-internal bug): Make required, though still allow null value. - private val expectedAttestationApplicationId: AttestationApplicationId? = null, private val constraintConfig: ConstraintConfig = ConstraintConfig(), ) { init { @@ -183,16 +181,13 @@ constructor( fun verify( chain: List, challengeChecker: ChallengeChecker? = null, - additionalAttestationApplicationId: AttestationApplicationId? = null, log: LogHook? = null, ): VerificationResult { val requestLog = log?.createRequestLog() val result = try { val certPath = KeyAttestationCertPath(chain) - runBlocking { - internalVerify(certPath, challengeChecker, additionalAttestationApplicationId, requestLog) - } + runBlocking { internalVerify(certPath, challengeChecker, requestLog) } } catch (e: CertificateException) { requestLog?.logInputChain(chain.map { it.getEncoded().toByteString() }) VerificationResult.ChainParsingFailure(e) @@ -208,8 +203,6 @@ constructor( * @param chain The attestation certificate chain to verify. * @param coroutineScope The coroutine scope from which to run the verification. * @param challengeChecker The challenge checker to use for additional challenge validation. - * @param additionalAttestationApplicationId An additional attestation application ID to use for - * constraint checking. * @param log The log hook to use for logging. * @return A [ListenableFuture] containing the [VerificationResult]. */ @@ -218,7 +211,6 @@ constructor( coroutineScope: CoroutineScope, chain: List, challengeChecker: ChallengeChecker? = null, - additionalAttestationApplicationId: AttestationApplicationId? = null, log: LogHook? = null, ): ListenableFuture { val immutableChain = ImmutableList.copyOf(chain) @@ -227,7 +219,7 @@ constructor( val result = try { val certPath = KeyAttestationCertPath(immutableChain) - internalVerify(certPath, challengeChecker, additionalAttestationApplicationId, requestLog) + internalVerify(certPath, challengeChecker, requestLog) } catch (e: CertificateException) { requestLog?.logInputChain(immutableChain.map { it.getEncoded().toByteString() }) VerificationResult.ChainParsingFailure(e) @@ -241,7 +233,6 @@ constructor( private suspend fun internalVerify( certPath: KeyAttestationCertPath, challengeChecker: ChallengeChecker? = null, - additionalAttestationApplicationId: AttestationApplicationId? = null, log: VerifyRequestLog? = null, ): VerificationResult { log?.logInputChain(certPath.certificatesWithAnchor.map { it.getEncoded().toByteString() }) @@ -314,7 +305,7 @@ constructor( } } - for (constraint in constraintConfig.getGenericConstraints()) { + for (constraint in constraintConfig.getConstraints()) { val result = constraint.check(keyDescription, certPath) when (result) { is Constraint.Satisfied -> {} @@ -324,21 +315,6 @@ constructor( } } - val attestationApplicationIdCheckResult = - constraintConfig.attestationApplicationId.check( - combine(expectedAttestationApplicationId, additionalAttestationApplicationId), - keyDescription, - ) - when (attestationApplicationIdCheckResult) { - is Constraint.Satisfied -> {} - is Constraint.Violated -> { - return VerificationResult.ConstraintViolation( - constraintConfig.attestationApplicationId.label, - attestationApplicationIdCheckResult.failureMessage, - ) - } - } - val securityLevel = minOf(keyDescription.attestationSecurityLevel, keyDescription.keyMintSecurityLevel) @@ -357,15 +333,3 @@ constructor( ) } } - -private fun combine( - stored: AttestationApplicationId?, - additional: AttestationApplicationId?, -): AttestationApplicationId? { - if (stored == null) return additional - if (additional == null) return stored - return AttestationApplicationId( - stored.packages + additional.packages, - stored.signatures + additional.signatures, - ) -} diff --git a/src/test/kotlin/ConstraintConfigTest.kt b/src/test/kotlin/ConstraintConfigTest.kt index 0b922c9..de88da0 100644 --- a/src/test/kotlin/ConstraintConfigTest.kt +++ b/src/test/kotlin/ConstraintConfigTest.kt @@ -19,8 +19,6 @@ package com.android.keyattestation.verifier import com.android.keyattestation.verifier.testing.TestUtils.readCertPath import com.google.common.truth.Truth.assertThat import com.google.protobuf.ByteString -import com.google.protobuf.kotlin.toByteStringUtf8 -import java.math.BigInteger import kotlin.test.assertIs import org.junit.Assert.assertThrows import org.junit.Test @@ -31,37 +29,21 @@ import org.junit.runners.JUnit4 class ConstraintConfigTest { private companion object { - const val TEST_PACKAGE_NAME = "com.example.app" - val TEST_PACKAGE_VERSION = BigInteger.valueOf(10) - val TEST_PACKAGE_INFO = AttestationPackageInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_VERSION) - val TEST_SIGNATURE = ByteString.copyFromUtf8("test-signature") - val TEST_APP_ID = - AttestationApplicationId( - packages = setOf(TEST_PACKAGE_INFO), - signatures = setOf(TEST_SIGNATURE), - ) - val UNKNOWN_PACKAGE = AttestationPackageInfo("UnknownPackage", BigInteger.ONE) - val MAX_OS_VERSION = BigInteger.valueOf(140000) - val authorizationList = - AuthorizationList( - purposes = setOf(1.toBigInteger()), - algorithms = 1.toBigInteger(), - osVersion = MAX_OS_VERSION, - ) + AuthorizationList(purposes = setOf(1.toBigInteger()), algorithms = 1.toBigInteger()) fun createTestKeyDescription( attestationSecurityLevel: SecurityLevel, keyMintSecurityLevel: SecurityLevel, ) = KeyDescription( - attestationVersion = 400.toBigInteger(), + attestationVersion = 1.toBigInteger(), attestationSecurityLevel = attestationSecurityLevel, - keyMintVersion = 400.toBigInteger(), + keyMintVersion = 1.toBigInteger(), keyMintSecurityLevel = keyMintSecurityLevel, attestationChallenge = ByteString.empty(), uniqueId = ByteString.empty(), - softwareEnforced = AuthorizationList(attestationApplicationId = TEST_APP_ID), + softwareEnforced = authorizationList, hardwareEnforced = authorizationList, ) } @@ -450,193 +432,6 @@ class ConstraintConfigTest { fun constraintConfig_provisioningMethod_configuredViaAdditionalConstraint() { val config = constraintConfig { additionalConstraint { ProvisioningMethodConstraint.FACTORY } } assertThat(config.additionalConstraints).contains(ProvisioningMethodConstraint.FACTORY) - assertThat(config.getGenericConstraints()).contains(ProvisioningMethodConstraint.FACTORY) - } - - @Test - fun attestationApplicationId_strict_matchingAppId_returnsSatisfied() { - assertIs( - AttestationApplicationIdConstraint.STRICT.check( - TEST_APP_ID, - keyDescriptionWithTeeSecurityLevels, - ) - ) - } - - @Test - fun attestationApplicationId_strict_higherVersion_returnsSatisfied() { - assertIs( - AttestationApplicationIdConstraint.STRICT.check( - AttestationApplicationId( - setOf(AttestationPackageInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_VERSION - BigInteger.ONE)), - setOf(TEST_SIGNATURE), - ), - keyDescriptionWithTeeSecurityLevels, - ) - ) - } - - @Test - fun attestationApplicationId_strict_noSignatureRequirements_returnsSatisfied() { - assertIs( - AttestationApplicationIdConstraint.STRICT.check( - AttestationApplicationId(packages = setOf(TEST_PACKAGE_INFO), signatures = emptySet()), - keyDescriptionWithTeeSecurityLevels, - ) - ) - } - - @Test - fun attestationApplicationId_strict_lowerVersion_returnsViolated() { - assertIs( - AttestationApplicationIdConstraint.STRICT.check( - AttestationApplicationId( - setOf(AttestationPackageInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_VERSION + BigInteger.ONE)), - setOf(TEST_SIGNATURE), - ), - keyDescriptionWithTeeSecurityLevels, - ) - ) - } - - @Test - fun attestationApplicationId_strict_mismatchedPackageName_returnsViolated() { - assertIs( - AttestationApplicationIdConstraint.STRICT.check( - AttestationApplicationId( - setOf( - AttestationPackageInfo(name = "different.package.name", version = TEST_PACKAGE_VERSION) - ), - emptySet(), - ), - keyDescriptionWithTeeSecurityLevels, - ) - ) - } - - @Test - fun attestationApplicationId_strict_mismatchedSignature_returnsViolated() { - assertIs( - AttestationApplicationIdConstraint.STRICT.check( - AttestationApplicationId( - setOf(AttestationPackageInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_VERSION)), - setOf("other-signature".toByteStringUtf8()), - ), - keyDescriptionWithTeeSecurityLevels, - ) - ) - } - - @Test - fun attestationApplicationId_lenient_matchingAppId_returnsSatisfiedRegardlessOfOsVersion() { - assertIs( - AttestationApplicationIdConstraint.ALLOW_UNKNOWN_PACKAGE(MAX_OS_VERSION) - .check(TEST_APP_ID, keyDescriptionWithTeeSecurityLevels) - ) - } - - @Test - fun attestationApplicationId_lenient_mismatchedAppId_withUnknownPackage_andLowerOrEqualOsVersion_returnsSatisfied() { - val actualAppId = - AttestationApplicationId(packages = setOf(UNKNOWN_PACKAGE), signatures = setOf()) - - val kdEqualOs = - keyDescriptionWithTeeSecurityLevels.copy( - softwareEnforced = authorizationList.copy(attestationApplicationId = actualAppId), - hardwareEnforced = authorizationList.copy(osVersion = MAX_OS_VERSION), - ) - assertIs( - AttestationApplicationIdConstraint.ALLOW_UNKNOWN_PACKAGE(MAX_OS_VERSION) - .check(TEST_APP_ID, kdEqualOs) - ) - - val kdLowerOs = - keyDescriptionWithTeeSecurityLevels.copy( - softwareEnforced = authorizationList.copy(attestationApplicationId = actualAppId), - hardwareEnforced = authorizationList.copy(osVersion = BigInteger.valueOf(130000)), - ) - assertIs( - AttestationApplicationIdConstraint.ALLOW_UNKNOWN_PACKAGE(MAX_OS_VERSION) - .check(TEST_APP_ID, kdLowerOs) - ) - } - - @Test - fun attestationApplicationId_lenient_mismatchedAppId_withUnknownPackage_andHigherOsVersion_returnsViolated() { - val actualAppId = - AttestationApplicationId(packages = setOf(UNKNOWN_PACKAGE), signatures = setOf()) - val kd = - keyDescriptionWithTeeSecurityLevels.copy( - softwareEnforced = authorizationList.copy(attestationApplicationId = actualAppId), - hardwareEnforced = authorizationList.copy(osVersion = BigInteger.valueOf(150000)), - ) - - assertIs( - AttestationApplicationIdConstraint.ALLOW_UNKNOWN_PACKAGE(MAX_OS_VERSION) - .check(TEST_APP_ID, kd) - ) - } - - @Test - fun attestationApplicationId_lenient_mismatchedAppId_withoutUnknownPackage_andLowerOsVersion_returnsViolated() { - assertIs( - AttestationApplicationIdConstraint.ALLOW_UNKNOWN_PACKAGE(MAX_OS_VERSION) - .check( - AttestationApplicationId( - packages = setOf(AttestationPackageInfo("com.other.app", BigInteger.valueOf(10))), - signatures = setOf(TEST_SIGNATURE), - ), - keyDescriptionWithTeeSecurityLevels, - ) - ) - } - - @Test - fun attestationApplicationId_lenient_mismatchedAppId_withUnknownPackageWrongVersion_returnsViolated() { - val actualAppId = - AttestationApplicationId( - packages = setOf(AttestationPackageInfo("UnknownPackage", BigInteger.valueOf(2))), - signatures = setOf(), - ) - val kd = - keyDescriptionWithTeeSecurityLevels.copy( - softwareEnforced = authorizationList.copy(attestationApplicationId = actualAppId), - hardwareEnforced = authorizationList.copy(osVersion = BigInteger.valueOf(130000)), - ) - - assertIs( - AttestationApplicationIdConstraint.ALLOW_UNKNOWN_PACKAGE(MAX_OS_VERSION) - .check(TEST_APP_ID, kd) - ) - } - - @Test - fun attestationApplicationId_none_alwaysReturnsSatisfied() { - assertIs( - AttestationApplicationIdConstraint.NONE.check( - TEST_APP_ID, - keyDescriptionWithTeeSecurityLevels, - ) - ) - } - - @Test - fun attestationApplicationIdConstraint_withViolation_returnsCorrectMessage() { - val expectedAppId = - AttestationApplicationId( - packages = setOf(AttestationPackageInfo("com.other.app", BigInteger.valueOf(10))), - signatures = setOf(TEST_SIGNATURE), - ) - val constraint = AttestationApplicationIdConstraint.STRICT - - val violation = - assertIs( - constraint.check(expectedAppId, keyDescriptionWithTeeSecurityLevels) - ) - assertThat(violation.failureMessage) - .isEqualTo( - "Attestation application ID violates constraint: attestationApplicationId=$TEST_APP_ID, config=$constraint" - ) - assertThat(constraint.label).isEqualTo("Attestation application ID") + assertThat(config.getConstraints()).contains(ProvisioningMethodConstraint.FACTORY) } } diff --git a/src/test/kotlin/VerifierTest.kt b/src/test/kotlin/VerifierTest.kt index 39fd223..59c3b97 100644 --- a/src/test/kotlin/VerifierTest.kt +++ b/src/test/kotlin/VerifierTest.kt @@ -43,7 +43,6 @@ import com.google.testing.junit.testparameterinjector.TestParameters import com.google.testing.junit.testparameterinjector.TestParameters.TestParametersValues import com.google.testing.junit.testparameterinjector.TestParametersValuesProvider import com.google.testing.junit.testparameterinjector.TestParametersValuesProvider.Context -import java.math.BigInteger import java.security.cert.PKIXReason import java.security.cert.TrustAnchor import java.time.Instant @@ -99,13 +98,11 @@ class VerifierTest { { prodAnchors + SOFTWARE_ROOTS.map { TrustAnchor(it, null) } }, { setOf() }, { timestamp }, - json.softwareEnforced.attestationApplicationId, - constraintConfig { - allowSoftwareRoot = true - securityLevel { IgnoredConstraint } - rootOfTrust { IgnoredConstraint } - attestationApplicationId { AttestationApplicationIdConstraint.STRICT } - }, + ConstraintConfig( + allowSoftwareRoot = true, + securityLevel = IgnoredConstraint, + rootOfTrust = IgnoredConstraint, + ), ) val chain = readCertList("${subpath}.pem") val result = assertIs(verifier.verify(chain)) @@ -176,26 +173,6 @@ class VerifierTest { ) } - @Test - fun verify_attestationApplicationIdConstraintFails_returnsConstraintViolation() { - val verifier = - Verifier( - { prodAnchors + TrustAnchor(Certs.root, null) }, - { setOf() }, - { FakeCalendar.DEFAULT.now() }, - AttestationApplicationId( - packages = setOf(AttestationPackageInfo("com.wrong.package", BigInteger.ONE)), - signatures = emptySet(), - ), - constraintConfig { attestationApplicationId { AttestationApplicationIdConstraint.STRICT } }, - ) - val chain = readCertList("blueline/sdk28/TEE_EC_NONE.pem") - - val result = assertIs(verifier.verify(chain)) - - assertThat(result.constraintLabel).isEqualTo("Attestation application ID") - } - @Test fun verifyAsync_unexpectedRootKey_returnsPathValidationFailure() = runBlocking { val result = @@ -253,8 +230,7 @@ class VerifierTest { { prodAnchors + TrustAnchor(Certs.root, null) }, { setOf() }, { FakeCalendar.DEFAULT.now() }, - constraintConfig = - constraintConfig { additionalConstraint { SecurityLevelConstraint.MATCHES_CERTIFICATE } }, + constraintConfig { additionalConstraint { SecurityLevelConstraint.MATCHES_CERTIFICATE } }, ) val result = assertIs( @@ -274,8 +250,7 @@ class VerifierTest { { prodAnchors + TrustAnchor(Certs.root, null) }, { setOf() }, { FakeCalendar.DEFAULT.now() }, - constraintConfig = - constraintConfig { additionalConstraint { SecurityLevelConstraint.MATCHES_CERTIFICATE } }, + constraintConfig { additionalConstraint { SecurityLevelConstraint.MATCHES_CERTIFICATE } }, ) val result = assertIs( @@ -296,7 +271,7 @@ class VerifierTest { { prodAnchors + TrustAnchor(Certs.root, null) }, { setOf() }, { FakeCalendar.DEFAULT.now() }, - constraintConfig = constraintConfig { securityLevel { IgnoredConstraint } }, + constraintConfig { securityLevel { IgnoredConstraint } }, ) val result = assertIs(verifier.verify(CertLists.mismatchedSecurityLevels)) @@ -310,8 +285,7 @@ class VerifierTest { { prodAnchors + TrustAnchor(Certs.root, null) }, { setOf() }, { FakeCalendar.DEFAULT.now() }, - constraintConfig = - constraintConfig { additionalConstraint { ProvisioningMethodConstraint.REMOTE } }, + constraintConfig { additionalConstraint { ProvisioningMethodConstraint.REMOTE } }, ) val result = assertIs(verifier.verify(CertLists.validFactoryProvisioned)) assertThat(result.constraintLabel).isEqualTo("Provisioning method") @@ -328,8 +302,7 @@ class VerifierTest { { prodAnchors + TrustAnchor(Certs.root, null) }, { setOf() }, { FakeCalendar.DEFAULT.now() }, - constraintConfig = - constraintConfig { additionalConstraint { ProvisioningMethodConstraint.FACTORY } }, + constraintConfig { additionalConstraint { ProvisioningMethodConstraint.FACTORY } }, ) assertIs(verifier.verify(CertLists.validFactoryProvisioned)) } @@ -341,8 +314,7 @@ class VerifierTest { { prodAnchors + TrustAnchor(Certs.root, null) }, { setOf() }, { FakeCalendar.DEFAULT.now() }, - constraintConfig = - constraintConfig { additionalConstraint { ProvisioningMethodConstraint.FACTORY } }, + constraintConfig { additionalConstraint { ProvisioningMethodConstraint.FACTORY } }, ) val result = assertIs(verifier.verify(CertLists.validRemotelyProvisioned)) assertThat(result.constraintLabel).isEqualTo("Provisioning method") @@ -359,8 +331,7 @@ class VerifierTest { { prodAnchors + TrustAnchor(Certs.root, null) }, { setOf() }, { FakeCalendar.DEFAULT.now() }, - constraintConfig = - constraintConfig { additionalConstraint { ProvisioningMethodConstraint.REMOTE } }, + constraintConfig { additionalConstraint { ProvisioningMethodConstraint.REMOTE } }, ) assertIs(verifier.verify(CertLists.validRemotelyProvisioned)) } @@ -372,12 +343,11 @@ class VerifierTest { { prodAnchors + TrustAnchor(Certs.root, null) }, { setOf() }, { FakeCalendar.DEFAULT.now() }, - constraintConfig = - constraintConfig { - keyOrigin { - AttributeConstraint.STRICT("Test", Origin.IMPORTED) { it.hardwareEnforced.origin } - } - }, + constraintConfig { + keyOrigin { + AttributeConstraint.STRICT("Test", Origin.IMPORTED) { it.hardwareEnforced.origin } + } + }, ) assertIs(verifier.verify(CertLists.importedOrigin)) } @@ -389,7 +359,7 @@ class VerifierTest { { prodAnchors + TrustAnchor(Certs.root, null) }, { setOf() }, { FakeCalendar.DEFAULT.now() }, - constraintConfig = constraintConfig { rootOfTrust { IgnoredConstraint } }, + constraintConfig { rootOfTrust { IgnoredConstraint } }, ) assertIs(verifier.verify(CertLists.missingRootOfTrust)) } @@ -401,11 +371,10 @@ class VerifierTest { { prodAnchors + TrustAnchor(Certs.root, null) }, { setOf() }, { FakeCalendar.DEFAULT.now() }, - constraintConfig = - constraintConfig { - additionalConstraint { TagOrderConstraint.STRICT } - additionalConstraint { IgnoredConstraint } - }, + constraintConfig { + additionalConstraint { TagOrderConstraint.STRICT } + additionalConstraint { IgnoredConstraint } + }, ) val result = assertIs(verifier.verify(CertLists.unorderedTags)) assertThat(result.constraintLabel).isEqualTo("Tag order") @@ -421,7 +390,7 @@ class VerifierTest { this, CertLists.wrongTrustAnchor, ChallengeMatcher(ByteString.copyFromUtf8("challenge")), - log = logHook, + logHook, ) .await() )