• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.google.attestationexample;
2 
3 import android.os.AsyncTask;
4 import android.security.keystore.KeyGenParameterSpec;
5 import android.security.keystore.KeyProperties;
6 import android.util.Base64;
7 import android.util.Log;
8 
9 import com.google.common.collect.ImmutableSet;
10 
11 import java.io.ByteArrayInputStream;
12 import java.security.GeneralSecurityException;
13 import java.security.InvalidAlgorithmParameterException;
14 import java.security.InvalidKeyException;
15 import java.security.KeyPairGenerator;
16 import java.security.KeyStore;
17 import java.security.NoSuchAlgorithmException;
18 import java.security.NoSuchProviderException;
19 import java.security.PrivateKey;
20 import java.security.PublicKey;
21 import java.security.Signature;
22 import java.security.SignatureException;
23 import java.security.cert.Certificate;
24 import java.security.cert.CertificateException;
25 import java.security.cert.CertificateFactory;
26 import java.security.cert.X509Certificate;
27 import java.security.spec.ECGenParameterSpec;
28 import java.util.Arrays;
29 import java.util.Date;
30 import java.util.HashSet;
31 import java.util.Set;
32 import java.util.regex.Pattern;
33 
34 import static android.security.keystore.KeyProperties.DIGEST_SHA256;
35 import static android.security.keystore.KeyProperties.KEY_ALGORITHM_EC;
36 import static com.google.attestationexample.RootOfTrust.KM_VERIFIED_BOOT_VERIFIED;
37 
38 /**
39  * AttestationTest generates an EC Key pair, with attestation, and displays the result in the
40  * TextView provided to its constructor.
41  */
42 public class AttestationTest extends AsyncTask<Void, String, Void> {
43     private static final int ORIGINATION_TIME_OFFSET = 1000000;
44     private static final int CONSUMPTION_TIME_OFFSET = 2000000;
45 
46     private static final int KEY_USAGE_BITSTRING_LENGTH = 9;
47     private static final int KEY_USAGE_DIGITAL_SIGNATURE_BIT_OFFSET = 0;
48     private static final int KEY_USAGE_KEY_ENCIPHERMENT_BIT_OFFSET = 2;
49     private static final int KEY_USAGE_DATA_ENCIPHERMENT_BIT_OFFSET = 3;
50 
51     private static final int OS_MAJOR_VERSION_MATCH_GROUP_NAME = 1;
52     private static final int OS_MINOR_VERSION_MATCH_GROUP_NAME = 2;
53     private static final int OS_SUBMINOR_VERSION_MATCH_GROUP_NAME = 3;
54     private static final Pattern OS_VERSION_STRING_PATTERN = Pattern
55             .compile("([0-9]{1,2})(?:\\.([0-9]{1,2}))?(?:\\.([0-9]{1,2}))?(?:[^0-9.]+.*)?");
56 
57     private static final int OS_PATCH_LEVEL_YEAR_GROUP_NAME = 1;
58     private static final int OS_PATCH_LEVEL_MONTH_GROUP_NAME = 2;
59     private static final Pattern OS_PATCH_LEVEL_STRING_PATTERN = Pattern
60             .compile("([0-9]{4})-([0-9]{2})-[0-9]{2}");
61 
62     private static final int KM_ERROR_INVALID_INPUT_LENGTH = -21;
63 
64     private static final String FINISHED = "AttestationFinished";
65     private static final String FAIL = "AttestationFail";
66     private static final String INFO = "AttestationInfo";
67     private static final String KEY_DESCRIPTION_OID = "1.3.6.1.4.1.11129.2.1.17";
68     private static final String KEY_USAGE_OID = "2.5.29.15";  // Standard key usage extension.
69 
70     private static final String GOOGLE_ROOT_CERTIFICATE =
71             "-----BEGIN CERTIFICATE-----\n"
72                     + "MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV"
73                     + "BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYy"
74                     + "ODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B"
75                     + "AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS"
76                     + "Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7"
77                     + "tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj"
78                     + "nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq"
79                     + "C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ"
80                     + "oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O"
81                     + "JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg"
82                     + "sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi"
83                     + "igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M"
84                     + "RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E"
85                     + "aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um"
86                     + "AGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYD"
87                     + "VR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAO"
88                     + "BgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lk"
89                     + "Lmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQAD"
90                     + "ggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfB"
91                     + "Pb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00m"
92                     + "qC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rY"
93                     + "DBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPm"
94                     + "QUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4u"
95                     + "JU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyD"
96                     + "CdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79Iy"
97                     + "ZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxD"
98                     + "qwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23Uaic"
99                     + "MDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1"
100                     + "wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk\n"
101                     + "-----END CERTIFICATE-----";
102 
103     @Override
doInBackground(Void... params)104     protected Void doInBackground(Void... params) {
105         try {
106             testEcAttestation();
107         } catch (Exception e) {
108             Log.e(FAIL, "Something is wrong, check detailed logcat logs:\n", e);
109         }
110         return null;
111     }
112 
testEcAttestation()113     private void testEcAttestation() throws Exception {
114         String ecCurve = "secp256r1";
115         int keySize = 256;
116         byte[] challenge = "challenge".getBytes();
117         String keystoreAlias = "test_key";
118 
119         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
120         keyStore.load(null);
121 
122         keyStore.deleteEntry(keystoreAlias);
123 
124         Log.d(INFO,"Generating key pair...");
125         Date startTime = new Date(new Date().getTime() - 1000);
126         Log.d("****", "Start Time is: " + startTime.toString());
127         Date originationEnd = new Date(startTime.getTime() + ORIGINATION_TIME_OFFSET);
128         Date consumptionEnd = new Date(startTime.getTime() + CONSUMPTION_TIME_OFFSET);
129         KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias,
130                 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
131                 .setAlgorithmParameterSpec(new ECGenParameterSpec(ecCurve))
132                 .setDigests(DIGEST_SHA256)
133                 .setAttestationChallenge(challenge);
134 
135         builder.setKeyValidityStart(startTime)
136                 .setKeyValidityForOriginationEnd(originationEnd)
137                 .setKeyValidityForConsumptionEnd(consumptionEnd);
138 
139         generateKeyPair(KEY_ALGORITHM_EC, builder.build());
140         Log.d(INFO, "Key pair generated\n\n");
141 
142         Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
143         Log.d(INFO, "Retrieved certificate chain of length " + certificates.length + "\n");
144         try {
145             verifyCertificateSignatures(certificates);
146         } catch (GeneralSecurityException e) {
147             Log.e(FAIL, "Certificate chain does not verify.", e);
148         }
149 
150         X509Certificate attestationCert = (X509Certificate) certificates[0];
151         X509Certificate secureRoot = (X509Certificate) CertificateFactory
152                 .getInstance("X.509").generateCertificate(
153                         new ByteArrayInputStream(
154                                 GOOGLE_ROOT_CERTIFICATE.getBytes()));
155         X509Certificate rootCert = (X509Certificate) certificates[certificates.length - 1];
156         if (!Arrays.equals(secureRoot.getPublicKey().getEncoded(),
157                 rootCert.getPublicKey().getEncoded())) {
158             Log.e(FAIL, "root certificate public key does not match Google public key");
159         }
160         printKeyUsage(attestationCert);
161 
162         ImmutableSet<String> unexpectedExtensionOids =
163                 (ImmutableSet) retrieveUnexpectedExtensionOids(attestationCert);
164         if (!unexpectedExtensionOids.isEmpty()) {
165             Log.e(FAIL, "attestation certificate contains unexpected OIDs");
166             for (String oid : unexpectedExtensionOids.toArray(new String[unexpectedExtensionOids.size()])) {
167                 Log.e(FAIL, "Unexpected OID: " + oid);
168             }
169         }
170 
171         Attestation attestation = new Attestation(attestationCert);
172         if (!Arrays.equals(attestation.getAttestationChallenge(), challenge)) {
173             Utils.logError("challenge mismatch\nExpected:",
174                     challenge, attestation.getAttestationChallenge());
175         }
176 
177         if (attestation.getAttestationSecurityLevel() != 1) {
178             Utils.logError("Attestation cert reporting non-TEE security level",
179                     1, attestation.getAttestationSecurityLevel());
180         }
181         if (attestation.getKeymasterSecurityLevel() != 1) {
182             Utils.logError("Keymaster reporting non-TEE security level",
183                     1, attestation.getKeymasterSecurityLevel());
184         }
185 
186         checkRootOfTrust(attestation);
187 
188         Signature signer = Signature.getInstance("SHA256WithECDSA");
189         KeyStore keystore = KeyStore.getInstance("AndroidKeyStore");
190         keystore.load(null);
191 
192         PrivateKey key = (PrivateKey) keystore.getKey(keystoreAlias, null);
193         try {
194             signer.initSign(key);
195             signer.update("Hello".getBytes());
196             signer.sign();
197         } catch(Exception e) {
198             Log.e(FAIL,"Failed to sign with generated key", e);
199         }
200         Log.d(FINISHED, "Signing completed");
201     }
202 
generateKeyPair(String algorithm, KeyGenParameterSpec spec)203     private void generateKeyPair(String algorithm, KeyGenParameterSpec spec)
204             throws NoSuchAlgorithmException, NoSuchProviderException,
205             InvalidAlgorithmParameterException {
206         KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm,
207                 "AndroidKeyStore");
208         keyPairGenerator.initialize(spec);
209         keyPairGenerator.generateKeyPair();
210     }
211 
verifyCertificateSignatures(Certificate[] certChain)212     private void verifyCertificateSignatures(Certificate[] certChain)
213             throws GeneralSecurityException {
214 
215         for (Certificate cert : certChain) {
216             final byte[] derCert = cert.getEncoded();
217             final String pemCertPre = Base64.encodeToString(derCert, Base64.NO_WRAP);
218             Log.e("****", pemCertPre);
219         }
220 
221         for (int i = 1; i < certChain.length; ++i) {
222             PublicKey pubKey = certChain[i].getPublicKey();
223             try {
224                 certChain[i - 1].verify(pubKey);
225             } catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException
226                     | NoSuchProviderException | SignatureException e) {
227                 throw new GeneralSecurityException("Failed to verify certificate "
228                         + certChain[i - 1] + " with public key " + certChain[i].getPublicKey(), e);
229             }
230             if (i == certChain.length - 1) {
231                 // Last cert is self-signed.
232                 try {
233                     certChain[i].verify(pubKey);
234                 } catch (CertificateException e) {
235                     throw new GeneralSecurityException(
236                             "Root cert " + certChain[i] + " is not correctly self-signed", e);
237                 }
238             }
239         }
240     }
241 
printKeyUsage(X509Certificate attestationCert)242     private void printKeyUsage(X509Certificate attestationCert) {
243         Log.d(INFO, "Key usage:");
244         if (attestationCert.getKeyUsage() == null) {
245             Log.d(INFO, " NONE\n");
246             return;
247         }
248         if (attestationCert.getKeyUsage()[KEY_USAGE_DIGITAL_SIGNATURE_BIT_OFFSET]) {
249             Log.d(INFO, " sign");
250         }
251         if (attestationCert.getKeyUsage()[KEY_USAGE_DATA_ENCIPHERMENT_BIT_OFFSET]) {
252             Log.d(INFO, " encrypt_data");
253         }
254         if (attestationCert.getKeyUsage()[KEY_USAGE_KEY_ENCIPHERMENT_BIT_OFFSET]) {
255             Log.d(INFO, " encrypt_keys");
256         }
257         Log.d(INFO, "\n");
258     }
259 
retrieveUnexpectedExtensionOids(X509Certificate x509Cert)260     private Set<String> retrieveUnexpectedExtensionOids(X509Certificate x509Cert) {
261         return new ImmutableSet.Builder<String>()
262                 .addAll(x509Cert.getCriticalExtensionOIDs()
263                         .stream()
264                         .filter(s -> !KEY_USAGE_OID.equals(s))
265                         .iterator())
266                 .addAll(x509Cert.getNonCriticalExtensionOIDs()
267                         .stream()
268                         .filter(s -> !KEY_DESCRIPTION_OID.equals(s))
269                         .iterator())
270                 .build();
271     }
272 
273 
checkRootOfTrust(Attestation attestation)274     private void checkRootOfTrust(Attestation attestation) {
275         RootOfTrust rootOfTrust = attestation.getTeeEnforced().getRootOfTrust();
276         if (rootOfTrust == null) {
277             Log.e(FAIL, "Root of trust is null");
278             return;
279         }
280         if (rootOfTrust.getVerifiedBootKey() == null) {
281             Log.e(FAIL, "Verified boot key is null");
282             return;
283         }
284         if (rootOfTrust.getVerifiedBootKey().length < 32) {
285             Log.e(FAIL, "Verified boot key is less than 32 bytes");
286         }
287         if (isAllZeroes(rootOfTrust.getVerifiedBootKey())) {
288             Log.e(FAIL, "Verified boot key is all zeros.");
289         }
290         if (!rootOfTrust.isDeviceLocked()) {
291             Log.e(FAIL, "Device isn't locked");
292         }
293         if (KM_VERIFIED_BOOT_VERIFIED != rootOfTrust.getVerifiedBootState()) {
294             Utils.logError("Root of trust isn't reporting boot verified.",
295                     KM_VERIFIED_BOOT_VERIFIED, rootOfTrust.getVerifiedBootState());
296         }
297         if (rootOfTrust.getVerifiedBootHash() == null) {
298             Log.e(FAIL, "Verified boot hash is null");
299             return;
300         }
301         if (rootOfTrust.getVerifiedBootHash().length != 32) {
302             Log.e(FAIL, "Verified boot hash isn't 32 bytes");
303         }
304         if (isAllZeroes(rootOfTrust.getVerifiedBootHash())) {
305             Log.e(FAIL, "Verified boot hash is all zeros.");
306         }
307     }
308 
isAllZeroes(byte[] checkArray)309     private boolean isAllZeroes(byte[] checkArray) {
310         for (byte b : checkArray) {
311             if (b != 0) {
312                 return false;
313             }
314         }
315         return true;
316     }
317 }
318