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 android.util.IndentingPrintWriter 13 import android.util.Log 14 import androidx.test.ext.junit.runners.AndroidJUnit4 15 import androidx.test.filters.SmallTest 16 import androidx.test.platform.app.InstrumentationRegistry 17 import com.android.server.security.AttestationVerificationManagerService.DumpLogger 18 import com.google.common.truth.Truth.assertThat 19 import java.io.ByteArrayOutputStream 20 import java.io.PrintWriter 21 import java.io.StringWriter 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 import org.junit.After 28 import org.junit.Before 29 import org.junit.Test 30 import org.junit.runner.RunWith 31 import org.mockito.Mock 32 import org.mockito.MockitoAnnotations 33 34 35 /** Test for Peer Device attestation verifier. */ 36 @SmallTest 37 @RunWith(AndroidJUnit4::class) 38 class AttestationVerificationPeerDeviceVerifierTest { 39 private val certificateFactory = CertificateFactory.getInstance("X.509") 40 @Mock private lateinit var context: Context 41 private val dumpLogger = DumpLogger() 42 private lateinit var trustAnchors: HashSet<TrustAnchor> 43 44 @Before setupnull45 fun setup() { 46 MockitoAnnotations.initMocks(this) 47 48 val rootCerts = TEST_ROOT_CERT_FILENAME.fromPEMFileToCerts() 49 trustAnchors = HashSet<TrustAnchor>() 50 rootCerts.forEach { 51 trustAnchors.add(TrustAnchor(it as X509Certificate, null)) 52 } 53 } 54 55 @After dumpAndLognull56 fun dumpAndLog() { 57 val dump = dumpLogger.getDump() 58 Log.d(TAG, "$dump") 59 } 60 61 @Test verifyAttestation_returnsSuccessTypeChallengenull62 fun verifyAttestation_returnsSuccessTypeChallenge() { 63 val verifier = AttestationVerificationPeerDeviceVerifier( 64 context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1), 65 LocalDate.of(2021, 8, 1) 66 ) 67 val challengeRequirements = Bundle() 68 challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) 69 70 val result = verifier.verifyAttestation( 71 TYPE_CHALLENGE, challengeRequirements, 72 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() 73 ) 74 assertThat(result).isEqualTo(RESULT_SUCCESS) 75 } 76 77 @Test verifyAttestation_returnsSuccessLocalPatchOlderThanOneYearnull78 fun verifyAttestation_returnsSuccessLocalPatchOlderThanOneYear() { 79 val verifier = AttestationVerificationPeerDeviceVerifier( 80 context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1), 81 LocalDate.of(2021, 1, 1) 82 ) 83 val challengeRequirements = Bundle() 84 challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) 85 86 val result = verifier.verifyAttestation( 87 TYPE_CHALLENGE, challengeRequirements, 88 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() 89 ) 90 assertThat(result).isEqualTo(RESULT_SUCCESS) 91 } 92 93 @Test verifyAttestation_returnsSuccessTypePublicKeynull94 fun verifyAttestation_returnsSuccessTypePublicKey() { 95 val verifier = AttestationVerificationPeerDeviceVerifier( 96 context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1), 97 LocalDate.of(2021, 8, 1) 98 ) 99 100 val leafCert = 101 (TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToCerts() as List)[0] 102 as X509Certificate 103 val pkRequirements = Bundle() 104 pkRequirements.putByteArray(PARAM_PUBLIC_KEY, leafCert.publicKey.encoded) 105 106 val result = verifier.verifyAttestation( 107 TYPE_PUBLIC_KEY, pkRequirements, 108 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() 109 ) 110 assertThat(result).isEqualTo(RESULT_SUCCESS) 111 } 112 113 @Test verifyAttestation_returnsSuccessOwnedBySystemnull114 fun verifyAttestation_returnsSuccessOwnedBySystem() { 115 val verifier = AttestationVerificationPeerDeviceVerifier( 116 context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1), 117 LocalDate.of(2021, 1, 1) 118 ) 119 val challengeRequirements = Bundle() 120 challengeRequirements.putByteArray(PARAM_CHALLENGE, "activeUnlockValid".encodeToByteArray()) 121 challengeRequirements.putBoolean("android.key_owned_by_system", true) 122 123 val result = verifier.verifyAttestation( 124 TYPE_CHALLENGE, challengeRequirements, 125 TEST_OWNED_BY_SYSTEM_FILENAME.fromPEMFileToByteArray() 126 ) 127 128 assertThat(result).isEqualTo(RESULT_SUCCESS) 129 } 130 131 @Test verifyAttestation_returnsFailureOwnedBySystemnull132 fun verifyAttestation_returnsFailureOwnedBySystem() { 133 val verifier = AttestationVerificationPeerDeviceVerifier( 134 context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1), 135 LocalDate.of(2021, 1, 1) 136 ) 137 val challengeRequirements = Bundle() 138 challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) 139 challengeRequirements.putBoolean("android.key_owned_by_system", true) 140 141 val result = verifier.verifyAttestation( 142 TYPE_CHALLENGE, challengeRequirements, 143 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() 144 ) 145 assertThat(result).isEqualTo(RESULT_FAILURE) 146 } 147 148 @Test verifyAttestation_returnsFailurePatchDateNotWithinOneYearLocalPatchnull149 fun verifyAttestation_returnsFailurePatchDateNotWithinOneYearLocalPatch() { 150 val verifier = AttestationVerificationPeerDeviceVerifier( 151 context, dumpLogger, trustAnchors, false, LocalDate.of(2023, 3, 1), 152 LocalDate.of(2023, 2, 1) 153 ) 154 val challengeRequirements = Bundle() 155 challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) 156 157 val result = verifier.verifyAttestation( 158 TYPE_CHALLENGE, challengeRequirements, 159 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() 160 ) 161 assertThat(result).isEqualTo(RESULT_FAILURE) 162 } 163 164 @Test verifyAttestation_returnsFailureTrustedAnchorEmptynull165 fun verifyAttestation_returnsFailureTrustedAnchorEmpty() { 166 val verifier = AttestationVerificationPeerDeviceVerifier( 167 context, dumpLogger, HashSet(), false, LocalDate.of(2022, 1, 1), 168 LocalDate.of(2022, 1, 1) 169 ) 170 val challengeRequirements = Bundle() 171 challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) 172 173 val result = verifier.verifyAttestation( 174 TYPE_CHALLENGE, challengeRequirements, 175 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() 176 ) 177 assertThat(result).isEqualTo(RESULT_FAILURE) 178 } 179 180 @Test verifyAttestation_returnsFailureTrustedAnchorMismatchnull181 fun verifyAttestation_returnsFailureTrustedAnchorMismatch() { 182 val badTrustAnchorsCerts = TEST_ATTESTATION_CERT_FILENAME.fromPEMFileToCerts() 183 val badTrustAnchors = HashSet<TrustAnchor>() 184 badTrustAnchorsCerts.forEach { 185 badTrustAnchors.add(TrustAnchor(it as X509Certificate, null)) 186 } 187 188 val verifier = AttestationVerificationPeerDeviceVerifier( 189 context, dumpLogger, badTrustAnchors, false, LocalDate.of(2022, 1, 1), 190 LocalDate.of(2022, 1, 1) 191 ) 192 val challengeRequirements = Bundle() 193 challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) 194 195 val result = verifier.verifyAttestation( 196 TYPE_CHALLENGE, challengeRequirements, 197 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() 198 ) 199 assertThat(result).isEqualTo(RESULT_FAILURE) 200 } 201 verifyAttestation_returnsFailureChallengenull202 fun verifyAttestation_returnsFailureChallenge() { 203 val verifier = AttestationVerificationPeerDeviceVerifier( 204 context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 1, 1), 205 LocalDate.of(2022, 1, 1) 206 ) 207 val challengeRequirements = Bundle() 208 challengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray()) 209 210 val result = verifier.verifyAttestation( 211 TYPE_CHALLENGE, challengeRequirements, 212 TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() 213 ) 214 assertThat(result).isEqualTo(RESULT_FAILURE) 215 } 216 Stringnull217 private fun String.fromPEMFileToCerts(): Collection<Certificate> { 218 return certificateFactory.generateCertificates( 219 InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets() 220 .open(this) 221 ) 222 } 223 fromPEMFileToByteArraynull224 private fun String.fromPEMFileToByteArray(): ByteArray { 225 val certs = this.fromPEMFileToCerts() 226 val bos = ByteArrayOutputStream() 227 certs.forEach { 228 bos.write(it.encoded) 229 } 230 return bos.toByteArray() 231 } 232 getDumpnull233 private fun DumpLogger.getDump(): String { 234 val sw = StringWriter() 235 this.dumpTo(IndentingPrintWriter(PrintWriter(sw), " ")) 236 return sw.toString() 237 } 238 239 class TestActivity : Activity() { onCreatenull240 override fun onCreate(savedInstanceState: Bundle?) { 241 super.onCreate(savedInstanceState) 242 } 243 } 244 245 companion object { 246 private const val TAG = "AVFTest" 247 private const val TEST_ROOT_CERT_FILENAME = "test_root_certs.pem" 248 private const val TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME = 249 "test_attestation_with_root_certs.pem" 250 private const val TEST_ATTESTATION_CERT_FILENAME = "test_attestation_wrong_root_certs.pem" 251 private const val TEST_OWNED_BY_SYSTEM_FILENAME = "test_owned_by_system_certs.pem" 252 } 253 } 254