• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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  *      http://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.cts.verifier.security;
18 
19 import com.android.cts.verifier.PassFailButtons;
20 import com.android.cts.verifier.R;
21 
22 import android.app.AlertDialog;
23 import android.app.KeyguardManager;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.os.Bundle;
27 import android.security.keystore.KeyGenParameterSpec;
28 import android.security.keystore.KeyPermanentlyInvalidatedException;
29 import android.security.keystore.KeyProperties;
30 import android.security.keystore.UserNotAuthenticatedException;
31 import android.view.View;
32 import android.view.View.OnClickListener;
33 import android.widget.Button;
34 import android.widget.Toast;
35 
36 import java.io.IOException;
37 import java.security.InvalidAlgorithmParameterException;
38 import java.security.InvalidKeyException;
39 import java.security.KeyStore;
40 import java.security.KeyStoreException;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.NoSuchProviderException;
43 import java.security.UnrecoverableKeyException;
44 import java.security.cert.CertificateException;
45 
46 import javax.crypto.BadPaddingException;
47 import javax.crypto.Cipher;
48 import javax.crypto.IllegalBlockSizeException;
49 import javax.crypto.KeyGenerator;
50 import javax.crypto.NoSuchPaddingException;
51 import javax.crypto.SecretKey;
52 
53 public class LskfBoundKeysTest extends PassFailButtons.Activity {
54 
55     /** Alias for our key in the Android Key Store. */
56     private static final String KEY_NAME = "my_lock_key";
57     private static final byte[] SECRET_BYTE_ARRAY = new byte[] {1, 2, 3, 4, 5, 6};
58     private static final int AUTHENTICATION_DURATION_SECONDS = 5;
59     private static final int CONFIRM_CREDENTIALS_REQUEST_CODE = 1;
60 
61     private KeyguardManager mKeyguardManager;
62 
63     @Override
onCreate(Bundle savedInstanceState)64     protected void onCreate(Bundle savedInstanceState) {
65         super.onCreate(savedInstanceState);
66         setContentView(R.layout.sec_screen_lock_keys_main);
67         setPassFailButtonClickListeners();
68         setInfoResources(R.string.sec_lskf_bound_key_test, R.string.sec_lskf_bound_key_test_info, -1);
69 
70         mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
71 
72         getPassButton().setEnabled(false);
73 
74         Button startTestButton = (Button) findViewById(R.id.sec_start_test_button);
75         startTestButton.setOnClickListener(new OnClickListener() {
76             @Override
77             public void onClick(View v) {
78                 showToast("Test running...");
79                 v.postDelayed(new Runnable() {
80                     @Override
81                     public void run() {
82                         if (tryEncrypt()) {
83                             showToast("Test failed. Key accessible without auth.");
84                         } else {
85                             showAuthenticationScreen();
86                         }
87                     }
88                 },
89                 AUTHENTICATION_DURATION_SECONDS * 1000);
90             }
91 
92         });
93 
94         if (!mKeyguardManager.isKeyguardSecure()) {
95             // Show a message that the user hasn't set up a lock screen.
96             Toast.makeText(this,
97                     "Secure lock screen hasn't set up.\n"
98                             + "Go to 'Settings -> Security -> Screenlock' to set up a lock screen",
99                     Toast.LENGTH_LONG).show();
100             startTestButton.setEnabled(false);
101             return;
102         }
103 
104         createKey();
105     }
106 
107     /**
108      * Creates a symmetric key in the Android Key Store which can only be used after the user has
109      * authenticated with device credentials within the last X seconds.
110      */
createKey()111     private void createKey() {
112         // Generate a key to decrypt payment credentials, tokens, etc.
113         // This will most likely be a registration step for the user when they are setting up your app.
114         try {
115             KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
116             keyStore.load(null);
117             KeyGenerator keyGenerator = KeyGenerator.getInstance(
118                     KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
119 
120             // Set the alias of the entry in Android KeyStore where the key will appear
121             // and the constrains (purposes) in the constructor of the Builder
122             keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME,
123                     KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
124                     .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
125                     .setUserAuthenticationRequired(true)
126                             // Require that the user has unlocked in the last 30 seconds
127                     .setUserAuthenticationValidityDurationSeconds(AUTHENTICATION_DURATION_SECONDS)
128                     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
129                     .build());
130             keyGenerator.generateKey();
131         } catch (NoSuchAlgorithmException | NoSuchProviderException
132                 | InvalidAlgorithmParameterException | KeyStoreException
133                 | CertificateException | IOException e) {
134             throw new RuntimeException("Failed to create a symmetric key", e);
135         }
136     }
137 
138     /**
139      * Tries to encrypt some data with the generated key in {@link #createKey} which is
140      * only works if the user has just authenticated via device credentials.
141      */
tryEncrypt()142     private boolean tryEncrypt() {
143         try {
144             KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
145             keyStore.load(null);
146             SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_NAME, null);
147             Cipher cipher = Cipher.getInstance(
148                     KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/"
149                             + KeyProperties.ENCRYPTION_PADDING_PKCS7);
150 
151             // Try encrypting something, it will only work if the user authenticated within
152             // the last AUTHENTICATION_DURATION_SECONDS seconds.
153             cipher.init(Cipher.ENCRYPT_MODE, secretKey);
154             cipher.doFinal(SECRET_BYTE_ARRAY);
155             return true;
156         } catch (UserNotAuthenticatedException e) {
157             // User is not authenticated, let's authenticate with device credentials.
158             return false;
159         } catch (KeyPermanentlyInvalidatedException e) {
160             // This happens if the lock screen has been disabled or reset after the key was
161             // generated after the key was generated.
162             createKey();
163             Toast.makeText(this, "Set up lockscreen after test ran. Retry the test.\n"
164                             + e.getMessage(),
165                     Toast.LENGTH_LONG).show();
166             return false;
167         } catch (BadPaddingException | IllegalBlockSizeException | KeyStoreException |
168                 CertificateException | UnrecoverableKeyException | IOException
169                 | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
170             throw new RuntimeException(e);
171         }
172     }
173 
showAuthenticationScreen()174     private void showAuthenticationScreen() {
175         // Create the Confirm Credentials screen. You can customize the title and description. Or
176         // we will provide a generic one for you if you leave it null
177         Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null);
178         if (intent != null) {
179             startActivityForResult(intent, CONFIRM_CREDENTIALS_REQUEST_CODE);
180         }
181     }
182 
showToast(String message)183     private void showToast(String message) {
184         Toast.makeText(this, message, Toast.LENGTH_LONG)
185             .show();
186     }
187 
188     @Override
onActivityResult(int requestCode, int resultCode, Intent data)189     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
190         super.onActivityResult(requestCode, resultCode, data);
191         switch (requestCode) {
192             case CONFIRM_CREDENTIALS_REQUEST_CODE:
193                 if (resultCode == RESULT_OK) {
194                     /**
195                      * Regarding to b/258823254, sometimes the test fail because the
196                      * decryption of synthetic password has not been done when
197                      * calling tryEncrypt(). So that add the retry for tryEncrypt()
198                     */
199                     final long deadline = System.currentTimeMillis() + 1000L;
200                     while (System.currentTimeMillis() < deadline) {
201                         if (tryEncrypt()) {
202                             showToast("Test passed.");
203                             getPassButton().setEnabled(true);
204                             return;
205                         }
206                         try {
207                             Thread.sleep(100);
208                         } catch(InterruptedException e) {
209                         }
210                     }
211                     showToast("Test failed. Key not accessible after auth");
212                 }
213         }
214     }
215 }
216 
217