• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.FLAG_FAILURE_CERTS
7 import android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS
8 import android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_PATCH_LEVEL_DIFF
9 import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
10 import android.security.attestationverification.AttestationVerificationManager.PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS
11 import android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY
12 import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
13 import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
14 import android.util.IndentingPrintWriter
15 import android.util.Log
16 import androidx.test.ext.junit.runners.AndroidJUnit4
17 import androidx.test.filters.SmallTest
18 import androidx.test.platform.app.InstrumentationRegistry
19 import com.android.server.security.AttestationVerificationManagerService.DumpLogger
20 import com.google.common.truth.Truth.assertThat
21 import java.io.ByteArrayOutputStream
22 import java.io.PrintWriter
23 import java.io.StringWriter
24 import java.security.cert.Certificate
25 import java.security.cert.CertificateFactory
26 import java.security.cert.TrustAnchor
27 import java.security.cert.X509Certificate
28 import java.time.LocalDate
29 import org.junit.After
30 import org.junit.Before
31 import org.junit.Test
32 import org.junit.runner.RunWith
33 import org.mockito.Mock
34 import org.mockito.MockitoAnnotations
35 
36 
37 /** Test for Peer Device attestation verifier. */
38 @SmallTest
39 @RunWith(AndroidJUnit4::class)
40 class AttestationVerificationPeerDeviceVerifierTest {
41     private val certificateFactory = CertificateFactory.getInstance("X.509")
42     @Mock private lateinit var context: Context
43     private val dumpLogger = DumpLogger()
44     private lateinit var trustAnchors: HashSet<TrustAnchor>
45 
46     @Before
setupnull47     fun setup() {
48         MockitoAnnotations.initMocks(this)
49 
50         val rootCerts = TEST_ROOT_CERT_FILENAME.fromPEMFileToCerts()
51         trustAnchors = HashSet<TrustAnchor>()
52         rootCerts.forEach {
53             trustAnchors.add(TrustAnchor(it as X509Certificate, null))
54         }
55     }
56 
57     @After
dumpAndLognull58     fun dumpAndLog() {
59         val dump = dumpLogger.getDump()
60         Log.d(TAG, "$dump")
61     }
62 
63     @Test
verifyAttestation_returnsSuccessTypeChallengenull64     fun verifyAttestation_returnsSuccessTypeChallenge() {
65         val verifier = AttestationVerificationPeerDeviceVerifier(
66             context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1),
67             LocalDate.of(2021, 8, 1)
68         )
69         val challengeRequirements = Bundle()
70         challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
71 
72         val result = verifier.verifyAttestation(
73             TYPE_CHALLENGE, challengeRequirements,
74             TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
75         )
76         assertThat(result).isEqualTo(0)
77     }
78 
79     @Test
verifyAttestation_returnsSuccessLocalPatchOlderThanOneYearnull80     fun verifyAttestation_returnsSuccessLocalPatchOlderThanOneYear() {
81         val verifier = AttestationVerificationPeerDeviceVerifier(
82             context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1),
83             LocalDate.of(2021, 1, 1)
84         )
85         val challengeRequirements = Bundle()
86         challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
87 
88         val result = verifier.verifyAttestation(
89             TYPE_CHALLENGE, challengeRequirements,
90             TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
91         )
92         assertThat(result).isEqualTo(0)
93     }
94 
95     @Test
verifyAttestation_returnsSuccessTypePublicKeynull96     fun verifyAttestation_returnsSuccessTypePublicKey() {
97         val verifier = AttestationVerificationPeerDeviceVerifier(
98             context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1),
99             LocalDate.of(2021, 8, 1)
100         )
101 
102         val leafCert =
103             (TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToCerts() as List)[0]
104                     as X509Certificate
105         val pkRequirements = Bundle()
106         pkRequirements.putByteArray(PARAM_PUBLIC_KEY, leafCert.publicKey.encoded)
107 
108         val result = verifier.verifyAttestation(
109             TYPE_PUBLIC_KEY, pkRequirements,
110             TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
111         )
112         assertThat(result).isEqualTo(0)
113     }
114 
115     @Test
verifyAttestation_returnsSuccessOwnedBySystemnull116     fun verifyAttestation_returnsSuccessOwnedBySystem() {
117         val verifier = AttestationVerificationPeerDeviceVerifier(
118             context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1),
119             LocalDate.of(2021, 1, 1)
120         )
121         val challengeRequirements = Bundle()
122         challengeRequirements.putByteArray(PARAM_CHALLENGE, "activeUnlockValid".encodeToByteArray())
123         challengeRequirements.putBoolean("android.key_owned_by_system", true)
124 
125         val result = verifier.verifyAttestation(
126             TYPE_CHALLENGE, challengeRequirements,
127             TEST_OWNED_BY_SYSTEM_FILENAME.fromPEMFileToByteArray()
128         )
129 
130         assertThat(result).isEqualTo(0)
131     }
132 
133     @Test
verifyAttestation_returnsFailureOwnedBySystemnull134     fun verifyAttestation_returnsFailureOwnedBySystem() {
135         val verifier = AttestationVerificationPeerDeviceVerifier(
136             context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1),
137             LocalDate.of(2021, 1, 1)
138         )
139         val challengeRequirements = Bundle()
140         challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
141         challengeRequirements.putBoolean("android.key_owned_by_system", true)
142 
143         val result = verifier.verifyAttestation(
144             TYPE_CHALLENGE, challengeRequirements,
145             TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
146         )
147         assertThat(result).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
148     }
149 
150     @Test
verifyAttestation_returnsFailurePatchDateNotWithinOneYearLocalPatchnull151     fun verifyAttestation_returnsFailurePatchDateNotWithinOneYearLocalPatch() {
152         val verifier = AttestationVerificationPeerDeviceVerifier(
153             context, dumpLogger, trustAnchors, false, LocalDate.of(2023, 3, 1),
154             LocalDate.of(2023, 2, 1)
155         )
156         val challengeRequirements = Bundle()
157         challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
158 
159         val result = verifier.verifyAttestation(
160             TYPE_CHALLENGE, challengeRequirements,
161             TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
162         )
163         assertThat(result).isEqualTo(FLAG_FAILURE_PATCH_LEVEL_DIFF)
164     }
165 
166     @Test
verifyAttestation_returnsSuccessPatchDataWithinMaxPatchDiffnull167     fun verifyAttestation_returnsSuccessPatchDataWithinMaxPatchDiff() {
168         val verifier = AttestationVerificationPeerDeviceVerifier(
169             context, dumpLogger, trustAnchors, false, LocalDate.of(2023, 3, 1),
170             LocalDate.of(2023, 2, 1)
171         )
172         val challengeRequirements = Bundle()
173         challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
174         challengeRequirements.putInt(PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS, 24)
175 
176         val result = verifier.verifyAttestation(
177             TYPE_CHALLENGE, challengeRequirements,
178             TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
179         )
180         assertThat(result).isEqualTo(0)
181     }
182 
183     @Test
verifyAttestation_returnsFailurePatchDataNotWithinMaxPatchDiffnull184     fun verifyAttestation_returnsFailurePatchDataNotWithinMaxPatchDiff() {
185         val verifier = AttestationVerificationPeerDeviceVerifier(
186             context, dumpLogger, trustAnchors, false, LocalDate.of(2024, 10, 1),
187             LocalDate.of(2024, 9, 1)
188         )
189         val challengeRequirements = Bundle()
190         challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
191         challengeRequirements.putInt(PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS, 24)
192 
193         val result = verifier.verifyAttestation(
194             TYPE_CHALLENGE, challengeRequirements,
195             TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
196         )
197         assertThat(result).isEqualTo(FLAG_FAILURE_PATCH_LEVEL_DIFF)
198     }
199 
200     @Test
verifyAttestation_returnsFailureOwnedBySystemAndPatchDataNotWithinMaxPatchDiffnull201     fun verifyAttestation_returnsFailureOwnedBySystemAndPatchDataNotWithinMaxPatchDiff() {
202         val verifier = AttestationVerificationPeerDeviceVerifier(
203             context, dumpLogger, trustAnchors, false, LocalDate.of(2024, 10, 1),
204             LocalDate.of(2024, 9, 1)
205         )
206         val challengeRequirements = Bundle()
207         challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
208         challengeRequirements.putBoolean("android.key_owned_by_system", true)
209         challengeRequirements.putInt(PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS, 24)
210 
211         val result = verifier.verifyAttestation(
212             TYPE_CHALLENGE, challengeRequirements,
213             TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
214         )
215         // Both "owned by system" and "patch level diff" checks should fail.
216         assertThat(result).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS or FLAG_FAILURE_PATCH_LEVEL_DIFF)
217     }
218 
219     @Test
verifyAttestation_returnsFailureTrustedAnchorEmptynull220     fun verifyAttestation_returnsFailureTrustedAnchorEmpty() {
221         val verifier = AttestationVerificationPeerDeviceVerifier(
222             context, dumpLogger, HashSet(), false, LocalDate.of(2022, 1, 1),
223             LocalDate.of(2022, 1, 1)
224         )
225         val challengeRequirements = Bundle()
226         challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
227 
228         val result = verifier.verifyAttestation(
229             TYPE_CHALLENGE, challengeRequirements,
230             TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
231         )
232         assertThat(result).isEqualTo(FLAG_FAILURE_CERTS)
233     }
234 
235     @Test
verifyAttestation_returnsFailureTrustedAnchorMismatchnull236     fun verifyAttestation_returnsFailureTrustedAnchorMismatch() {
237         val badTrustAnchorsCerts = TEST_ATTESTATION_CERT_FILENAME.fromPEMFileToCerts()
238         val badTrustAnchors = HashSet<TrustAnchor>()
239         badTrustAnchorsCerts.forEach {
240             badTrustAnchors.add(TrustAnchor(it as X509Certificate, null))
241         }
242 
243         val verifier = AttestationVerificationPeerDeviceVerifier(
244             context, dumpLogger, badTrustAnchors, false, LocalDate.of(2022, 1, 1),
245             LocalDate.of(2022, 1, 1)
246         )
247         val challengeRequirements = Bundle()
248         challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
249 
250         val result = verifier.verifyAttestation(
251             TYPE_CHALLENGE, challengeRequirements,
252             TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
253         )
254         assertThat(result).isEqualTo(FLAG_FAILURE_CERTS)
255     }
256 
verifyAttestation_returnsFailureChallengenull257     fun verifyAttestation_returnsFailureChallenge() {
258         val verifier = AttestationVerificationPeerDeviceVerifier(
259             context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 1, 1),
260             LocalDate.of(2022, 1, 1)
261         )
262         val challengeRequirements = Bundle()
263         challengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray())
264 
265         val result = verifier.verifyAttestation(
266             TYPE_CHALLENGE, challengeRequirements,
267             TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
268         )
269         assertThat(result).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
270     }
271 
Stringnull272     private fun String.fromPEMFileToCerts(): Collection<Certificate> {
273         return certificateFactory.generateCertificates(
274             InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
275                 .open(this)
276         )
277     }
278 
fromPEMFileToByteArraynull279     private fun String.fromPEMFileToByteArray(): ByteArray {
280         val certs = this.fromPEMFileToCerts()
281         val bos = ByteArrayOutputStream()
282         certs.forEach {
283             bos.write(it.encoded)
284         }
285         return bos.toByteArray()
286     }
287 
getDumpnull288     private fun DumpLogger.getDump(): String {
289         val sw = StringWriter()
290         this.dumpTo(IndentingPrintWriter(PrintWriter(sw), " "))
291         return sw.toString()
292     }
293 
294     class TestActivity : Activity() {
onCreatenull295         override fun onCreate(savedInstanceState: Bundle?) {
296             super.onCreate(savedInstanceState)
297         }
298     }
299 
300     companion object {
301         private const val TAG = "AVFTest"
302         private const val TEST_ROOT_CERT_FILENAME = "test_root_certs.pem"
303         // Local patch date is 20220105
304         private const val TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME =
305             "test_attestation_with_root_certs.pem"
306         private const val TEST_ATTESTATION_CERT_FILENAME = "test_attestation_wrong_root_certs.pem"
307         private const val TEST_OWNED_BY_SYSTEM_FILENAME = "test_owned_by_system_certs.pem"
308     }
309 }
310