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