• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<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