Home | History | Annotate | Download | only in cts
      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