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