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