<lambda>null1 package android.security.attestationverification
2
3 import android.os.Bundle
4 import android.app.Activity
5 import androidx.test.ext.junit.rules.ActivityScenarioRule
6 import androidx.test.ext.junit.runners.AndroidJUnit4
7 import androidx.test.filters.SmallTest
8 import org.junit.Assert.assertThrows
9 import org.junit.Before
10 import org.junit.Rule
11 import org.junit.Test
12 import org.junit.runner.RunWith
13 import com.google.common.truth.Truth.assertThat
14 import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
15 import android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED
16 import android.security.attestationverification.AttestationVerificationManager.PROFILE_UNKNOWN
17 import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
18 import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
19 import android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN
20 import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
21 import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
22 import android.security.keystore.KeyGenParameterSpec
23 import android.security.keystore.KeyProperties
24 import java.lang.IllegalArgumentException
25 import java.io.ByteArrayOutputStream
26 import java.security.KeyPairGenerator
27 import java.security.KeyStore
28 import java.time.Duration
29 import java.util.concurrent.CompletableFuture
30 import java.util.concurrent.TimeUnit
31
32 /** Test for system-defined attestation verifiers. */
33 @SmallTest
34 @RunWith(AndroidJUnit4::class)
35 class SystemAttestationVerificationTest {
36 @get:Rule
37 val rule = ActivityScenarioRule(TestActivity::class.java)
38
39 private lateinit var activity: Activity
40 private lateinit var avm: AttestationVerificationManager
41 private lateinit var androidKeystore: KeyStore
42
43 @Before
44 fun setup() {
45 rule.getScenario().onActivity {
46 avm = it.getSystemService(AttestationVerificationManager::class.java)!!
47 activity = it
48 androidKeystore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) }
49 }
50 }
51
52 @Test
53 fun verifyAttestation_returnsUnknown() {
54 val future = CompletableFuture<Int>()
55 val profile = AttestationProfile(PROFILE_UNKNOWN)
56 avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
57 activity.mainExecutor) { result, _ ->
58 future.complete(result)
59 }
60
61 assertThat(future.getSoon()).isEqualTo(RESULT_UNKNOWN)
62 }
63
64 @Test
65 fun verifyAttestation_returnsFailureWithEmptyAttestation() {
66 val future = CompletableFuture<Int>()
67 val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
68 avm.verifyAttestation(profile, TYPE_CHALLENGE, Bundle(), ByteArray(0),
69 activity.mainExecutor) { result, _ ->
70 future.complete(result)
71 }
72
73 assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
74 }
75
76 @Test
77 fun verifyAttestation_returnsFailureWithEmptyRequirements() {
78 val future = CompletableFuture<Int>()
79 val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
80 avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
81 Bundle(), selfTrusted.attestation, activity.mainExecutor) { result, _ ->
82 future.complete(result)
83 }
84 assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
85 }
86
87 @Test
88 fun verifyAttestation_returnsFailureWithWrongBindingType() {
89 val future = CompletableFuture<Int>()
90 val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
91 avm.verifyAttestation(selfTrusted.profile, TYPE_PUBLIC_KEY,
92 selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
93 future.complete(result)
94 }
95 assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
96 }
97
98 @Test
99 fun verifyAttestation_returnsFailureWithWrongRequirements() {
100 val future = CompletableFuture<Int>()
101 val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
102 val wrongKeyRequirements = Bundle()
103 wrongKeyRequirements.putByteArray(
104 "wrongBindingKey", "challengeStr".encodeToByteArray())
105 avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
106 wrongKeyRequirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
107 future.complete(result)
108 }
109 assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
110 }
111
112 @Test
113 fun verifyAttestation_returnsFailureWithWrongChallenge() {
114 val future = CompletableFuture<Int>()
115 val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
116 val wrongChallengeRequirements = Bundle()
117 wrongChallengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray())
118 avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
119 wrongChallengeRequirements, selfTrusted.attestation, activity.mainExecutor) {
120 result, _ -> future.complete(result)
121 }
122 assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
123 }
124
125 // TODO(b/216144791): Add more failure tests for PROFILE_SELF_TRUSTED.
126 @Test
127 fun verifyAttestation_returnsSuccess() {
128 val future = CompletableFuture<Int>()
129 val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
130 avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
131 selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
132 future.complete(result)
133 }
134 assertThat(future.getSoon()).isEqualTo(RESULT_SUCCESS)
135 }
136
137 @Test
138 fun verifyToken_returnsUnknown() {
139 val future = CompletableFuture<Int>()
140 val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
141 avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
142 activity.mainExecutor) { _, token ->
143 val result = avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), token, null)
144 future.complete(result)
145 }
146
147 assertThat(future.getSoon()).isEqualTo(RESULT_UNKNOWN)
148 }
149
150 @Test
151 fun verifyToken_tooBigMaxAgeThrows() {
152 val future = CompletableFuture<VerificationToken>()
153 val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
154 avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
155 activity.mainExecutor) { _, token ->
156 future.complete(token)
157 }
158
159 assertThrows(IllegalArgumentException::class.java) {
160 avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), future.getSoon(),
161 Duration.ofSeconds(3601))
162 }
163 }
164
165 private fun <T> CompletableFuture<T>.getSoon(): T {
166 return this.get(1, TimeUnit.SECONDS)
167 }
168
169 class TestActivity : Activity() {
170 override fun onCreate(savedInstanceState: Bundle?) {
171 super.onCreate(savedInstanceState)
172 }
173 }
174
175 inner class TestSelfTrustedAttestation(val alias: String, val challenge: String) {
176 val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
177 val localBindingType = TYPE_CHALLENGE
178 val requirements: Bundle
179 val attestation: ByteArray
180
181 init {
182 val challengeByteArray = challenge.encodeToByteArray()
183 generateAndStoreKey(alias, challengeByteArray)
184 attestation = generateCertificatesByteArray(alias)
185 requirements = Bundle()
186 requirements.putByteArray(PARAM_CHALLENGE, challengeByteArray)
187 }
188
189 private fun generateAndStoreKey(alias: String, challenge: ByteArray) {
190 val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
191 KeyProperties.KEY_ALGORITHM_EC,
192 ANDROID_KEYSTORE
193 )
194 val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
195 alias,
196 KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
197 ).run {
198 // a challenge results in a generated attestation
199 setAttestationChallenge(challenge)
200 setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
201 build()
202 }
203 kpg.initialize(parameterSpec)
204 kpg.generateKeyPair()
205 }
206
207 private fun generateCertificatesByteArray(alias: String): ByteArray {
208 val pkEntry = androidKeystore.getEntry(alias, null) as KeyStore.PrivateKeyEntry
209 val certs = pkEntry.certificateChain
210 val bos = ByteArrayOutputStream()
211 certs.forEach {
212 bos.write(it.encoded)
213 }
214 return bos.toByteArray()
215 }
216 }
217
218 companion object {
219 private const val TAG = "AVFTEST"
220 private const val ANDROID_KEYSTORE = "AndroidKeyStore"
221 }
222 }
223