Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/main/kotlin/Extension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ data class ProvisioningInfoMap(
) {
fun cborEncode(): ByteArray {
val map = CborModelMap()
map.put(UnsignedInteger(1L), certificatesIssued.asDataItem())
map.put(UnsignedInteger(CERTIFICATES_ISSUED_TAG), certificatesIssued.asDataItem())
return cborEncode(map)
}

Expand All @@ -113,6 +113,7 @@ data class ProvisioningInfoMap(
* https://developer.android.com/privacy-and-security/security-key-attestation#provisioning_attestation_ext_schema
*/
@JvmField val OID = ASN1ObjectIdentifier("1.3.6.1.4.1.11129.2.1.30")
internal const val CERTIFICATES_ISSUED_TAG = 1L

@JvmStatic
@JvmOverloads
Expand All @@ -132,7 +133,7 @@ data class ProvisioningInfoMap(
private fun from(seq: CborModelMap): ProvisioningInfoMap {
require(seq.keys.size >= 1)
return ProvisioningInfoMap(
certificatesIssued = seq.get(UnsignedInteger(1L)).asInteger(),
certificatesIssued = seq.get(UnsignedInteger(CERTIFICATES_ISSUED_TAG)).asInteger(),
)
}
}
Expand Down
62 changes: 62 additions & 0 deletions src/main/kotlin/testing/Certs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.android.keyattestation.verifier.testing

import co.nstant.`in`.cbor.CborBuilder
import com.android.keyattestation.verifier.AttestationPackageInfo
import com.android.keyattestation.verifier.AuthorizationList
import com.android.keyattestation.verifier.KeyDescription
Expand All @@ -25,6 +26,7 @@ import com.android.keyattestation.verifier.ProvisioningInfoMap
import com.android.keyattestation.verifier.RootOfTrust
import com.android.keyattestation.verifier.SecurityLevel
import com.android.keyattestation.verifier.VerifiedBootState
import com.android.keyattestation.verifier.cborEncode
import com.android.keyattestation.verifier.provider.KeyAttestationCertPath
import com.google.protobuf.ByteString
import com.google.protobuf.kotlin.toByteStringUtf8
Expand Down Expand Up @@ -406,6 +408,58 @@ object CertLists {
)
}

val maxCertificatesIssued by lazy {
val rkpAttestationCert =
certFactory.generateRkpAttestationCert(
securityLevel = SecurityLevel.TRUSTED_ENVIRONMENT,
serialNumber = TEST_ATTESTATION_SERIAL_NUMBER,
extraExtension =
Extension(
ProvisioningInfoMap.OID,
/* critical= */ false,
ProvisioningInfoMap(
certificatesIssued = Int.MAX_VALUE,
)
.cborEncode(),
),
)
listOf(
certFactory.generateLeafCert(issuer = rkpAttestationCert.subject),
rkpAttestationCert,
certFactory.rkpIntermediate,
Certs.remoteIntermediate,
certFactory.root,
)
}

val outOfBoundsCertificatesIssued by lazy {
val rkpAttestationCert =
certFactory.generateRkpAttestationCert(
securityLevel = SecurityLevel.TRUSTED_ENVIRONMENT,
serialNumber = TEST_ATTESTATION_SERIAL_NUMBER,
extraExtension =
Extension(
ProvisioningInfoMap.OID,
/* critical= */ false,
cborEncode(
CborBuilder()
.addMap()
.put(ProvisioningInfoMap.CERTIFICATES_ISSUED_TAG, Int.MAX_VALUE.toLong() + 1)
.end()
.build()
.single()
),
),
)
listOf(
certFactory.generateLeafCert(issuer = rkpAttestationCert.subject),
rkpAttestationCert,
certFactory.rkpIntermediate,
Certs.remoteIntermediate,
certFactory.root,
)
}

/* Different revoked serial numbers for testing. */
@JvmField val REVOKED_SERIAL_NUMBER = 42.toBigInteger()
@JvmField val REVOKED_SERIAL_NUMBER_BIG = 8000000000000.toBigInteger()
Expand Down Expand Up @@ -471,6 +525,14 @@ object Chains {
KeyAttestationCertPath(CertLists.validRemotelyProvisioned)
}

@JvmStatic
val maxCertificatesIssued by lazy { KeyAttestationCertPath(CertLists.maxCertificatesIssued) }

@JvmStatic
val outOfBoundsCertificatesIssued by lazy {
KeyAttestationCertPath(CertLists.outOfBoundsCertificatesIssued)
}

@JvmStatic val wrongTrustAnchor by lazy { KeyAttestationCertPath(CertLists.wrongTrustAnchor) }

/* A chain where the intermediate certificate has a mismatched subject. */
Expand Down
19 changes: 10 additions & 9 deletions src/main/kotlin/testing/KeyAttestationCertFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ internal class KeyAttestationCertFactory(
serialNumber: BigInteger,
notBefore: Date = fakeCalendar.lastWeek(),
notAfter: Date = fakeCalendar.nextWeek(),
extraExtension: Extension =
Extension(
ProvisioningInfoMap.OID,
/* critical= */ false,
ProvisioningInfoMap(
certificatesIssued = 1,
)
.cborEncode(),
),
) =
generateAttestationCert(
signingKey = rkpKey.private,
Expand All @@ -131,15 +140,7 @@ internal class KeyAttestationCertFactory(
serialNumber,
notBefore,
notAfter,
extraExtension =
Extension(
ProvisioningInfoMap.OID,
/* critical= */ false,
ProvisioningInfoMap(
certificatesIssued = 1,
)
.cborEncode(),
),
extraExtension = extraExtension,
)

internal fun generateAttestationCert(
Expand Down
21 changes: 21 additions & 0 deletions src/test/kotlin/ExtensionTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,27 @@ class ExtensionTest {
assertThat(provisioningInfo).isEqualTo(expectedProvisioningInfo)
}

@Test
fun parseFrom_maxCertificatesIssuedFromAttestationCert_success() {
val expectedProvisioningInfo =
ProvisioningInfoMap(
certificatesIssued = Int.MAX_VALUE,
)
val provisioningInfo =
ProvisioningInfoMap.parseFrom(Chains.maxCertificatesIssued.attestationCert(), InputLimits())
assertThat(provisioningInfo).isEqualTo(expectedProvisioningInfo)
}

@Test
fun parseFrom_outOfBoundsCertificatesIssuedFromAttestationCert_throws() {
assertThrows(IllegalArgumentException::class.java) {
ProvisioningInfoMap.parseFrom(
Chains.outOfBoundsCertificatesIssued.attestationCert(),
InputLimits(),
)
}
}

@Test
@TestParameters(valuesProvider = TestCaseProvider::class)
fun parseFrom_success(model: String, sdk: Int) {
Expand Down
14 changes: 14 additions & 0 deletions src/test/kotlin/VerifierTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,20 @@ class VerifierTest {
assertIs<VerificationResult.Success>(verifier.verify(CertLists.validRemotelyProvisioned))
}

@Test
fun verify_maxCertificatesIssued_succeedsWithMaxDeviceInformation() {
val result =
assertIs<VerificationResult.Success>(verifier.verify(CertLists.maxCertificatesIssued))
assertThat(result.deviceInformation?.certificatesIssued).isEqualTo(Int.MAX_VALUE)
}

@Test
fun verify_outOfBoundsCertificatesIssued_succeedsWithNullDeviceInformation() {
val result =
assertIs<VerificationResult.Success>(verifier.verify(CertLists.outOfBoundsCertificatesIssued))
assertThat(result.deviceInformation).isNull()
}

@Test
fun importedOrigins_customConfig_succeeds() {
val verifier =
Expand Down
Loading