• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 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 android.keystore.cts;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertArrayEquals;
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertSame;
26 import static org.junit.Assert.assertTrue;
27 import static org.junit.Assert.fail;
28 
29 import android.app.KeyguardManager;
30 import android.content.Context;
31 import android.content.pm.PackageManager;
32 import android.keystore.cts.util.EmptyArray;
33 import android.keystore.cts.util.ImportedKey;
34 import android.keystore.cts.util.TestUtils;
35 import android.os.SystemClock;
36 import android.platform.test.annotations.Presubmit;
37 import android.security.keystore.KeyProperties;
38 import android.security.keystore.KeyProtection;
39 import android.server.wm.ActivityManagerTestBase;
40 import android.server.wm.UiDeviceUtils;
41 
42 import androidx.test.InstrumentationRegistry;
43 import androidx.test.runner.AndroidJUnit4;
44 
45 import com.google.common.collect.ObjectArrays;
46 
47 import org.junit.Test;
48 import org.junit.runner.RunWith;
49 
50 import java.io.ByteArrayInputStream;
51 import java.io.ByteArrayOutputStream;
52 import java.security.AlgorithmParameters;
53 import java.security.InvalidKeyException;
54 import java.security.Key;
55 import java.security.KeyStoreException;
56 import java.security.Provider;
57 import java.security.Provider.Service;
58 import java.security.Security;
59 import java.security.spec.AlgorithmParameterSpec;
60 import java.security.spec.MGF1ParameterSpec;
61 import java.util.Arrays;
62 import java.util.Collection;
63 import java.util.Date;
64 import java.util.HashSet;
65 import java.util.Locale;
66 import java.util.Map;
67 import java.util.Random;
68 import java.util.Set;
69 import java.util.TreeMap;
70 
71 import javax.crypto.BadPaddingException;
72 import javax.crypto.Cipher;
73 import javax.crypto.CipherInputStream;
74 import javax.crypto.CipherOutputStream;
75 import javax.crypto.IllegalBlockSizeException;
76 import javax.crypto.spec.GCMParameterSpec;
77 import javax.crypto.spec.IvParameterSpec;
78 import javax.crypto.spec.OAEPParameterSpec;
79 import javax.crypto.spec.PSource;
80 import javax.crypto.spec.SecretKeySpec;
81 
82 /**
83  * Tests for algorithm-agnostic functionality of {@code Cipher} implementations backed by Android
84  * Keystore.
85  */
86 @RunWith(AndroidJUnit4.class)
87 public class CipherTest {
88 
89     private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_CRYPTO_OP_PROVIDER_NAME;
90 
91     private static final String[] BASE_EXPECTED_ALGORITHMS = {
92         "AES/ECB/NoPadding",
93         "AES/ECB/PKCS7Padding",
94         "AES/CBC/NoPadding",
95         "AES/CBC/PKCS7Padding",
96         "AES/CTR/NoPadding",
97         "AES/GCM/NoPadding",
98         "RSA/ECB/NoPadding",
99         "RSA/ECB/PKCS1Padding",
100         "RSA/ECB/OAEPPadding",
101         "RSA/ECB/OAEPWithSHA-1AndMGF1Padding",
102         "RSA/ECB/OAEPWithSHA-224AndMGF1Padding",
103         "RSA/ECB/OAEPWithSHA-256AndMGF1Padding",
104         "RSA/ECB/OAEPWithSHA-384AndMGF1Padding",
105         "RSA/ECB/OAEPWithSHA-512AndMGF1Padding"
106     };
107 
108     private static final String[] DESEDE_ALGORITHMS = {
109         "DESede/CBC/NoPadding",
110         "DESede/CBC/PKCS7Padding",
111         "DESede/ECB/NoPadding",
112         "DESede/ECB/PKCS7Padding",
113     };
114 
115     private static String[] EXPECTED_ALGORITHMS = BASE_EXPECTED_ALGORITHMS;
116 
117     // For tests of behavior largely unrelated to the selected algorithm, such as
118     // unlockedDeviceRequired
119     private static final String[] BASIC_ALGORITHMS = {
120             "AES/GCM/NoPadding",
121             "RSA/ECB/OAEPWithSHA-256AndMGF1Padding",
122     };
123 
124     static {
125       if (TestUtils.supports3DES()) {
126         EXPECTED_ALGORITHMS = ObjectArrays
127             .concat(BASE_EXPECTED_ALGORITHMS, DESEDE_ALGORITHMS, String.class);
128       }
129     }
130 
131     private static class KatVector {
132         private final byte[] plaintext;
133         private final byte[] ciphertext;
134         private final AlgorithmParameterSpec params;
135 
KatVector(String plaintextHex, String ciphertextHex)136         private KatVector(String plaintextHex, String ciphertextHex) {
137             this(plaintextHex, null, ciphertextHex);
138         }
139 
KatVector(String plaintextHex, AlgorithmParameterSpec params, String ciphertextHex)140         private KatVector(String plaintextHex, AlgorithmParameterSpec params,
141                 String ciphertextHex) {
142             this(HexEncoding.decode(plaintextHex), params, HexEncoding.decode(ciphertextHex));
143         }
144 
KatVector(byte[] plaintext, byte[] ciphertext)145         private KatVector(byte[] plaintext, byte[] ciphertext) {
146             this(plaintext, null, ciphertext);
147         }
148 
KatVector(byte[] plaintext, AlgorithmParameterSpec params, byte[] ciphertext)149         private KatVector(byte[] plaintext, AlgorithmParameterSpec params, byte[] ciphertext) {
150             this.plaintext = plaintext;
151             this.ciphertext = ciphertext;
152             this.params = params;
153         }
154     }
155     private static final Map<String, KatVector> KAT_VECTORS =
156             new TreeMap<String, KatVector>(String.CASE_INSENSITIVE_ORDER);
157     static {
158         // From RI
159         KAT_VECTORS.put("AES/ECB/NoPadding", new KatVector(
160                 "0383911bb1519d58e6656f3fd35639c502dbeb2196cea937fca272666cb4a80b",
161                 "6574c5065283b89e0c930019e4655d8516b98170db6516cd83e589bd9c5e5adc"));
162         KAT_VECTORS.put("AES/ECB/PKCS7Padding", new KatVector(
163                 "1ad3d73a3cfa66dac78a51a95c2cb2125ea701e6e9ecbca2415b436f0258e2ba7439b67545",
164                 "920f873f2f9e91bac4c9c948d66496a21b8b2606850490dac7abecae83317488ee550b9973ac5cd142"
165                 + "f387d7d2a12752"));
166         KAT_VECTORS.put("AES/CBC/NoPadding", new KatVector(
167                 "1dffe21c8f18276c3a39ed0c53ab257b84efcedab60095c4cadd131143058cf7",
168                 new IvParameterSpec(HexEncoding.decode("10b3eea6cc8a7d6f48337e9b6987d28c")),
169                 "47ab115bfadca91eaebec73ab942a06f3121fdd5aa55d223bd2cbcc3855e1ef8"));
170         KAT_VECTORS.put("AES/CBC/PKCS7Padding", new KatVector(
171                 "9d49fb970b23bfe742ae7c45a773ada9faad84708c8858a06e4a192e0a90e2f6083548e0bf3f67",
172                 new IvParameterSpec(HexEncoding.decode("ecd87bf9c49f37dcd2294e309192289a")),
173                 "aeb64f48ec18a086eda7ee080948651a50b6f582ab54aac5454c9ab0a4de5b4a4abac526a4307011d1"
174                 + "2881f1849c32ae"));
175         KAT_VECTORS.put("AES/CTR/NoPadding", new KatVector(
176                 "b4e786cab9df48d2fce0c7872651314db1318d1f31a1b10a2c334d2555b4117668",
177                 new IvParameterSpec(HexEncoding.decode("94d9f7a6d16f58018819b668020b68cc")),
178                 "022e74572a70be57a0b65b2fb5bc9b803ce48973b6163f528bbe1fd001e29d330a"));
179         KAT_VECTORS.put("AES/GCM/NoPadding", new KatVector(
180                 "03889a6ca811e3fd7e78467e3dae587d2110e80e98edbc9dfe17afba238c4c493186",
181                 new GCMParameterSpec(128, HexEncoding.decode("f67aaf97cdec65b12188315e")),
182                 "159eb1ffc86589b38f18097c32db646c7de3525b603876c3ae671bc2ca52a5395a374b377a915c9ed1"
183                 + "a349abf9fc54c9ca81"));
184         KAT_VECTORS.put("RSA/ECB/NoPadding", new KatVector(
185                 "50c499d558c38fd48ea76832887db2abc76e4e153a98fd4323ccb8006d34f11724a5692fb101b0eb96"
186                 + "060eb9d15222",
187                 "349b1d5061e98d0ab3f2327680bbc0cbb1b8ef8ee26148d7c67cf535223e3f78d822d369592ede29b1"
188                 + "654aab25e6ae5e098318e55c13dc405f5ba27e5cc69ced32778592a51e6293a03f95e14ed17099fb"
189                 + "0ac585e41297b87c3432953df0d98be7e505dc7de7bfe9d9ec750f475afeba4cc2dd78838c0d4399"
190                 + "d8de02b07f00b292dc3d32d2a2f98ea5a5dac1a0fec4d01e5c3aea8c56eeff264896fb6cf2144401"
191                 + "278c6663417bc00aafbb9eb97c056573cdec88d6ac6fd6c333d131337b16031da229029e3b6fe6f8"
192                 + "ee427f2e90041e9636d67cddac75845914ce4be56092eed7188fe7e2bb33769efdeed86a7acbe15d"
193                 + "debf92d9fbaaddede206acfa650697"));
194         KAT_VECTORS.put("RSA/ECB/PKCS1Padding", new KatVector(
195                 "aed8cd94f35b2a54cdd3ed771482bd87e256b995408558fb82e5d475d1ee54711472f899ad6cbb6847"
196                 + "99e52ff1d57cbc39f4",
197                 "64148dee294dd3ea31d2b595ea661318cf90c89f71393cf6559087d6e8993e73eb1e6b5f4d3cfde3cb"
198                 + "267938c5eca522b95a2df02df9c703dbe3103c157af0d2ed5b70da51cb4caa49061319420d0ea433"
199                 + "f24b727530c162226bc806b7f39079cd494a5c8a242737413d27063f9fb74aadd20f521211316719"
200                 + "c628fd4351d0608928949b6f59f351d9ccec4c596514335010834fcabd53a2cbb2642e0f83c4f89c"
201                 + "199ee2c68ace9182cf484d99e86b0b2213c1cc113d24891958e5a0774b7486abae1475e46a939a94"
202                 + "5d6491b98ad7979fd6e752b47e43e960557a0c0589d7d0444b011d75c9f5b143da6e1dcf7b678a2e"
203                 + "f82fbe37a74df3e20fb1a9dbfd5978"));
204         KAT_VECTORS.put("RSA/ECB/OAEPPadding", new KatVector(
205                 "c219f4e3e37eae2315f0fa4ebc4b46ef0c6befbb43a51ceda07435fc88a9",
206                 "7a9bcfd0d02b6434025bbf5ba09c2dad118a4a3bca7cced8b404bc0fc2f17ddee13de82c8324294bf2"
207                 + "60ad6e5171c2c3728a0c0fab20dd60e4e56cfef3e66239439ed2eddcc83ac8eeaedfd970e9966de3"
208                 + "94ad1df0df503a0a640a49e10885b3a4115c3e94e893fff87bf9a5808350f957d6bc556ca6b08f81"
209                 + "bf697704a3eb3db774797f883af0dcdc9bd9196d7595bab5e87d3187eb45b5771abe4e4dc70c25fa"
210                 + "b9e3cddb6ae453a1d8e517d000779472e1376e5848b1654a51a9e90be4a4a6d0f6b8723c6e93c471"
211                 + "313ea94f24504ca377b502057331355965a7e0b9c3b1d1fbd24ab5a4167f721d1ddac4d3c094d5c9"
212                 + "0d2e277e9b5617cbf2770186323e89"));
213         KAT_VECTORS.put("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", new KatVector(
214                 "bb2854620bb0e361d1384703dda12acee1fefc22024bcfc40a86390d5342c693aab8c7ed6517d8da86"
215                 + "04492c9d",
216                 "77033c578f24ef0ed93bfe6dc6f7c3f9f0505e7562f67ce987a269cabaa8a3ae7dd5e567a8b37db42d"
217                 + "a79aa86ea2e189af5b9560b39407ff86f2785cdaf660fc7c93649bc24a818de564cb0d03e7681fa8"
218                 + "f3cd42b3bfc58c49d3f049e0c98b07aff95876f05ddc45ebaa7127a198f27ae0cfd161c5598ac795"
219                 + "8ed386d98b13d45730e6dc16313fe012af27d7be0e45215040bbfb07f2d35e34291fe4335a68175a"
220                 + "46be99a15c1ccf673659157e1f52105de5a0a6f8c9d946740216eefe2a01a37b0ab144a44ff0d800"
221                 + "be713b5b44acf4fcb1a60d5db977af4d77fa77bdb8594032b2f5bbdd49346b08e0e98ab1051b462e"
222                 + "160c1bff62b927cd26c936948b723a"));
223         KAT_VECTORS.put("RSA/ECB/OAEPWithSHA-224AndMGF1Padding", new KatVector(
224                 "1bae19434be6599d1987b1ed866dd6b684dcd908bd98d797250be545eafea46d05ebdf9018",
225                 "0f18b4a1153c6f8821e18a4275e4b570d540c8ad86bfc99146e5475238a43ecbe63bc81368cd64b9a2"
226                 + "ab3ccd586e6afaad054c9d7bdc986adf022ec86335d110c53ebd5f2f2bd49d48d6da9541312c9b1b"
227                 + "cc299ca4f59475869e4ec2253c91b137eae274a245fc9ee6262f74754bbda55d8bd25bfa4c1698f3"
228                 + "a22d2d8d7fc6e9fbb56d828e61912b3085d82cceaeb1d2da425871575e7ba31a3d47b1b7d7df0bda"
229                 + "81d62c75a9887bbc528fc6bb51db09884bb513b4cc94ca4a5fe0b370ca548dcdf60eebbf61e7efe7"
230                 + "630fc47256d6d617fc1c2c774405f385650898abea03502cfbdcb53579fd18d896490e67aecdb7c7"
231                 + "b7b950dc7ddba5c64188494c1a177b"));
232         KAT_VECTORS.put("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", new KatVector(
233                 "332c2f2fc066fb29ec0928a52b5111ce6965546ce73927340c42d33b56b6ba547b77ac361ac0d13316"
234                 + "345ca953840023d892fa4ff1aa32cc66d5aa88b79867",
235                 "942c0ba1c67a34a7e116d9281b1df5084c66bc1458faf1b26d4f0f63a57307a9addcd3e5d2f3320071"
236                 + "5a3d95ae84fb40a8dfe4cb0a28873fd5883ff8ee6efbfe38c460c755577b34fcf05bb2077afec7b2"
237                 + "203799022be6a0903915e01e94abc51efe9c5548eb86bbbb4fd7f3bfc7b86f388128b6df1e6ce651"
238                 + "230c6bc18bbf55b029f1e31da880c27d947ff97519df66a57ead6db791c4978f1d62edec0d89bb16"
239                 + "83d237213f3f24271ddb8c4b50a82527954f0e49ae44d3acd8ddd3a57cfbfa456dd40675d5d75542"
240                 + "31c6b79c7fb3500b1631be1d100e67d85ce423845fdc7c7f45e346a8ba573f5d11de9009069468dd"
241                 + "8d517ad4adb1509dd5173ee1862d74"));
242         KAT_VECTORS.put("RSA/ECB/OAEPWithSHA-384AndMGF1Padding", new KatVector(
243                 "f51f158cbad4dbab38403b839c724f09a480c49be29c0e72615539dbe57ec86143f31f19392f419b5b"
244                 + "e4ba9e3c6f1e870d307a7cf1a9e2",
245                 "944243f35f534e7a273e94986b6835a4f5cdc5bc4efb9970d4760986599a02f652a848fcae333ff25a"
246                 + "64108c9b900aaf002688398ad9fc17c73be52726306af9c13540df9d1765336b6f09ba4cb8a54d72"
247                 + "5a4e45854bfa3802cfb110a6d7f7054e6072440ec00da62828cb75fe2566ec5be79eb8a3d1fbe2c2"
248                 + "4439c107e5018e445e201ad80725755543c00dec50bb464c6ca897600eb3cda51fcef8161ac13d75"
249                 + "a3eb30d385a1e718a61ae1b5d47aadb966fc007becc84db397d0b3cd983121872f9975995153e869"
250                 + "9e24554a3c5e885f0ed8cd03e916da5ed541f1598da9bd6209447301d00f086153da353deff9d045"
251                 + "8976ff7570410f0bdcfb3f56b782f5"));
252         KAT_VECTORS.put("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", new KatVector(
253                 "d45f6ccc7e663957f234c237c1f09bf7791f6f5c1b9ef4fefb16e55ded0d96112e590f1bb08a60f85c"
254                 + "2d0d2533f1d69792dfd8d647d880b18f87cfe32488c73613a3d535da7d776d90d9a4ba6a0311f456"
255                 + "8511da49107c",
256                 "5a037df3e5d6f3f703541e2db2aef7c69985e513bdff67c8ade6a09f50e27267bfb444f6c69b40a77a"
257                 + "9136a27b29876af9d2bf4e7099863445d35b188d31f376b89fbd196059667ca657e10b9454c2b25f"
258                 + "046fc9f7b42506e382e6b6fd99409cf97e865e65f8dce5d14a06b8aa8833c4bc72c8764467758f2d"
259                 + "7960243161dce4ca8231e91bfcd3c933a80bc703ceab976224c876b1f550f91a6c2a0332d4377bd8"
260                 + "dfe4b1283ab114e517b7b9e4a6e0bf166d5b506e7a3b7328078e12cb23b1d938760767dc9b3c3eb0"
261                 + "848ddda101792aca9273ad414314c13fc511ffa0358a8f4c5f38edded3a2dc111fa62c80e6032c32"
262                 + "ae04aeac7729f16a6310f1f6785c27"));
263 
264 
265         KAT_VECTORS.put("DESede/CBC/NoPadding",
266                 new KatVector("eac1b7959e1e23c11dc4a0e233eedd99e5bf5dd391a5f107d006133a9af3e385",
267                         new IvParameterSpec(HexEncoding.decode("ecd87bf9c49f37dc")),
268                         "632511c46680d60883a228e62cd31244ad61b987e8df7901dae0eb220c839689"));
269         KAT_VECTORS.put("DESede/CBC/PKCS7Padding",
270                 new KatVector("31323334353637383132333435363738",
271                         new IvParameterSpec(HexEncoding.decode("DFCA366848DEA6BB")),
272                         "e70bb5761d796d7b0eb40b5b60deb6a9726f72d97cf2ada4"));
273         KAT_VECTORS.put("DESede/ECB/NoPadding",
274                 new KatVector("31323334353637383132333435363738",
275                         "ade119f9e35ab3e9ade119f9e35ab3e9"));
276         KAT_VECTORS.put("DESede/ECB/PKCS7Padding",
277                 new KatVector("31323334353637383132333435363738",
278                         "ade119f9e35ab3e9ade119f9e35ab3e94bcb01bbc0d05526"));
279     }
280 
281     private static final long DAY_IN_MILLIS = TestUtils.DAY_IN_MILLIS;
282 
283     private static final byte[] AES128_KAT_KEY_BYTES =
284             HexEncoding.decode("7d9f11a0da111e9d8bdd14f04648ed91");
285 
286     private static final byte[] AES192_KAT_KEY_BYTES =
287             HexEncoding.decode("69ef2c44a48d3dc4d5744a281f7ebb5ca976c2202f91e10c");
288 
289     private static final byte[] AES256_KAT_KEY_BYTES =
290             HexEncoding.decode("cf601cc10aaf434d1f01747136aff222af7fb426d101901712214c3fea18125f");
291 
292     private static final byte[] DESede_KAT_KEY_BYTES = HexEncoding.decode(
293             "5EBE2294ECD0E0F08EAB7690D2A6EE6926AE5CC854E36B6B");
294 
getContext()295     private Context getContext() {
296         return InstrumentationRegistry.getInstrumentation().getTargetContext();
297     }
298 
299     private class DeviceLockSession extends ActivityManagerTestBase implements AutoCloseable {
300 
301         private LockScreenSession mLockCredential;
302 
DeviceLockSession()303         public DeviceLockSession() throws Exception {
304             setUp();
305             mLockCredential = new LockScreenSession();
306             mLockCredential.setLockCredential();
307         }
308 
performDeviceLock()309         public void performDeviceLock() {
310             mLockCredential.sleepDevice();
311             KeyguardManager keyguardManager = (KeyguardManager)getContext().getSystemService(Context.KEYGUARD_SERVICE);
312             for (int i = 0; i < 25 && !keyguardManager.isDeviceLocked(); i++) {
313                 SystemClock.sleep(200);
314             }
315         }
316 
performDeviceUnlock()317         public void performDeviceUnlock() throws Exception {
318             mLockCredential.gotoKeyguard();
319             UiDeviceUtils.pressUnlockButton();
320             mLockCredential.enterAndConfirmLockCredential();
321             launchHomeActivity();
322             KeyguardManager keyguardManager = (KeyguardManager)getContext().getSystemService(
323                     Context.KEYGUARD_SERVICE);
324             for (int i = 0; i < 25 && keyguardManager.isDeviceLocked(); i++) {
325                 SystemClock.sleep(200);
326             }
327             assertFalse(keyguardManager.isDeviceLocked());
328         }
329 
330         @Override
close()331         public void close() throws Exception {
332             mLockCredential.close();
333         }
334     }
335 
336     @Presubmit
337     @Test
testAlgorithmList()338     public void testAlgorithmList() {
339         // Assert that Android Keystore Provider exposes exactly the expected Cipher
340         // transformations. We don't care whether the transformations are exposed via aliases, as
341         // long as canonical names of transformation are accepted.
342         // If the Provider exposes extraneous algorithms, it'll be caught because it'll have to
343         // expose at least one Service for such an algorithm, and this Service's algorithm will
344         // not be in the expected set.
345 
346         Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
347         Set<Service> services = provider.getServices();
348         Set<String> actualAlgsLowerCase = new HashSet<String>();
349         Set<String> expectedAlgsLowerCase = new HashSet<String>(
350                 Arrays.asList(TestUtils.toLowerCase(EXPECTED_ALGORITHMS)));
351         for (Service service : services) {
352             if ("Cipher".equalsIgnoreCase(service.getType())) {
353                 String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
354                 actualAlgsLowerCase.add(algLowerCase);
355             }
356         }
357 
358         TestUtils.assertContentsInAnyOrder(actualAlgsLowerCase,
359                 expectedAlgsLowerCase.toArray(new String[0]));
360     }
361 
362     @Test
testAndroidKeyStoreKeysHandledByAndroidKeyStoreProviderWhenDecrypting()363     public void testAndroidKeyStoreKeysHandledByAndroidKeyStoreProviderWhenDecrypting()
364             throws Exception {
365         Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
366         assertNotNull(provider);
367         for (String algorithm : EXPECTED_ALGORITHMS) {
368             try {
369                 ImportedKey key = importDefaultKatKey(
370                         algorithm,
371                         KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
372                         false);
373 
374                 // Decryption may need additional parameters. Initializing a Cipher for encryption
375                 // forces it to generate any such parameters.
376                 Cipher cipher = Cipher.getInstance(algorithm, provider);
377                 cipher.init(Cipher.ENCRYPT_MODE, key.getKeystoreBackedEncryptionKey());
378                 AlgorithmParameters params = cipher.getParameters();
379 
380                 // Test DECRYPT_MODE
381                 cipher = Cipher.getInstance(algorithm);
382                 Key decryptionKey = key.getKeystoreBackedDecryptionKey();
383                 cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
384                 assertSame(provider, cipher.getProvider());
385 
386                 // Test UNWRAP_MODE
387                 cipher = Cipher.getInstance(algorithm);
388                 if (params != null) {
389                     cipher.init(Cipher.UNWRAP_MODE, decryptionKey, params);
390                 } else {
391                     cipher.init(Cipher.UNWRAP_MODE, decryptionKey);
392                 }
393                 assertSame(provider, cipher.getProvider());
394             } catch (Throwable e) {
395                 throw new RuntimeException("Failed for " + algorithm, e);
396             }
397         }
398     }
399 
400     @Test
testAndroidKeyStorePublicKeysAcceptedByHighestPriorityProviderWhenEncrypting()401     public void testAndroidKeyStorePublicKeysAcceptedByHighestPriorityProviderWhenEncrypting()
402             throws Exception {
403         Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
404         assertNotNull(provider);
405         for (String algorithm : EXPECTED_ALGORITHMS) {
406             if (isSymmetric(algorithm)) {
407                 continue;
408             }
409             try {
410                 Key key = importDefaultKatKey(
411                         algorithm,
412                         KeyProperties.PURPOSE_ENCRYPT,
413                         false).getKeystoreBackedEncryptionKey();
414 
415                 Cipher cipher = Cipher.getInstance(algorithm);
416                 cipher.init(Cipher.ENCRYPT_MODE, key);
417 
418                 cipher = Cipher.getInstance(algorithm);
419                 cipher.init(Cipher.WRAP_MODE, key);
420             } catch (Throwable e) {
421                 throw new RuntimeException("Failed for" + algorithm, e);
422             }
423         }
424     }
425 
426     @Test
testEmptyPlaintextEncryptsAndDecrypts()427     public void testEmptyPlaintextEncryptsAndDecrypts()
428             throws Exception {
429         Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
430         assertNotNull(provider);
431         final byte[] originalPlaintext = EmptyArray.BYTE;
432         for (String algorithm : EXPECTED_ALGORITHMS) {
433             for (ImportedKey key : importKatKeys(
434                     algorithm,
435                     KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
436                     false)) {
437                 try {
438                     Key encryptionKey = key.getKeystoreBackedEncryptionKey();
439                     byte[] plaintext = truncatePlaintextIfNecessary(
440                             algorithm, encryptionKey, originalPlaintext);
441                     if (plaintext == null) {
442                         // Key is too short to encrypt anything using this transformation
443                         continue;
444                     }
445                     Cipher cipher = Cipher.getInstance(algorithm, provider);
446                     cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
447                     AlgorithmParameters params = cipher.getParameters();
448                     byte[] ciphertext = cipher.doFinal(plaintext);
449                     byte[] expectedPlaintext = plaintext;
450                     if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
451                         // RSA decryption without padding left-pads resulting plaintext with NUL
452                         // bytes to the length of RSA modulus.
453                         int modulusLengthBytes = (TestUtils.getKeySizeBits(encryptionKey) + 7) / 8;
454                         expectedPlaintext = TestUtils.leftPadWithZeroBytes(
455                                 expectedPlaintext, modulusLengthBytes);
456                     }
457 
458                     cipher = Cipher.getInstance(algorithm, provider);
459                     Key decryptionKey = key.getKeystoreBackedDecryptionKey();
460                     cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
461                     byte[] actualPlaintext = cipher.doFinal(ciphertext);
462                     assertArrayEquals(expectedPlaintext, actualPlaintext);
463                 } catch (Throwable e) {
464                     throw new RuntimeException(
465                             "Failed for " + algorithm + " with key " + key.getAlias(),
466                             e);
467                 }
468             }
469         }
470     }
471 
472     /*
473      * This test performs a round trip en/decryption. It does so while the current thread
474      * is in interrupted state which cannot be signaled to the user of the Java Crypto
475      * API.
476      */
477     @Test
testEncryptsAndDecryptsInterrupted()478     public void testEncryptsAndDecryptsInterrupted()
479             throws Exception {
480 
481         Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
482         assertNotNull(provider);
483         final byte[] originalPlaintext = EmptyArray.BYTE;
484         for (String algorithm : EXPECTED_ALGORITHMS) {
485             for (ImportedKey key : importKatKeys(
486                     algorithm,
487                     KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
488                     false)) {
489                 try {
490                     Key encryptionKey = key.getKeystoreBackedEncryptionKey();
491                     byte[] plaintext = truncatePlaintextIfNecessary(
492                             algorithm, encryptionKey, originalPlaintext);
493                     if (plaintext == null) {
494                         // Key is too short to encrypt anything using this transformation
495                         continue;
496                     }
497                     Cipher cipher = Cipher.getInstance(algorithm, provider);
498                     Thread.currentThread().interrupt();
499                     cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
500                     AlgorithmParameters params = cipher.getParameters();
501                     byte[] ciphertext = cipher.doFinal(plaintext);
502                     byte[] expectedPlaintext = plaintext;
503                     if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
504                         // RSA decryption without padding left-pads resulting plaintext with NUL
505                         // bytes to the length of RSA modulus.
506                         int modulusLengthBytes = (TestUtils.getKeySizeBits(encryptionKey) + 7) / 8;
507                         expectedPlaintext = TestUtils.leftPadWithZeroBytes(
508                                 expectedPlaintext, modulusLengthBytes);
509                     }
510 
511                     cipher = Cipher.getInstance(algorithm, provider);
512                     Key decryptionKey = key.getKeystoreBackedDecryptionKey();
513                     cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
514                     byte[] actualPlaintext = cipher.doFinal(ciphertext);
515                     assertTrue(Thread.currentThread().interrupted());
516                     assertArrayEquals(expectedPlaintext, actualPlaintext);
517                 } catch (Throwable e) {
518                     throw new RuntimeException(
519                             "Failed for " + algorithm + " with key " + key.getAlias(),
520                             e);
521                 }
522             }
523         }
524     }
525 
526     /*
527      * This test performs a round trip en/decryption using Cipher*Streams.
528      */
529     @Test
testEncryptsAndDecryptsUsingCipherStreams()530     public void testEncryptsAndDecryptsUsingCipherStreams()
531             throws Exception {
532 
533         Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
534         assertNotNull(provider);
535         final byte[] originalPlaintext = new byte[1024];
536         new Random().nextBytes(originalPlaintext);
537         for (String algorithm : EXPECTED_ALGORITHMS) {
538             for (ImportedKey key : importKatKeys(
539                     algorithm,
540                     KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
541                     false)) {
542                 try {
543                     Key encryptionKey = key.getKeystoreBackedEncryptionKey();
544                     byte[] plaintext = truncatePlaintextIfNecessary(
545                             algorithm, encryptionKey, originalPlaintext);
546                     if (plaintext == null) {
547                         // Key is too short to encrypt anything using this transformation
548                         continue;
549                     }
550                     Cipher cipher = Cipher.getInstance(algorithm, provider);
551                     cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
552                     final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
553                     final CipherOutputStream cipherOutputStream =
554                             new CipherOutputStream(byteArrayOutputStream, cipher);
555 
556                     cipherOutputStream.write(plaintext);
557                     cipherOutputStream.close();
558                     AlgorithmParameters params = cipher.getParameters();
559                     byte[] expectedPlaintext = plaintext;
560                     if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
561                         // RSA decryption without padding left-pads resulting plaintext with NUL
562                         // bytes to the length of RSA modulus.
563                         int modulusLengthBytes = (TestUtils.getKeySizeBits(encryptionKey) + 7) / 8;
564                         expectedPlaintext = TestUtils.leftPadWithZeroBytes(
565                                 expectedPlaintext, modulusLengthBytes);
566                     }
567 
568                     byte[] ciphertext = byteArrayOutputStream.toByteArray();
569                     assertNotNull(ciphertext);
570                     cipher = Cipher.getInstance(algorithm, provider);
571                     Key decryptionKey = key.getKeystoreBackedDecryptionKey();
572                     cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
573 
574                     final ByteArrayInputStream byteArrayInputStream =
575                             new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
576                     final CipherInputStream cipherInputStream =
577                             new CipherInputStream(byteArrayInputStream, cipher);
578                     byte[] actualPlaintext = new byte[plaintext.length * 2];
579                     int total = 0;
580                     int count = 0;
581                     while((count = cipherInputStream.read(actualPlaintext, total,
582                             actualPlaintext.length - total)) != -1) {
583                         total += count;
584                     }
585                     actualPlaintext = Arrays.copyOf(actualPlaintext, total);
586                     cipherInputStream.close();
587                     assertTrue("expected(" + expectedPlaintext.length + "): "
588                             + HexEncoding.encode(expectedPlaintext)
589                             + "\nactual(" + actualPlaintext.length + "): "
590                             + HexEncoding.encode(actualPlaintext),
591                             Arrays.equals(expectedPlaintext, actualPlaintext));
592                 } catch (Throwable e) {
593                     throw new RuntimeException(
594                             "Failed for " + algorithm + " with key " + key.getAlias(),
595                             e);
596                 }
597             }
598         }
599     }
600 
isDecryptValid(byte[] expectedPlaintext, byte[] ciphertext, Cipher cipher, AlgorithmParameters params, ImportedKey key)601     private boolean isDecryptValid(byte[] expectedPlaintext, byte[] ciphertext, Cipher cipher,
602             AlgorithmParameters params, ImportedKey key) {
603         try {
604             Key decryptionKey = key.getKeystoreBackedDecryptionKey();
605             cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
606             byte[] actualPlaintext = cipher.doFinal(ciphertext);
607             assertArrayEquals(expectedPlaintext, actualPlaintext);
608             return true;
609         } catch (Throwable e) {
610             return false;
611         }
612     }
613 
isLeanbackOnly()614     private boolean isLeanbackOnly() {
615         PackageManager pm = getContext().getPackageManager();
616         return (pm != null && pm.hasSystemFeature("android.software.leanback_only"));
617     }
618 
619     @Presubmit
620     @Test
testKeyguardLockAndUnlock()621     public void testKeyguardLockAndUnlock()
622             throws Exception {
623         if (!TestUtils.hasSecureLockScreen(getContext())) {
624             return;
625         }
626 
627         try (DeviceLockSession dl = new DeviceLockSession()) {
628             KeyguardManager keyguardManager = (KeyguardManager)getContext()
629                     .getSystemService(Context.KEYGUARD_SERVICE);
630 
631             dl.performDeviceLock();
632             assertTrue(keyguardManager.isDeviceLocked());
633 
634             dl.performDeviceUnlock();
635             assertFalse(keyguardManager.isDeviceLocked());
636         }
637     }
638 
639     @Test
testEmptyPlaintextEncryptsAndDecryptsWhenUnlockedRequired()640     public void testEmptyPlaintextEncryptsAndDecryptsWhenUnlockedRequired()
641             throws Exception {
642         final boolean isUnlockedDeviceRequired = true;
643         final boolean isUserAuthRequired = false;
644 
645         if (!TestUtils.hasSecureLockScreen(getContext())) {
646             return;
647         }
648 
649         try (DeviceLockSession dl = new DeviceLockSession()) {
650             dl.performDeviceLock();
651             KeyguardManager keyguardManager = (KeyguardManager)getContext()
652                 .getSystemService(Context.KEYGUARD_SERVICE);
653 
654             Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
655             assertNotNull(provider);
656             final byte[] originalPlaintext = EmptyArray.BYTE;
657             // Normally we would test all combinations of algorithms and key sizes, but the
658             // semi-manual locking and unlocking this requires takes way too long if we try to
659             // go through all of those. Other tests check all the key sizes, so we don't need to
660             // duplicate all that work.
661             for (String algorithm : BASIC_ALGORITHMS) {
662                 for (boolean createWhileLocked : new boolean[]{false, true}) {
663                     try {
664                         if (createWhileLocked) {
665                             dl.performDeviceLock();
666                         } else {
667                             dl.performDeviceUnlock();
668                         }
669                         // Test just a single key in the collection
670                         ImportedKey key = importKatKeys(
671                                 algorithm,
672                                 KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
673                                 false, isUnlockedDeviceRequired,
674                                 isUserAuthRequired).iterator().next();
675                         if (createWhileLocked) {
676                             dl.performDeviceUnlock();
677                         }
678                         Key encryptionKey = key.getKeystoreBackedEncryptionKey();
679                         byte[] plaintext = truncatePlaintextIfNecessary(
680                                 algorithm, encryptionKey, originalPlaintext);
681                         if (plaintext == null) {
682                             // Key is too short to encrypt anything using this transformation
683                             continue;
684                         }
685 
686                         Cipher cipher = Cipher.getInstance(algorithm, provider);
687                         cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
688                         AlgorithmParameters params = cipher.getParameters();
689                         byte[] ciphertext = cipher.doFinal(plaintext);
690                         byte[] expectedPlaintext = plaintext;
691                         if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
692                             // RSA decryption without padding left-pads resulting plaintext
693                             // with NUL bytes to the length of RSA modulus.
694                             int modulusLengthBytes = (TestUtils.getKeySizeBits(encryptionKey)
695                                     + 7) / 8;
696                             expectedPlaintext = TestUtils.leftPadWithZeroBytes(
697                                     expectedPlaintext, modulusLengthBytes);
698                         }
699 
700                         dl.performDeviceLock();
701 
702                         // Attempt to decrypt the data with the device locked.
703                         cipher = Cipher.getInstance(algorithm, provider);
704                         assertFalse(
705                                 isDecryptValid(expectedPlaintext, ciphertext, cipher, params,
706                                         key));
707 
708                         // Then attempt to decrypt the data with the device unlocked
709                         // This should succeed
710                         dl.performDeviceUnlock();
711                         cipher = Cipher.getInstance(algorithm, provider);
712                         assertTrue(isDecryptValid(expectedPlaintext, ciphertext, cipher, params,
713                                 key));
714 
715                         // Ensure a second decryption also succeeds
716                         cipher = Cipher.getInstance(algorithm, provider);
717                         assertTrue(isDecryptValid(expectedPlaintext, ciphertext, cipher, params,
718                                 key));
719                     } catch (Throwable e) {
720                         throw new RuntimeException(
721                                 "Failed on createWhileLocked " + createWhileLocked + " for " +
722                                         algorithm,
723                                 e);
724                     }
725                 }
726             }
727         }
728     }
729 
730     @Test
testCiphertextGeneratedByAndroidKeyStoreDecryptsByAndroidKeyStore()731     public void testCiphertextGeneratedByAndroidKeyStoreDecryptsByAndroidKeyStore()
732             throws Exception {
733         Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
734         assertNotNull(provider);
735         final byte[] originalPlaintext = "Very secret message goes here...".getBytes("US-ASCII");
736         for (String algorithm : EXPECTED_ALGORITHMS) {
737             for (ImportedKey key : importKatKeys(
738                     algorithm,
739                     KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
740                     false)) {
741                 try {
742                     Key encryptionKey = key.getKeystoreBackedEncryptionKey();
743                     byte[] plaintext = truncatePlaintextIfNecessary(
744                             algorithm, encryptionKey, originalPlaintext);
745                     if (plaintext == null) {
746                         // Key is too short to encrypt anything using this transformation
747                         continue;
748                     }
749                     Cipher cipher = Cipher.getInstance(algorithm, provider);
750                     cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
751                     AlgorithmParameters params = cipher.getParameters();
752                     byte[] ciphertext = cipher.doFinal(plaintext);
753                     byte[] expectedPlaintext = plaintext;
754                     if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
755                         // RSA decryption without padding left-pads resulting plaintext with NUL
756                         // bytes to the length of RSA modulus.
757                         int modulusLengthBytes = (TestUtils.getKeySizeBits(encryptionKey) + 7) / 8;
758                         expectedPlaintext = TestUtils.leftPadWithZeroBytes(
759                                 expectedPlaintext, modulusLengthBytes);
760                     }
761 
762                     cipher = Cipher.getInstance(algorithm, provider);
763                     Key decryptionKey = key.getKeystoreBackedDecryptionKey();
764                     cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
765                     byte[] actualPlaintext = cipher.doFinal(ciphertext);
766                     assertArrayEquals(expectedPlaintext, actualPlaintext);
767                 } catch (Throwable e) {
768                     throw new RuntimeException(
769                             "Failed for " + algorithm + " with key " + key.getAlias(),
770                             e);
771                 }
772             }
773         }
774     }
775 
776     @Test
testCiphertextGeneratedByHighestPriorityProviderDecryptsByAndroidKeyStore()777     public void testCiphertextGeneratedByHighestPriorityProviderDecryptsByAndroidKeyStore()
778             throws Exception {
779         Provider keystoreProvider = Security.getProvider(EXPECTED_PROVIDER_NAME);
780         assertNotNull(keystoreProvider);
781         byte[] originalPlaintext = "Very secret message goes here...".getBytes("UTF-8");
782         for (String algorithm : EXPECTED_ALGORITHMS) {
783             for (ImportedKey key : importKatKeys(
784                     algorithm,
785                     KeyProperties.PURPOSE_DECRYPT,
786                     false)) {
787                 Provider encryptionProvider = null;
788                 try {
789                     Key encryptionKey = key.getOriginalEncryptionKey();
790                     byte[] plaintext = truncatePlaintextIfNecessary(
791                             algorithm, encryptionKey, originalPlaintext);
792                     if (plaintext == null) {
793                         // Key is too short to encrypt anything using this transformation
794                         continue;
795                     }
796 
797                     Cipher cipher;
798                     try {
799                         cipher = Cipher.getInstance(algorithm);
800                         cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
801                     } catch (InvalidKeyException e) {
802                         // No providers support encrypting using this algorithm and key.
803                         continue;
804                     }
805                     encryptionProvider = cipher.getProvider();
806                     if (keystoreProvider == encryptionProvider) {
807                         // This is covered by another test.
808                         continue;
809                     }
810                     AlgorithmParameters params = cipher.getParameters();
811 
812                     // TODO: Remove this workaround for Bug 22405492 once the issue is fixed. The
813                     // issue is that Bouncy Castle incorrectly defaults the MGF1 digest to the
814                     // digest specified in the transformation. RI and Android Keystore keep the MGF1
815                     // digest defaulted at SHA-1.
816                     if ((params != null) && ("OAEP".equalsIgnoreCase(params.getAlgorithm()))) {
817                         OAEPParameterSpec spec = params.getParameterSpec(OAEPParameterSpec.class);
818                         if (!"SHA-1".equalsIgnoreCase(
819                                 ((MGF1ParameterSpec) spec.getMGFParameters())
820                                         .getDigestAlgorithm())) {
821                             // Create a new instance of Cipher because Bouncy Castle's RSA Cipher
822                             // caches AlgorithmParameters returned by Cipher.getParameters and does
823                             // not invalidate the cache when reinitialized with different
824                             // parameters.
825                             cipher = Cipher.getInstance(algorithm, encryptionProvider);
826                             cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new OAEPParameterSpec(
827                                     spec.getDigestAlgorithm(),
828                                     "MGF1",
829                                     MGF1ParameterSpec.SHA1,
830                                     PSource.PSpecified.DEFAULT));
831                             params = cipher.getParameters();
832                             OAEPParameterSpec newSpec =
833                                     params.getParameterSpec(OAEPParameterSpec.class);
834                             assertEquals(spec.getDigestAlgorithm(), newSpec.getDigestAlgorithm());
835                             assertEquals(
836                                     "SHA-1",
837                                     ((MGF1ParameterSpec) newSpec.getMGFParameters())
838                                             .getDigestAlgorithm());
839                         }
840                     }
841 
842                     byte[] ciphertext = cipher.doFinal(plaintext);
843                     byte[] expectedPlaintext = plaintext;
844                     if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
845                         // RSA decryption without padding left-pads resulting plaintext with NUL
846                         // bytes to the length of RSA modulus.
847                         int modulusLengthBytes = (TestUtils.getKeySizeBits(encryptionKey) + 7) / 8;
848                         expectedPlaintext = TestUtils.leftPadWithZeroBytes(
849                                 expectedPlaintext, modulusLengthBytes);
850                     }
851 
852                     // TODO: Remove this workaround once Android Keystore AES-GCM supports IVs of
853                     // sizes other than 12 bytes. For example, Bouncy Castle auto-generates 16-byte
854                     // long IVs.
855                     if ("AES/GCM/NoPadding".equalsIgnoreCase(algorithm)) {
856                         byte[] iv = cipher.getIV();
857                         if ((iv != null) && (iv.length != 12)) {
858                             // Android Keystore AES-GCM only supports 12-byte long IVs.
859                             continue;
860                         }
861                     }
862 
863                     // TODO: Remove this workaround for Bug 22319986 once the issue is fixed. The issue
864                     // is that Conscrypt and Bouncy Castle's AES/GCM/NoPadding implementations return
865                     // AlgorithmParameters of algorithm "AES" from which it's impossible to obtain a
866                     // GCMParameterSpec. They should be returning AlgorithmParameters of algorithm
867                     // "GCM".
868                     if (("AES/GCM/NoPadding".equalsIgnoreCase(algorithm))
869                             && (!"GCM".equalsIgnoreCase(params.getAlgorithm()))) {
870                         params = AlgorithmParameters.getInstance("GCM");
871                         params.init(new GCMParameterSpec(128, cipher.getIV()));
872                     }
873 
874                     cipher = Cipher.getInstance(algorithm, keystoreProvider);
875                     Key decryptionKey = key.getKeystoreBackedDecryptionKey();
876                     cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
877                     byte[] actualPlaintext = cipher.doFinal(ciphertext);
878                     assertArrayEquals(expectedPlaintext, actualPlaintext);
879                 } catch (Throwable e) {
880                     throw new RuntimeException(
881                             "Failed for " + algorithm + " with key " + key.getAlias()
882                                     + ", encryption provider: " + encryptionProvider,
883                             e);
884                 }
885             }
886         }
887     }
888 
889     @Test
testCiphertextGeneratedByAndroidKeyStoreDecryptsByHighestPriorityProvider()890     public void testCiphertextGeneratedByAndroidKeyStoreDecryptsByHighestPriorityProvider()
891             throws Exception {
892         Provider keystoreProvider = Security.getProvider(EXPECTED_PROVIDER_NAME);
893         assertNotNull(keystoreProvider);
894         byte[] originalPlaintext = "Very secret message goes here...".getBytes("UTF-8");
895         for (String algorithm : EXPECTED_ALGORITHMS) {
896             for (ImportedKey key : importKatKeys(
897                     algorithm,
898                     KeyProperties.PURPOSE_ENCRYPT,
899                     false)) {
900                 Provider decryptionProvider = null;
901                 try {
902                     Key encryptionKey = key.getKeystoreBackedEncryptionKey();
903                     byte[] plaintext = truncatePlaintextIfNecessary(
904                             algorithm, encryptionKey, originalPlaintext);
905                     if (plaintext == null) {
906                         // Key is too short to encrypt anything using this transformation
907                         continue;
908                     }
909                     Cipher cipher = Cipher.getInstance(algorithm, keystoreProvider);
910                     cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
911                     AlgorithmParameters params = cipher.getParameters();
912 
913                     byte[] ciphertext = cipher.doFinal(plaintext);
914                     byte[] expectedPlaintext = plaintext;
915                     if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
916                         // RSA decryption without padding left-pads resulting plaintext with NUL
917                         // bytes to the length of RSA modulus.
918                         int modulusLengthBytes = (TestUtils.getKeySizeBits(encryptionKey) + 7) / 8;
919                         expectedPlaintext = TestUtils.leftPadWithZeroBytes(
920                                 expectedPlaintext, modulusLengthBytes);
921                     }
922 
923                     Key decryptionKey = key.getOriginalDecryptionKey();
924                     try {
925                         cipher = Cipher.getInstance(algorithm);
926                         if (params != null) {
927                             cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
928                         } else {
929                             cipher.init(Cipher.DECRYPT_MODE, decryptionKey);
930                         }
931                     } catch (InvalidKeyException e) {
932                         // No providers support decrypting using this algorithm and key.
933                         continue;
934                     }
935                     decryptionProvider = cipher.getProvider();
936                     if (keystoreProvider == decryptionProvider) {
937                         // This is covered by another test.
938                         continue;
939                     }
940                     byte[] actualPlaintext = cipher.doFinal(ciphertext);
941                     assertArrayEquals(expectedPlaintext, actualPlaintext);
942                 } catch (Throwable e) {
943                     throw new RuntimeException(
944                             "Failed for " + algorithm + " with key " + key.getAlias()
945                                     + ", decryption provider: " + decryptionProvider,
946                             e);
947                 }
948             }
949         }
950     }
951 
952     @Test
testMaxSizedPlaintextSupported()953     public void testMaxSizedPlaintextSupported() throws Exception {
954         Provider keystoreProvider = Security.getProvider(EXPECTED_PROVIDER_NAME);
955         assertNotNull(keystoreProvider);
956         for (String algorithm : EXPECTED_ALGORITHMS) {
957             if (isSymmetric(algorithm)) {
958                 // No input length restrictions (except multiple of block size for some
959                 // transformations).
960                 continue;
961             }
962             for (ImportedKey key : importKatKeys(
963                     algorithm,
964                     KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
965                     false)) {
966                 int plaintextSizeBytes = -1;
967                 Provider otherProvider = null;
968                 try {
969                     Key encryptionKey = key.getKeystoreBackedEncryptionKey();
970                     int maxSupportedPlaintextSizeBytes =
971                             TestUtils.getMaxSupportedPlaintextInputSizeBytes(
972                                     algorithm, encryptionKey);
973                     if (maxSupportedPlaintextSizeBytes < 0) {
974                         // Key too short to encrypt anything using this transformation.
975                         continue;
976                     } else if (maxSupportedPlaintextSizeBytes == Integer.MAX_VALUE) {
977                         // No input length restrictions.
978                         continue;
979                     }
980                     byte[] plaintext = new byte[maxSupportedPlaintextSizeBytes];
981                     Arrays.fill(plaintext, (byte) 0xff);
982                     plaintextSizeBytes = plaintext.length;
983 
984                     // Encrypt plaintext using Android Keystore Cipher
985                     Cipher cipher = Cipher.getInstance(algorithm, keystoreProvider);
986                     cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
987                     AlgorithmParameters params = cipher.getParameters();
988                     byte[] ciphertext = cipher.doFinal(plaintext);
989                     byte[] expectedPlaintext = plaintext;
990                     if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
991                         // RSA decryption without padding left-pads resulting plaintext with NUL
992                         // bytes to the length of RSA modulus.
993                         int modulusLengthBytes = (TestUtils.getKeySizeBits(encryptionKey) + 7) / 8;
994                         expectedPlaintext = TestUtils.leftPadWithZeroBytes(
995                                 expectedPlaintext, modulusLengthBytes);
996                     }
997 
998                     // Check that ciphertext decrypts using Android Keystore Cipher
999                     cipher = Cipher.getInstance(algorithm, keystoreProvider);
1000                     Key decryptionKey = key.getKeystoreBackedDecryptionKey();
1001                     cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
1002                     byte[] actualPlaintext = cipher.doFinal(ciphertext);
1003                     assertArrayEquals(expectedPlaintext, actualPlaintext);
1004 
1005                     // Check that ciphertext decrypts using the highest-priority provider.
1006                     cipher = Cipher.getInstance(algorithm);
1007                     decryptionKey = key.getOriginalDecryptionKey();
1008                     try {
1009                         cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
1010                     } catch (InvalidKeyException e) {
1011                         // No other providers offer decryption using this transformation and key.
1012                         continue;
1013                     }
1014                     otherProvider = cipher.getProvider();
1015                     if (otherProvider == keystoreProvider) {
1016                         // This has already been tested above.
1017                         continue;
1018                     }
1019                     actualPlaintext = cipher.doFinal(ciphertext);
1020                     assertArrayEquals(expectedPlaintext, actualPlaintext);
1021                 } catch (Throwable e) {
1022                     throw new RuntimeException(
1023                             "Failed for " + algorithm + " with key " + key.getAlias()
1024                                     + " and " + plaintextSizeBytes + " long plaintext"
1025                                     + ", other provider: " + otherProvider,
1026                             e);
1027                 }
1028             }
1029         }
1030     }
1031 
1032     @Test
testLargerThanMaxSizedPlaintextRejected()1033     public void testLargerThanMaxSizedPlaintextRejected() throws Exception {
1034         Provider keystoreProvider = Security.getProvider(EXPECTED_PROVIDER_NAME);
1035         assertNotNull(keystoreProvider);
1036         for (String algorithm : EXPECTED_ALGORITHMS) {
1037             if (isSymmetric(algorithm)) {
1038                 // No input length restrictions (except multiple of block size for some
1039                 // transformations).
1040                 continue;
1041             }
1042             for (ImportedKey key : importKatKeys(
1043                     algorithm,
1044                     KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
1045                     false)) {
1046                 int plaintextSizeBytes = -1;
1047                 Provider otherProvider = null;
1048                 try {
1049                     Key encryptionKey = key.getKeystoreBackedEncryptionKey();
1050                     int maxSupportedPlaintextSizeBytes =
1051                             TestUtils.getMaxSupportedPlaintextInputSizeBytes(
1052                                     algorithm, encryptionKey);
1053                     if (maxSupportedPlaintextSizeBytes < 0) {
1054                         // Key too short to encrypt anything using this transformation.
1055                         continue;
1056                     } else if (maxSupportedPlaintextSizeBytes == Integer.MAX_VALUE) {
1057                         // No input length restrictions.
1058                         continue;
1059                     }
1060                     // Create plaintext which is one byte longer than maximum supported one.
1061                     byte[] plaintext = new byte[maxSupportedPlaintextSizeBytes + 1];
1062                     Arrays.fill(plaintext, (byte) 0xff);
1063                     plaintextSizeBytes = plaintext.length;
1064 
1065                     // Encrypting this plaintext using Android Keystore Cipher should fail.
1066                     Cipher cipher = Cipher.getInstance(algorithm, keystoreProvider);
1067                     cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
1068                     try {
1069                         byte[] ciphertext = cipher.doFinal(plaintext);
1070                         fail("Unexpectedly produced ciphertext (" + ciphertext.length
1071                                 + " bytes): " + HexEncoding.encode(ciphertext) + " for "
1072                                 + plaintext.length + " byte long plaintext");
1073                     } catch (IllegalBlockSizeException | BadPaddingException |
1074                             ArrayIndexOutOfBoundsException expected) {}
1075 
1076                     // Encrypting this plaintext using the highest-priority implementation should
1077                     // fail.
1078                     cipher = Cipher.getInstance(algorithm);
1079                     encryptionKey = key.getOriginalEncryptionKey();
1080                     try {
1081                         cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
1082                     } catch (InvalidKeyException e) {
1083                         // No other providers support this transformation with this key.
1084                         continue;
1085                     }
1086                     otherProvider = cipher.getProvider();
1087                     if (otherProvider == keystoreProvider) {
1088                         // This has already been tested above.
1089                         continue;
1090                     }
1091                     try {
1092                         byte[] ciphertext = cipher.doFinal(plaintext);
1093                         fail(otherProvider.getName() + " unexpectedly produced ciphertext ("
1094                                 + ciphertext.length + " bytes): "
1095                                 + HexEncoding.encode(ciphertext) + " for "
1096                                 + plaintext.length + " byte long plaintext");
1097                         // TODO: Remove the catching of RuntimeException and BadPaddingException
1098                         // workaround once the corresponding Bug 22567463 in Conscrypt is fixed.
1099                     } catch (IllegalBlockSizeException | BadPaddingException | RuntimeException
1100                             exception) {}
1101                 } catch (Throwable e) {
1102                     throw new RuntimeException(
1103                             "Failed for " + algorithm + " with key " + key.getAlias()
1104                             + " and " + plaintextSizeBytes + " byte long plaintext"
1105                             + ", other provider: " + otherProvider,
1106                             e);
1107                 }
1108             }
1109         }
1110     }
1111 
1112     @Test
testKat()1113     public void testKat() throws Exception {
1114         Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
1115         assertNotNull(provider);
1116         for (String algorithm : EXPECTED_ALGORITHMS) {
1117             try {
1118                 ImportedKey key = importDefaultKatKey(algorithm,
1119                         KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
1120                         true);
1121                 KatVector testVector = KAT_VECTORS.get(algorithm);
1122                 assertNotNull(testVector);
1123                 Cipher cipher = Cipher.getInstance(algorithm, provider);
1124                 Key decryptionKey = key.getKeystoreBackedDecryptionKey();
1125                 cipher.init(Cipher.DECRYPT_MODE, decryptionKey, testVector.params);
1126                 byte[] actualPlaintext = cipher.doFinal(testVector.ciphertext);
1127                 byte[] expectedPlaintext = testVector.plaintext;
1128                 if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
1129                     // RSA decryption without padding left-pads resulting plaintext with NUL bytes
1130                     // to the length of RSA modulus.
1131                     int modulusLengthBytes = (TestUtils.getKeySizeBits(decryptionKey) + 7) / 8;
1132                     expectedPlaintext = TestUtils.leftPadWithZeroBytes(
1133                             expectedPlaintext, modulusLengthBytes);
1134                 }
1135                 assertArrayEquals(expectedPlaintext, actualPlaintext);
1136                 if (!isRandomizedEncryption(algorithm)) {
1137                     // Deterministic encryption: ciphertext depends only on plaintext and input
1138                     // parameters. Assert that encrypting the plaintext results in the same
1139                     // ciphertext as in the test vector.
1140                     Key encryptionKey = key.getKeystoreBackedEncryptionKey();
1141                     cipher = Cipher.getInstance(algorithm, provider);
1142                     cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, testVector.params);
1143                     byte[] actualCiphertext = cipher.doFinal(testVector.plaintext);
1144                     assertArrayEquals(testVector.ciphertext, actualCiphertext);
1145                 }
1146             } catch (Throwable e) {
1147                 throw new RuntimeException("Failed for " + algorithm, e);
1148             }
1149         }
1150     }
1151 
isRandomizedEncryption(String transformation)1152     private static boolean isRandomizedEncryption(String transformation) {
1153         String transformationUpperCase = transformation.toUpperCase(Locale.US);
1154         return (transformationUpperCase.endsWith("/PKCS1PADDING"))
1155                 || (transformationUpperCase.contains("OAEP"));
1156     }
1157 
1158     @Test
testCanCreateAuthBoundKeyWhenScreenLocked()1159     public void testCanCreateAuthBoundKeyWhenScreenLocked() throws Exception {
1160         final boolean isUnlockedDeviceRequired = false;
1161         final boolean isUserAuthRequired = true;
1162 
1163         if (!TestUtils.hasSecureLockScreen(getContext())) {
1164             return;
1165         }
1166 
1167         try (DeviceLockSession dl = new DeviceLockSession()) {
1168             KeyguardManager keyguardManager = (KeyguardManager)getContext().getSystemService(Context.KEYGUARD_SERVICE);
1169 
1170             dl.performDeviceLock();
1171             assertTrue(keyguardManager.isDeviceLocked());
1172 
1173             Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
1174             assertNotNull(provider);
1175 
1176             for (String algorithm : EXPECTED_ALGORITHMS) {
1177                 for (ImportedKey key : importKatKeys(algorithm,
1178                         KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
1179                         false, isUnlockedDeviceRequired, isUserAuthRequired)) {
1180                     assertNotNull(key);
1181                 }
1182             }
1183         }
1184     }
1185 
1186     @Test
testCannotCreateAuthBoundKeyWhenDevicePinNotSet()1187     public void testCannotCreateAuthBoundKeyWhenDevicePinNotSet() throws Exception {
1188         final boolean isUserAuthRequired = true;
1189         final boolean isUnlockedDeviceRequired = false;
1190 
1191         if (isLeanbackOnly()) {
1192             return;
1193         }
1194 
1195         KeyguardManager keyguardManager = (KeyguardManager)getContext().getSystemService(Context.KEYGUARD_SERVICE);
1196         assertFalse(keyguardManager.isDeviceLocked());
1197 
1198         for (String algorithm : EXPECTED_ALGORITHMS) {
1199             try {
1200                 importKatKeys(algorithm, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
1201                         false, isUnlockedDeviceRequired, isUserAuthRequired);
1202                 fail("Importing auth bound keys to an insecure device should fail");
1203             } catch (KeyStoreException e) {
1204                 // Expected behavior
1205                 assertThat(e.getCause()).isInstanceOf(IllegalStateException.class);
1206             }
1207         }
1208     }
1209 
1210     @Test
testInitDecryptFailsWhenNotAuthorizedToDecrypt()1211     public void testInitDecryptFailsWhenNotAuthorizedToDecrypt() throws Exception {
1212         for (String transformation : EXPECTED_ALGORITHMS) {
1213             try {
1214                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1215                         transformation,
1216                         KeyProperties.PURPOSE_DECRYPT);
1217                 assertInitDecryptSucceeds(transformation, good);
1218                 assertInitDecryptThrowsInvalidKeyException(transformation,
1219                         TestUtils.buildUpon(good, KeyProperties.PURPOSE_ENCRYPT).build());
1220             } catch (Throwable e) {
1221                 throw new RuntimeException("Failed for " + transformation, e);
1222             }
1223         }
1224     }
1225 
1226     @Test
testInitEncryptSymmetricFailsWhenNotAuthorizedToEncrypt()1227     public void testInitEncryptSymmetricFailsWhenNotAuthorizedToEncrypt() throws Exception {
1228         for (String transformation : EXPECTED_ALGORITHMS) {
1229             if (!isSymmetric(transformation)) {
1230                 continue;
1231             }
1232 
1233             try {
1234                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1235                         transformation,
1236                         KeyProperties.PURPOSE_ENCRYPT);
1237                 assertInitEncryptSucceeds(transformation, good);
1238                 assertInitEncryptThrowsInvalidKeyException(transformation,
1239                         TestUtils.buildUpon(good, KeyProperties.PURPOSE_DECRYPT).build());
1240             } catch (Throwable e) {
1241                 throw new RuntimeException("Failed for " + transformation, e);
1242             }
1243         }
1244     }
1245 
1246     @Test
testInitEncryptAsymmetricIgnoresAuthorizedPurposes()1247     public void testInitEncryptAsymmetricIgnoresAuthorizedPurposes() throws Exception {
1248         for (String transformation : EXPECTED_ALGORITHMS) {
1249             if (isSymmetric(transformation)) {
1250                 continue;
1251             }
1252 
1253             try {
1254                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1255                         transformation,
1256                         KeyProperties.PURPOSE_ENCRYPT);
1257                 assertInitEncryptSucceeds(transformation, good);
1258                 assertInitEncryptSucceeds(transformation,
1259                         TestUtils.buildUpon(good, 0).build());
1260             } catch (Throwable e) {
1261                 throw new RuntimeException("Failed for " + transformation, e);
1262             }
1263         }
1264     }
1265 
1266     @Test
testInitDecryptFailsWhenBlockModeNotAuthorized()1267     public void testInitDecryptFailsWhenBlockModeNotAuthorized() throws Exception {
1268         for (String transformation : EXPECTED_ALGORITHMS) {
1269             if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(
1270                     TestUtils.getCipherKeyAlgorithm(transformation))) {
1271                 // Block modes do not apply
1272                 continue;
1273             }
1274 
1275             String goodBlockMode = TestUtils.getCipherBlockMode(transformation);
1276             String badBlockMode = KeyProperties.BLOCK_MODE_CBC.equalsIgnoreCase(goodBlockMode)
1277                     ? KeyProperties.BLOCK_MODE_CTR : KeyProperties.BLOCK_MODE_CBC;
1278 
1279             try {
1280                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1281                         transformation,
1282                         KeyProperties.PURPOSE_DECRYPT);
1283                 assertInitDecryptSucceeds(transformation, good);
1284                 assertInitDecryptThrowsInvalidKeyException(transformation,
1285                         TestUtils.buildUpon(good).setBlockModes(badBlockMode).build());
1286             } catch (Throwable e) {
1287                 throw new RuntimeException(
1288                         "Failed for " + transformation + " when authorized only for "
1289                                 + badBlockMode,
1290                         e);
1291             }
1292         }
1293     }
1294 
1295     @Test
testInitEncryptSymmetricFailsWhenBlockModeNotAuthorized()1296     public void testInitEncryptSymmetricFailsWhenBlockModeNotAuthorized() throws Exception {
1297         for (String transformation : EXPECTED_ALGORITHMS) {
1298             if (!isSymmetric(transformation)) {
1299                 continue;
1300             }
1301 
1302             if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(
1303                     TestUtils.getCipherKeyAlgorithm(transformation))) {
1304                 // Block modes do not apply
1305                 continue;
1306             }
1307 
1308             String goodBlockMode = TestUtils.getCipherBlockMode(transformation);
1309             String badBlockMode = KeyProperties.BLOCK_MODE_CBC.equalsIgnoreCase(goodBlockMode)
1310                     ? KeyProperties.BLOCK_MODE_CTR : KeyProperties.BLOCK_MODE_CBC;
1311 
1312             try {
1313                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1314                         transformation,
1315                         KeyProperties.PURPOSE_ENCRYPT);
1316 
1317                 assertInitEncryptSucceeds(transformation, good);
1318                 assertInitEncryptThrowsInvalidKeyException(transformation,
1319                         TestUtils.buildUpon(good).setBlockModes(badBlockMode).build());
1320             } catch (Throwable e) {
1321                 throw new RuntimeException(
1322                         "Failed for " + transformation + " when authorized only for "
1323                                 + badBlockMode,
1324                         e);
1325             }
1326         }
1327     }
1328 
1329     @Test
testInitEncryptAsymmetricIgnoresAuthorizedBlockModes()1330     public void testInitEncryptAsymmetricIgnoresAuthorizedBlockModes() throws Exception {
1331         for (String transformation : EXPECTED_ALGORITHMS) {
1332             if (isSymmetric(transformation)) {
1333                 continue;
1334             }
1335 
1336             try {
1337                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1338                         transformation,
1339                         KeyProperties.PURPOSE_ENCRYPT);
1340 
1341                 assertInitEncryptSucceeds(transformation, good);
1342                 assertInitEncryptSucceeds(transformation,
1343                         TestUtils.buildUpon(good).setBlockModes().build());
1344             } catch (Throwable e) {
1345                 throw new RuntimeException("Failed for " + transformation, e);
1346             }
1347         }
1348     }
1349 
1350     @Test
testInitDecryptFailsWhenDigestNotAuthorized()1351     public void testInitDecryptFailsWhenDigestNotAuthorized() throws Exception {
1352         for (String transformation : EXPECTED_ALGORITHMS) {
1353             String impliedDigest = TestUtils.getCipherDigest(transformation);
1354             if (impliedDigest == null) {
1355                 // No digest used by this transformation
1356                 continue;
1357             }
1358 
1359             String badDigest = KeyProperties.DIGEST_SHA256.equalsIgnoreCase(impliedDigest)
1360                     ? KeyProperties.DIGEST_SHA512 : KeyProperties.DIGEST_SHA256;
1361             try {
1362                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1363                         transformation,
1364                         KeyProperties.PURPOSE_DECRYPT);
1365 
1366                 assertInitDecryptSucceeds(transformation, good);
1367                 assertInitDecryptThrowsInvalidKeyException(transformation,
1368                         TestUtils.buildUpon(good).setDigests(badDigest).build());
1369 
1370                 if (!KeyProperties.DIGEST_NONE.equalsIgnoreCase(impliedDigest)) {
1371                     // Check that authorized digest NONE does not mean ANY digest is authorized.
1372                     badDigest = KeyProperties.DIGEST_NONE;
1373                     assertInitDecryptThrowsInvalidKeyException(transformation,
1374                             TestUtils.buildUpon(good).setDigests(badDigest).build());
1375                 }
1376             } catch (Throwable e) {
1377                 throw new RuntimeException(
1378                         "Failed for " + transformation + " when authorized only for " + badDigest,
1379                         e);
1380             }
1381         }
1382     }
1383 
1384     @Test
testInitEncryptSymmetricFailsWhenDigestNotAuthorized()1385     public void testInitEncryptSymmetricFailsWhenDigestNotAuthorized() throws Exception {
1386         for (String transformation : EXPECTED_ALGORITHMS) {
1387             if (!isSymmetric(transformation)) {
1388                 continue;
1389             }
1390 
1391             String impliedDigest = TestUtils.getCipherDigest(transformation);
1392             if (impliedDigest == null) {
1393                 // No digest used by this transformation
1394                 continue;
1395             }
1396 
1397             String badDigest = KeyProperties.DIGEST_SHA256.equalsIgnoreCase(impliedDigest)
1398                     ? KeyProperties.DIGEST_SHA512 : KeyProperties.DIGEST_SHA256;
1399 
1400             try {
1401                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1402                         transformation,
1403                         KeyProperties.PURPOSE_ENCRYPT);
1404                 assertInitEncryptSucceeds(transformation, good);
1405                 assertInitEncryptThrowsInvalidKeyException(transformation,
1406                         TestUtils.buildUpon(good).setDigests(badDigest).build());
1407 
1408                 if (!KeyProperties.DIGEST_NONE.equalsIgnoreCase(impliedDigest)) {
1409                     // Check that authorized digest NONE does not mean ANY digest is authorized.
1410                     badDigest = KeyProperties.DIGEST_NONE;
1411                     assertInitEncryptThrowsInvalidKeyException(transformation,
1412                             TestUtils.buildUpon(good).setDigests(badDigest).build());
1413                 }
1414             } catch (Throwable e) {
1415                 throw new RuntimeException(
1416                         "Failed for " + transformation + " when authorized only for " + badDigest,
1417                         e);
1418             }
1419         }
1420     }
1421 
1422     @Test
testInitEncryptAsymmetricIgnoresAuthorizedDigests()1423     public void testInitEncryptAsymmetricIgnoresAuthorizedDigests() throws Exception {
1424         for (String transformation : EXPECTED_ALGORITHMS) {
1425             if (isSymmetric(transformation)) {
1426                 continue;
1427             }
1428             try {
1429                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1430                         transformation,
1431                         KeyProperties.PURPOSE_ENCRYPT);
1432                 assertInitEncryptSucceeds(transformation, good);
1433                 assertInitEncryptSucceeds(transformation,
1434                         TestUtils.buildUpon(good).setDigests().build());
1435             } catch (Throwable e) {
1436                 throw new RuntimeException("Failed for " + transformation, e);
1437             }
1438         }
1439     }
1440 
1441     @Test
testInitDecryptFailsWhenPaddingSchemeNotAuthorized()1442     public void testInitDecryptFailsWhenPaddingSchemeNotAuthorized() throws Exception {
1443         for (String transformation : EXPECTED_ALGORITHMS) {
1444             String impliedEncryptionPadding = TestUtils.getCipherEncryptionPadding(transformation);
1445             String badEncryptionPadding;
1446             if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(
1447                     TestUtils.getCipherKeyAlgorithm(transformation))) {
1448                 badEncryptionPadding =
1449                         KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(
1450                                 impliedEncryptionPadding)
1451                         ? KeyProperties.ENCRYPTION_PADDING_RSA_OAEP
1452                         : KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1;
1453             } else {
1454                 badEncryptionPadding = KeyProperties.ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(
1455                         impliedEncryptionPadding)
1456                         ? KeyProperties.ENCRYPTION_PADDING_NONE
1457                         : KeyProperties.ENCRYPTION_PADDING_PKCS7;
1458             }
1459             try {
1460                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1461                         transformation,
1462                         KeyProperties.PURPOSE_DECRYPT);
1463 
1464                 assertInitDecryptSucceeds(transformation, good);
1465                 assertInitDecryptThrowsInvalidKeyException(transformation,
1466                         TestUtils.buildUpon(good)
1467                                 .setEncryptionPaddings(badEncryptionPadding)
1468                                 .build());
1469 
1470                 if (!KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(
1471                         impliedEncryptionPadding)) {
1472                     // Check that authorized padding NONE does not mean ANY padding is authorized.
1473                     badEncryptionPadding = KeyProperties.ENCRYPTION_PADDING_NONE;
1474                     assertInitDecryptThrowsInvalidKeyException(transformation,
1475                             TestUtils.buildUpon(good)
1476                                     .setEncryptionPaddings(badEncryptionPadding)
1477                                     .build());
1478                 }
1479             } catch (Throwable e) {
1480                 throw new RuntimeException(
1481                         "Failed for " + transformation + " when authorized only for "
1482                                 + badEncryptionPadding,
1483                         e);
1484             }
1485         }
1486     }
1487 
1488     @Test
testInitEncryptSymmetricFailsWhenPaddingSchemeNotAuthorized()1489     public void testInitEncryptSymmetricFailsWhenPaddingSchemeNotAuthorized() throws Exception {
1490         for (String transformation : EXPECTED_ALGORITHMS) {
1491             if (!isSymmetric(transformation)) {
1492                 continue;
1493             }
1494             String impliedEncryptionPadding = TestUtils.getCipherEncryptionPadding(transformation);
1495             String badEncryptionPadding = KeyProperties.ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(
1496                     impliedEncryptionPadding)
1497                     ? KeyProperties.ENCRYPTION_PADDING_NONE
1498                     : KeyProperties.ENCRYPTION_PADDING_PKCS7;
1499             try {
1500                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1501                         transformation,
1502                         KeyProperties.PURPOSE_ENCRYPT);
1503 
1504                 assertInitEncryptSucceeds(transformation, good);
1505                 assertInitEncryptThrowsInvalidKeyException(transformation,
1506                         TestUtils.buildUpon(good)
1507                                 .setEncryptionPaddings(badEncryptionPadding)
1508                                 .build());
1509 
1510                 if (!KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(
1511                         impliedEncryptionPadding)) {
1512                     // Check that authorized padding NONE does not mean ANY padding is authorized.
1513                     badEncryptionPadding = KeyProperties.ENCRYPTION_PADDING_NONE;
1514                     assertInitEncryptThrowsInvalidKeyException(transformation,
1515                             TestUtils.buildUpon(good)
1516                                     .setEncryptionPaddings(badEncryptionPadding)
1517                                     .build());
1518                 }
1519             } catch (Throwable e) {
1520                 throw new RuntimeException(
1521                         "Failed for " + transformation + " when authorized only for "
1522                                 + badEncryptionPadding,
1523                         e);
1524             }
1525         }
1526     }
1527 
1528     @Test
testInitEncryptAsymmetricIgnoresAuthorizedPaddingSchemes()1529     public void testInitEncryptAsymmetricIgnoresAuthorizedPaddingSchemes() throws Exception {
1530         for (String transformation : EXPECTED_ALGORITHMS) {
1531             if (isSymmetric(transformation)) {
1532                 continue;
1533             }
1534             try {
1535                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1536                         transformation,
1537                         KeyProperties.PURPOSE_ENCRYPT);
1538 
1539                 assertInitEncryptSucceeds(transformation, good);
1540                 assertInitEncryptSucceeds(transformation,
1541                         TestUtils.buildUpon(good)
1542                                 .setEncryptionPaddings()
1543                                 .setSignaturePaddings()
1544                                 .build());
1545             } catch (Throwable e) {
1546                 throw new RuntimeException("Failed for " + transformation, e);
1547             }
1548         }
1549     }
1550 
1551     @Test
testInitDecryptFailsWhenKeyNotYetValid()1552     public void testInitDecryptFailsWhenKeyNotYetValid() throws Exception {
1553         Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
1554         for (String transformation : EXPECTED_ALGORITHMS) {
1555             try {
1556                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1557                         transformation,
1558                         KeyProperties.PURPOSE_DECRYPT);
1559 
1560                 assertInitDecryptSucceeds(transformation, good);
1561                 assertInitDecryptThrowsInvalidKeyException(transformation,
1562                         TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
1563             } catch (Throwable e) {
1564                 throw new RuntimeException("Failed for " + transformation, e);
1565             }
1566         }
1567     }
1568 
1569     @Test
testInitEncryptSymmetricFailsWhenKeyNotYetValid()1570     public void testInitEncryptSymmetricFailsWhenKeyNotYetValid() throws Exception {
1571         Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
1572         for (String transformation : EXPECTED_ALGORITHMS) {
1573             if (!isSymmetric(transformation)) {
1574                 continue;
1575             }
1576             try {
1577                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1578                         transformation,
1579                         KeyProperties.PURPOSE_ENCRYPT);
1580 
1581                 assertInitEncryptSucceeds(transformation, good);
1582                 assertInitEncryptThrowsInvalidKeyException(transformation,
1583                         TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
1584             } catch (Throwable e) {
1585                 throw new RuntimeException("Failed for " + transformation, e);
1586             }
1587         }
1588     }
1589 
1590     @Test
testInitEncryptAsymmetricIgnoresThatKeyNotYetValid()1591     public void testInitEncryptAsymmetricIgnoresThatKeyNotYetValid() throws Exception {
1592         Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
1593         for (String transformation : EXPECTED_ALGORITHMS) {
1594             if (isSymmetric(transformation)) {
1595                 continue;
1596             }
1597             try {
1598                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1599                         transformation,
1600                         KeyProperties.PURPOSE_ENCRYPT);
1601 
1602                 assertInitEncryptSucceeds(transformation, good);
1603                 assertInitEncryptSucceeds(transformation,
1604                         TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
1605             } catch (Throwable e) {
1606                 throw new RuntimeException("Failed for " + transformation, e);
1607             }
1608         }
1609     }
1610 
1611     @Test
testInitDecryptFailsWhenKeyNoLongerValidForConsumption()1612     public void testInitDecryptFailsWhenKeyNoLongerValidForConsumption() throws Exception {
1613         Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
1614         for (String transformation : EXPECTED_ALGORITHMS) {
1615             try {
1616                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1617                         transformation,
1618                         KeyProperties.PURPOSE_DECRYPT);
1619 
1620                 assertInitDecryptSucceeds(transformation, good);
1621                 assertInitDecryptThrowsInvalidKeyException(transformation,
1622                         TestUtils.buildUpon(good)
1623                                 .setKeyValidityForConsumptionEnd(badEndDate)
1624                                 .build());
1625             } catch (Throwable e) {
1626                 throw new RuntimeException("Failed for " + transformation, e);
1627             }
1628         }
1629     }
1630 
1631     @Test
testInitDecryptIgnoresThatKeyNoLongerValidForOrigination()1632     public void testInitDecryptIgnoresThatKeyNoLongerValidForOrigination() throws Exception {
1633         Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
1634         for (String transformation : EXPECTED_ALGORITHMS) {
1635             try {
1636                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1637                         transformation,
1638                         KeyProperties.PURPOSE_DECRYPT);
1639 
1640                 assertInitDecryptSucceeds(transformation, good);
1641                 assertInitDecryptSucceeds(transformation,
1642                         TestUtils.buildUpon(good)
1643                                 .setKeyValidityForOriginationEnd(badEndDate)
1644                                 .build());
1645             } catch (Throwable e) {
1646                 throw new RuntimeException("Failed for " + transformation, e);
1647             }
1648         }
1649     }
1650 
1651     @Test
testInitEncryptSymmetricFailsWhenKeyNoLongerValidForOrigination()1652     public void testInitEncryptSymmetricFailsWhenKeyNoLongerValidForOrigination() throws Exception {
1653         Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
1654         for (String transformation : EXPECTED_ALGORITHMS) {
1655             if (!isSymmetric(transformation)) {
1656                 continue;
1657             }
1658             try {
1659                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1660                         transformation,
1661                         KeyProperties.PURPOSE_ENCRYPT);
1662 
1663                 assertInitEncryptSucceeds(transformation, good);
1664                 assertInitEncryptThrowsInvalidKeyException(transformation,
1665                         TestUtils.buildUpon(good)
1666                                 .setKeyValidityForOriginationEnd(badEndDate)
1667                                 .build());
1668             } catch (Throwable e) {
1669                 throw new RuntimeException("Failed for " + transformation, e);
1670             }
1671         }
1672     }
1673 
1674     @Test
testInitEncryptSymmetricIgnoresThatKeyNoLongerValidForConsumption()1675     public void testInitEncryptSymmetricIgnoresThatKeyNoLongerValidForConsumption()
1676             throws Exception {
1677         Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
1678         for (String transformation : EXPECTED_ALGORITHMS) {
1679             if (!isSymmetric(transformation)) {
1680                 continue;
1681             }
1682             try {
1683                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1684                         transformation,
1685                         KeyProperties.PURPOSE_ENCRYPT);
1686 
1687                 assertInitEncryptSucceeds(transformation, good);
1688                 assertInitEncryptSucceeds(transformation,
1689                         TestUtils.buildUpon(good)
1690                                 .setKeyValidityForConsumptionEnd(badEndDate)
1691                                 .build());
1692             } catch (Throwable e) {
1693                 throw new RuntimeException("Failed for " + transformation, e);
1694             }
1695         }
1696     }
1697 
1698     @Test
testInitEncryptAsymmetricIgnoresThatKeyNoLongerValid()1699     public void testInitEncryptAsymmetricIgnoresThatKeyNoLongerValid() throws Exception {
1700         Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
1701         for (String transformation : EXPECTED_ALGORITHMS) {
1702             if (isSymmetric(transformation)) {
1703                 continue;
1704             }
1705             try {
1706                 KeyProtection good = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1707                         transformation,
1708                         KeyProperties.PURPOSE_ENCRYPT);
1709 
1710                 assertInitEncryptSucceeds(transformation, good);
1711                 assertInitEncryptSucceeds(transformation,
1712                         TestUtils.buildUpon(good)
1713                                 .setKeyValidityForOriginationEnd(badEndDate)
1714                                 .build());
1715                 assertInitEncryptSucceeds(transformation,
1716                         TestUtils.buildUpon(good)
1717                                 .setKeyValidityForConsumptionEnd(badEndDate)
1718                                 .build());
1719             } catch (Throwable e) {
1720                 throw new RuntimeException("Failed for " + transformation, e);
1721             }
1722         }
1723     }
1724 
getWorkingDecryptionParameterSpec(String transformation)1725     private AlgorithmParameterSpec getWorkingDecryptionParameterSpec(String transformation) {
1726         String keyAlgorithm = TestUtils.getCipherKeyAlgorithm(transformation);
1727         if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
1728             return null;
1729         } else if (KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(keyAlgorithm)) {
1730             String blockMode = TestUtils.getCipherBlockMode(transformation);
1731             if (KeyProperties.BLOCK_MODE_ECB.equalsIgnoreCase(blockMode)) {
1732                 return null;
1733             } else if ((KeyProperties.BLOCK_MODE_CBC.equalsIgnoreCase(blockMode))
1734                     || (KeyProperties.BLOCK_MODE_CTR.equalsIgnoreCase(blockMode))) {
1735                 return new IvParameterSpec(
1736                         new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16});
1737             } else if (KeyProperties.BLOCK_MODE_GCM.equalsIgnoreCase(blockMode)) {
1738                 return new GCMParameterSpec(
1739                         128,
1740                         new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
1741             } else {
1742                 throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
1743             }
1744         } else if (KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(keyAlgorithm)) {
1745             String blockMode = TestUtils.getCipherBlockMode(transformation);
1746             if (KeyProperties.BLOCK_MODE_ECB.equalsIgnoreCase(blockMode)) {
1747                 return null;
1748             } else if (KeyProperties.BLOCK_MODE_CBC.equalsIgnoreCase(blockMode)) {
1749                 return new IvParameterSpec(
1750                         new byte[]{1, 2, 3, 4, 5, 6, 7, 8});
1751             } else {
1752                 throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
1753             }
1754         } else {
1755             throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
1756         }
1757     }
1758 
assertInitDecryptSucceeds(String transformation, KeyProtection importParams)1759     private void assertInitDecryptSucceeds(String transformation, KeyProtection importParams)
1760             throws Exception {
1761         Cipher cipher = Cipher.getInstance(transformation, EXPECTED_PROVIDER_NAME);
1762         Key key =
1763                 importDefaultKatKey(transformation, importParams).getKeystoreBackedDecryptionKey();
1764         AlgorithmParameterSpec params = getWorkingDecryptionParameterSpec(transformation);
1765         cipher.init(Cipher.DECRYPT_MODE, key, params);
1766     }
1767 
assertInitDecryptThrowsInvalidKeyException( String transformation, KeyProtection importParams)1768     private void assertInitDecryptThrowsInvalidKeyException(
1769             String transformation, KeyProtection importParams) throws Exception {
1770         Cipher cipher = Cipher.getInstance(transformation, EXPECTED_PROVIDER_NAME);
1771         Key key =
1772                 importDefaultKatKey(transformation, importParams).getKeystoreBackedDecryptionKey();
1773         AlgorithmParameterSpec params = getWorkingDecryptionParameterSpec(transformation);
1774         try {
1775             cipher.init(Cipher.DECRYPT_MODE, key, params);
1776             fail("InvalidKeyException should have been thrown");
1777         } catch (InvalidKeyException | RuntimeException expected) {}
1778     }
1779 
assertInitEncryptSucceeds(String transformation, KeyProtection importParams)1780     private void assertInitEncryptSucceeds(String transformation, KeyProtection importParams)
1781             throws Exception {
1782         Cipher cipher = Cipher.getInstance(transformation, EXPECTED_PROVIDER_NAME);
1783         Key key =
1784                 importDefaultKatKey(transformation, importParams).getKeystoreBackedEncryptionKey();
1785         cipher.init(Cipher.ENCRYPT_MODE, key);
1786     }
1787 
assertInitEncryptThrowsInvalidKeyException( String transformation, KeyProtection importParams)1788     private void assertInitEncryptThrowsInvalidKeyException(
1789             String transformation, KeyProtection importParams) throws Exception {
1790         Cipher cipher = Cipher.getInstance(transformation, EXPECTED_PROVIDER_NAME);
1791         Key key =
1792                 importDefaultKatKey(transformation, importParams).getKeystoreBackedEncryptionKey();
1793         try {
1794             cipher.init(Cipher.ENCRYPT_MODE, key);
1795             fail("InvalidKeyException should have been thrown");
1796         } catch (InvalidKeyException expected) {}
1797     }
1798 
importDefaultKatKey( String transformation, KeyProtection importParams)1799     private ImportedKey importDefaultKatKey(
1800             String transformation, KeyProtection importParams)
1801             throws Exception {
1802         String keyAlgorithm = TestUtils.getCipherKeyAlgorithm(transformation);
1803         if (KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(keyAlgorithm)) {
1804             return TestUtils.importIntoAndroidKeyStore(
1805                     "testAES",
1806                     new SecretKeySpec(AES128_KAT_KEY_BYTES, "AES"),
1807                     importParams);
1808         } else if (KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(keyAlgorithm)) {
1809             return TestUtils.importIntoAndroidKeyStore(
1810                     "test3DES",
1811                     new SecretKeySpec(DESede_KAT_KEY_BYTES, "DESede"),
1812                     importParams);
1813         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
1814             return TestUtils.importIntoAndroidKeyStore(
1815                     "testRSA",
1816                     getContext(),
1817                     R.raw.rsa_key2_pkcs8,
1818                     R.raw.rsa_key2_cert,
1819                     importParams);
1820         } else {
1821             throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
1822         }
1823     }
1824 
importDefaultKatKey( String transformation, int purposes, boolean ivProvidedWhenEncrypting)1825     private ImportedKey importDefaultKatKey(
1826             String transformation, int purposes, boolean ivProvidedWhenEncrypting)
1827             throws Exception {
1828         KeyProtection importParams = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1829                 transformation, purposes, ivProvidedWhenEncrypting);
1830         return importDefaultKatKey(transformation, importParams);
1831     }
1832 
importKatKeys( String transformation, int purposes, boolean ivProvidedWhenEncrypting)1833     private Collection<ImportedKey> importKatKeys(
1834             String transformation, int purposes, boolean ivProvidedWhenEncrypting)
1835             throws Exception {
1836       return importKatKeys(transformation, purposes, ivProvidedWhenEncrypting, false, false);
1837     }
1838 
importKatKeys( String transformation, int purposes, boolean ivProvidedWhenEncrypting, boolean isUnlockedDeviceRequired, boolean isUserAuthRequired)1839     private Collection<ImportedKey> importKatKeys(
1840             String transformation, int purposes, boolean ivProvidedWhenEncrypting,
1841             boolean isUnlockedDeviceRequired, boolean isUserAuthRequired) throws Exception {
1842         KeyProtection importParams = TestUtils.getMinimalWorkingImportParametersForCipheringWith(
1843             transformation, purposes, ivProvidedWhenEncrypting, isUnlockedDeviceRequired,
1844             isUserAuthRequired);
1845         String keyAlgorithm = TestUtils.getCipherKeyAlgorithm(transformation);
1846         if (KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(keyAlgorithm)) {
1847             return Arrays.asList(
1848                     TestUtils.importIntoAndroidKeyStore(
1849                             "testAES128",
1850                             new SecretKeySpec(AES128_KAT_KEY_BYTES, "AES"),
1851                             importParams),
1852                     TestUtils.importIntoAndroidKeyStore(
1853                             "testAES192",
1854                             new SecretKeySpec(AES192_KAT_KEY_BYTES, "AES"),
1855                             importParams),
1856                     TestUtils.importIntoAndroidKeyStore(
1857                             "testAES256",
1858                             new SecretKeySpec(AES256_KAT_KEY_BYTES, "AES"),
1859                             importParams)
1860             );
1861         } else if (KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(keyAlgorithm)) {
1862             return Arrays.asList(TestUtils.importIntoAndroidKeyStore(
1863                     "test3DES",
1864                     new SecretKeySpec(DESede_KAT_KEY_BYTES, "DESede"),
1865                     importParams)
1866             );
1867         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
1868             return RSASignatureTest.importKatKeyPairs(getContext(), importParams);
1869         } else {
1870             throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
1871         }
1872     }
1873 
isSymmetric(String transformation)1874     private static boolean isSymmetric(String transformation) {
1875         return TestUtils.isCipherSymmetric(transformation);
1876     }
1877 
truncatePlaintextIfNecessary( String transformation, Key encryptionKey, byte[] plaintext)1878     private static byte[] truncatePlaintextIfNecessary(
1879             String transformation, Key encryptionKey, byte[] plaintext) {
1880         int maxSupportedPlaintextSizeBytes =
1881                 TestUtils.getMaxSupportedPlaintextInputSizeBytes(
1882                         transformation, encryptionKey);
1883         if (plaintext.length <= maxSupportedPlaintextSizeBytes) {
1884             // No need to truncate
1885             return plaintext;
1886         } else if (maxSupportedPlaintextSizeBytes < 0) {
1887             // Key too short to encrypt anything at all using this transformation
1888             return null;
1889         } else {
1890             // Truncate plaintext to exercise this transformation with this key
1891             return Arrays.copyOf(plaintext, maxSupportedPlaintextSizeBytes);
1892         }
1893     }
1894 }
1895