1 package com.android.server.security 2 3 import android.app.Activity 4 import android.content.Context 5 import android.os.Bundle 6 import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE 7 import android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY 8 import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE 9 import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS 10 import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE 11 import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY 12 import androidx.test.ext.junit.runners.AndroidJUnit4 13 import androidx.test.filters.SmallTest 14 import androidx.test.platform.app.InstrumentationRegistry 15 import com.google.common.truth.Truth.assertThat 16 import org.junit.Before 17 import org.junit.Test 18 import org.junit.runner.RunWith 19 import org.mockito.Mock 20 import org.mockito.MockitoAnnotations 21 import java.io.ByteArrayOutputStream 22 import java.security.cert.Certificate 23 import java.security.cert.CertificateFactory 24 import java.security.cert.TrustAnchor 25 import java.security.cert.X509Certificate 26 import java.time.LocalDate 27 28 /** Test for Peer Device attestation verifier. */ 29 @SmallTest 30 @RunWith(AndroidJUnit4::class) 31 class AttestationVerificationPeerDeviceVerifierTest { 32 private val certificateFactory = CertificateFactory.getInstance("X.509") 33 @Mock private lateinit var context: Context 34 private lateinit var trustAnchors: HashSet<TrustAnchor> 35 36 @Before 37 fun setup() { 38 MockitoAnnotations.initMocks(this) 39 40 val rootCerts = TEST_ROOT_CERT_FILENAME.fromPEMFileToCerts() 41 trustAnchors = HashSet<TrustAnchor>() 42 rootCerts.forEach { 43 trustAnchors.add(TrustAnchor(it as X509Certificate, null)) 44 } 45 } 46 47 @Test 48 fun verifyAttestation_returnsSuccessTypeChallenge() { 49 val verifier = AttestationVerificationPeerDeviceVerifier( 50 context, trustAnchors, false, LocalDate.of(2022, 2, 1), 51 LocalDate.of(2021, 8, 1)) 52 val challengeRequirements = Bundle() 53 challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) 54 55 val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, 56 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) 57 assertThat(result).isEqualTo(RESULT_SUCCESS) 58 } 59 60 @Test 61 fun verifyAttestation_returnsSuccessLocalPatchOlderThanOneYear() { 62 val verifier = AttestationVerificationPeerDeviceVerifier( 63 context, trustAnchors, false, LocalDate.of(2022, 2, 1), 64 LocalDate.of(2021, 1, 1)) 65 val challengeRequirements = Bundle() 66 challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) 67 68 val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, 69 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) 70 assertThat(result).isEqualTo(RESULT_SUCCESS) 71 } 72 73 @Test 74 fun verifyAttestation_returnsSuccessTypePublicKey() { 75 val verifier = AttestationVerificationPeerDeviceVerifier( 76 context, trustAnchors, false, LocalDate.of(2022, 2, 1), 77 LocalDate.of(2021, 8, 1)) 78 79 val leafCert = 80 (TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToCerts() as List)[0] 81 as X509Certificate 82 val pkRequirements = Bundle() 83 pkRequirements.putByteArray(PARAM_PUBLIC_KEY, leafCert.publicKey.encoded) 84 85 val result = verifier.verifyAttestation( 86 TYPE_PUBLIC_KEY, pkRequirements, 87 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) 88 assertThat(result).isEqualTo(RESULT_SUCCESS) 89 } 90 91 @Test 92 fun verifyAttestation_returnsSuccessOwnedBySystem() { 93 val verifier = AttestationVerificationPeerDeviceVerifier( 94 context, trustAnchors, false, LocalDate.of(2022, 2, 1), 95 LocalDate.of(2021, 1, 1)) 96 val challengeRequirements = Bundle() 97 challengeRequirements.putByteArray(PARAM_CHALLENGE, "activeUnlockValid".encodeToByteArray()) 98 challengeRequirements.putBoolean("android.key_owned_by_system", true) 99 100 val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, 101 TEST_OWNED_BY_SYSTEM_FILENAME.fromPEMFileToByteArray()) 102 assertThat(result).isEqualTo(RESULT_SUCCESS) 103 } 104 105 @Test 106 fun verifyAttestation_returnsFailureOwnedBySystem() { 107 val verifier = AttestationVerificationPeerDeviceVerifier( 108 context, trustAnchors, false, LocalDate.of(2022, 2, 1), 109 LocalDate.of(2021, 1, 1)) 110 val challengeRequirements = Bundle() 111 challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) 112 challengeRequirements.putBoolean("android.key_owned_by_system", true) 113 114 val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, 115 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) 116 assertThat(result).isEqualTo(RESULT_FAILURE) 117 } 118 119 @Test 120 fun verifyAttestation_returnsFailurePatchDateNotWithinOneYearLocalPatch() { 121 val verifier = AttestationVerificationPeerDeviceVerifier( 122 context, trustAnchors, false, LocalDate.of(2023, 3, 1), 123 LocalDate.of(2023, 2, 1)) 124 val challengeRequirements = Bundle() 125 challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) 126 127 val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, 128 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) 129 assertThat(result).isEqualTo(RESULT_FAILURE) 130 } 131 132 @Test 133 fun verifyAttestation_returnsFailureTrustedAnchorEmpty() { 134 val verifier = AttestationVerificationPeerDeviceVerifier( 135 context, HashSet(), false, LocalDate.of(2022, 1, 1), 136 LocalDate.of(2022, 1, 1)) 137 val challengeRequirements = Bundle() 138 challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) 139 140 val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, 141 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) 142 assertThat(result).isEqualTo(RESULT_FAILURE) 143 } 144 145 @Test 146 fun verifyAttestation_returnsFailureTrustedAnchorMismatch() { 147 val badTrustAnchorsCerts = TEST_ATTESTATION_CERT_FILENAME.fromPEMFileToCerts() 148 val badTrustAnchors = HashSet<TrustAnchor>() 149 badTrustAnchorsCerts.forEach { 150 badTrustAnchors.add(TrustAnchor(it as X509Certificate, null)) 151 } 152 153 val verifier = AttestationVerificationPeerDeviceVerifier( 154 context, badTrustAnchors, false, LocalDate.of(2022, 1, 1), 155 LocalDate.of(2022, 1, 1)) 156 val challengeRequirements = Bundle() 157 challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) 158 159 val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, 160 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) 161 assertThat(result).isEqualTo(RESULT_FAILURE) 162 } 163 164 fun verifyAttestation_returnsFailureChallenge() { 165 val verifier = AttestationVerificationPeerDeviceVerifier( 166 context, trustAnchors, false, LocalDate.of(2022, 1, 1), 167 LocalDate.of(2022, 1, 1)) 168 val challengeRequirements = Bundle() 169 challengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray()) 170 171 val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, 172 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) 173 assertThat(result).isEqualTo(RESULT_FAILURE) 174 } 175 176 private fun String.fromPEMFileToCerts(): Collection<Certificate> { 177 return certificateFactory.generateCertificates( 178 InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets() 179 .open(this)) 180 } 181 182 private fun String.fromPEMFileToByteArray(): ByteArray { 183 val certs = this.fromPEMFileToCerts() 184 val bos = ByteArrayOutputStream() 185 certs.forEach { 186 bos.write(it.encoded) 187 } 188 return bos.toByteArray() 189 } 190 191 class TestActivity : Activity() { 192 override fun onCreate(savedInstanceState: Bundle?) { 193 super.onCreate(savedInstanceState) 194 } 195 } 196 197 companion object { 198 private const val TEST_ROOT_CERT_FILENAME = "test_root_certs.pem" 199 private const val TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME = 200 "test_attestation_with_root_certs.pem" 201 private const val TEST_ATTESTATION_CERT_FILENAME = "test_attestation_wrong_root_certs.pem" 202 private const val TEST_OWNED_BY_SYSTEM_FILENAME = "test_owned_by_system_certs.pem" 203 } 204 } 205