1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.devicediagnostics.evaluated
18 
19 import android.security.keystore.KeyGenParameterSpec
20 import android.security.keystore.KeyProperties
21 import android.util.Log
22 import java.nio.ByteBuffer
23 import java.security.KeyPairGenerator
24 import java.security.KeyStore
25 import java.security.spec.ECGenParameterSpec
26 
27 private const val TAG = "GetAttestation"
28 private const val EC_CURVE = "secp256r1"
29 private const val KEYSTORE_ALIAS = "attestation_collector_key"
30 
getAttestationnull31 private fun getAttestation(challenge: ByteArray, withImei: Boolean): ByteArray {
32     val keyStore = KeyStore.getInstance("AndroidKeyStore")
33     keyStore.load(null)
34     keyStore.deleteEntry(KEYSTORE_ALIAS)
35     val keyPurpose = KeyProperties::PURPOSE_SIGN.get() or KeyProperties::PURPOSE_VERIFY.get()
36     var builder =
37         KeyGenParameterSpec.Builder(KEYSTORE_ALIAS, keyPurpose)
38             .setAlgorithmParameterSpec(ECGenParameterSpec(EC_CURVE))
39             .setDigests(KeyProperties.DIGEST_SHA256)
40             .setAttestationChallenge(challenge)
41 
42     // Use reflection to call this system API so we can still build in Android Studio
43     if (withImei)
44         builder =
45             builder::class
46                 .members
47                 .firstOrNull { it.name == "setAttestationIds" }
48                 ?.call(
49                     builder,
50                     intArrayOf(
51                         1,
52                         2,
53                     ), // AttestationUtils.ID_TYPE_SERIAL, AttestationUtils.ID_TYPE_IMEI
54                 ) as KeyGenParameterSpec.Builder
55 
56     val spec = builder.build()
57 
58     val keyPairGenerator =
59         KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore")
60     keyPairGenerator.initialize(spec)
61     keyPairGenerator.generateKeyPair()
62 
63     var report = ByteArray(0)
64     for (cert in keyStore.getCertificateChain(KEYSTORE_ALIAS)) {
65         report +=
66             ByteBuffer.allocate(Int.SIZE_BYTES).putInt(cert.encoded.size).array() + cert.encoded
67     }
68 
69     return report
70 }
71 
createAttestationRecordnull72 fun createAttestationRecord(challenge: ByteArray): ByteArray {
73     // On devices without device IDs provisioned, the above code will throw ProviderException
74     // Continue without this data to support testing other functionality
75     try {
76         return getAttestation(challenge, true)
77     } catch (e: java.security.ProviderException) {
78         Log.e(TAG, "Could not get attestation with IMEI, retrying without: $e")
79     } catch (e: SecurityException) {
80         Log.e(TAG, "Could not get attestation with IMEI, retrying without: $e")
81     } catch (e: Exception) {
82         Log.e(TAG, "Could not create attestation record: $e")
83         return ByteArray(0)
84     }
85 
86     try {
87         return getAttestation(challenge, false)
88     } catch (e: Exception) {
89         Log.e(TAG, "Could not create attestation record: $e")
90         return ByteArray(0)
91     }
92 }
93