Home | History | Annotate | Download | only in jca
      1 /*
      2  * Copyright (C) 2017 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 libcore.sun.security.jca;
     18 
     19 import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider;
     20 import org.junit.Test;
     21 import org.junit.runner.RunWith;
     22 import org.junit.runners.JUnit4;
     23 
     24 import dalvik.system.VMRuntime;
     25 
     26 import java.lang.reflect.Method;
     27 import java.security.AlgorithmParameters;
     28 import java.security.GeneralSecurityException;
     29 import java.security.KeyFactory;
     30 import java.security.MessageDigest;
     31 import java.security.NoSuchAlgorithmException;
     32 import java.security.Provider;
     33 import java.security.Security;
     34 import java.security.Signature;
     35 import java.util.ArrayList;
     36 import java.util.List;
     37 import javax.crypto.KeyGenerator;
     38 import javax.crypto.Mac;
     39 
     40 import sun.security.jca.Providers;
     41 
     42 import static org.junit.Assert.assertEquals;
     43 import static org.junit.Assert.assertNotNull;
     44 import static org.junit.Assert.fail;
     45 
     46 /**
     47  * Tests that the deprecation of algorithms from the BC provider works as expected.  Requests
     48  * from an application targeting an API level before the deprecation should receive them,
     49  * but those targeting an API level after the deprecation should cause an exception.  Tests
     50  * a representative sample of services and algorithms and various ways of naming them.
     51  */
     52 @RunWith(JUnit4.class)
     53 public class ProvidersTest {
     54 
     55     /**
     56      * An object that can be called to call an appropriate getInstance method.  Since
     57      * each type of object has its own class that the method should be called on,
     58      * it's either this or reflection, and this seems more straightforward.
     59      */
     60     private interface Algorithm {
     61         Object getInstance() throws GeneralSecurityException;
     62     }
     63 
     64     // getInstance calls that result in requests to BC
     65     private static final List<Algorithm> BC_ALGORITHMS = new ArrayList<>();
     66     // getInstance calls that result in requests to Conscrypt
     67     private static final List<Algorithm> CONSCRYPT_ALGORITHMS = new ArrayList<>();
     68     static {
     69         // A concrete algorithm, provider by name
     70         BC_ALGORITHMS.add(new Algorithm() {
     71             @Override
     72             public Object getInstance() throws GeneralSecurityException {
     73                 return Signature.getInstance("sha224withrsa", "BC");
     74             }
     75         });
     76         // A concrete algorithm, provider by instance
     77         BC_ALGORITHMS.add(new Algorithm() {
     78             @Override
     79             public Object getInstance() throws GeneralSecurityException {
     80                 return KeyFactory.getInstance("EC", Security.getProvider("BC"));
     81             }
     82         });
     83         // An alias for another algorithm, provider by name
     84         BC_ALGORITHMS.add(new Algorithm() {
     85             @Override
     86             public Object getInstance() throws GeneralSecurityException {
     87                 return Signature.getInstance("MD5withRSAEncryption", "BC");
     88             }
     89         });
     90         // An alias for another algorithm, provider by instance
     91         BC_ALGORITHMS.add(new Algorithm() {
     92             @Override
     93             public Object getInstance() throws GeneralSecurityException {
     94                 return KeyGenerator.getInstance("HMAC-MD5", Security.getProvider("BC"));
     95             }
     96         });
     97         // An alias with unusual characters, provider by name
     98         BC_ALGORITHMS.add(new Algorithm() {
     99             @Override
    100             public Object getInstance() throws GeneralSecurityException {
    101                 return Mac.getInstance("Hmac/sha256", "BC");
    102             }
    103         });
    104         // An alias with unusual characters, provider by instance
    105         BC_ALGORITHMS.add(new Algorithm() {
    106             @Override
    107             public Object getInstance() throws GeneralSecurityException {
    108                 return Signature.getInstance("SHA384/rsA", Security.getProvider("BC"));
    109             }
    110         });
    111         // An alias by OID, provider by name
    112         BC_ALGORITHMS.add(new Algorithm() {
    113             @Override
    114             public Object getInstance() throws GeneralSecurityException {
    115                 // OID for SHA-256
    116                 return MessageDigest.getInstance("2.16.840.1.101.3.4.2.1", "BC");
    117             }
    118         });
    119         // An alias by OID, provider by instance
    120         BC_ALGORITHMS.add(new Algorithm() {
    121             @Override
    122             public Object getInstance() throws GeneralSecurityException {
    123                 // OID for AES-128
    124                 return AlgorithmParameters.getInstance("2.16.840.1.101.3.4.1.2",
    125                         Security.getProvider("BC"));
    126             }
    127         });
    128         // All the same algorithms as for BC, but with no provider, which should produce
    129         // the Conscrypt implementation
    130         CONSCRYPT_ALGORITHMS.add(new Algorithm() {
    131             @Override
    132             public Object getInstance() throws GeneralSecurityException {
    133                 return Signature.getInstance("sha224withrsa");
    134             }
    135         });
    136         CONSCRYPT_ALGORITHMS.add(new Algorithm() {
    137             @Override
    138             public Object getInstance() throws GeneralSecurityException {
    139                 return KeyFactory.getInstance("EC");
    140             }
    141         });
    142         CONSCRYPT_ALGORITHMS.add(new Algorithm() {
    143             @Override
    144             public Object getInstance() throws GeneralSecurityException {
    145                 return Signature.getInstance("MD5withRSAEncryption");
    146             }
    147         });
    148         CONSCRYPT_ALGORITHMS.add(new Algorithm() {
    149             @Override
    150             public Object getInstance() throws GeneralSecurityException {
    151                 return KeyGenerator.getInstance("HMAC-MD5");
    152             }
    153         });
    154         CONSCRYPT_ALGORITHMS.add(new Algorithm() {
    155             @Override
    156             public Object getInstance() throws GeneralSecurityException {
    157                 return Mac.getInstance("Hmac/sha256");
    158             }
    159         });
    160         CONSCRYPT_ALGORITHMS.add(new Algorithm() {
    161             @Override
    162             public Object getInstance() throws GeneralSecurityException {
    163                 return Signature.getInstance("SHA384/rsA");
    164             }
    165         });
    166         CONSCRYPT_ALGORITHMS.add(new Algorithm() {
    167             @Override
    168             public Object getInstance() throws GeneralSecurityException {
    169                 // OID for SHA-256
    170                 return MessageDigest.getInstance("2.16.840.1.101.3.4.2.1");
    171             }
    172         });
    173         CONSCRYPT_ALGORITHMS.add(new Algorithm() {
    174             @Override
    175             public Object getInstance() throws GeneralSecurityException {
    176                 // OID for AES-128
    177                 return AlgorithmParameters.getInstance("2.16.840.1.101.3.4.1.2");
    178             }
    179         });
    180     }
    181 
    182     private static Provider getProvider(Object object) throws Exception {
    183         // Every JCA object has a getProvider() method
    184         Method m = object.getClass().getMethod("getProvider");
    185         return (Provider) m.invoke(object);
    186     }
    187 
    188     @Test
    189     public void testBeforeLimit() throws Exception {
    190         // When we're before the limit of the target API, all calls should succeed
    191         try {
    192             Providers.setMaximumAllowableApiLevelForBcDeprecation(
    193                     VMRuntime.getRuntime().getTargetSdkVersion() + 1);
    194             for (Algorithm a : BC_ALGORITHMS) {
    195                 Object result = a.getInstance();
    196                 assertEquals("BC", getProvider(result).getName());
    197             }
    198             for (Algorithm a : CONSCRYPT_ALGORITHMS) {
    199                 Object result = a.getInstance();
    200                 assertEquals("AndroidOpenSSL", getProvider(result).getName());
    201             }
    202         } finally {
    203             Providers.setMaximumAllowableApiLevelForBcDeprecation(
    204                     Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
    205         }
    206     }
    207 
    208     @Test
    209     public void testAtLimit() throws Exception {
    210         // When we're at the limit of the target API, all calls should still succeed
    211         try {
    212             Providers.setMaximumAllowableApiLevelForBcDeprecation(
    213                     VMRuntime.getRuntime().getTargetSdkVersion());
    214             for (Algorithm a : BC_ALGORITHMS) {
    215                 Object result = a.getInstance();
    216                 assertEquals("BC", getProvider(result).getName());
    217             }
    218             for (Algorithm a : CONSCRYPT_ALGORITHMS) {
    219                 Object result = a.getInstance();
    220                 assertEquals("AndroidOpenSSL", getProvider(result).getName());
    221             }
    222         } finally {
    223             Providers.setMaximumAllowableApiLevelForBcDeprecation(
    224                     Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
    225         }
    226     }
    227 
    228     @Test
    229     public void testPastLimit() throws Exception {
    230         // When we're beyond the limit of the target API, the Conscrypt calls should succeed
    231         // but the BC calls should throw NoSuchAlgorithmException
    232         try {
    233             Providers.setMaximumAllowableApiLevelForBcDeprecation(
    234                     VMRuntime.getRuntime().getTargetSdkVersion() - 1);
    235             for (Algorithm a : BC_ALGORITHMS) {
    236                 try {
    237                     a.getInstance();
    238                     fail("getInstance should have thrown");
    239                 } catch (NoSuchAlgorithmException expected) {
    240                 }
    241             }
    242             for (Algorithm a : CONSCRYPT_ALGORITHMS) {
    243                 Object result = a.getInstance();
    244                 assertEquals("AndroidOpenSSL", getProvider(result).getName());
    245             }
    246         } finally {
    247             Providers.setMaximumAllowableApiLevelForBcDeprecation(
    248                     Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
    249         }
    250     }
    251 
    252     @Test
    253     public void testCustomProvider() throws Exception {
    254         // When we install our own separate instance of Bouncy Castle, the system should
    255         // respect that and allow us to use its implementation.
    256         Provider originalBouncyCastle = null;
    257         int originalBouncyCastleIndex = -1;
    258         for (int i = 0; i < Security.getProviders().length; i++) {
    259             if (Security.getProviders()[i].getName().equals("BC")) {
    260                 originalBouncyCastle = Security.getProviders()[i];
    261                 originalBouncyCastleIndex = i;
    262                 break;
    263             }
    264         }
    265         assertNotNull(originalBouncyCastle);
    266         Provider newBouncyCastle = new BouncyCastleProvider();
    267         assertEquals("BC", newBouncyCastle.getName());
    268         try {
    269             // Remove the existing BC provider and replace it with a different one
    270             Security.removeProvider("BC");
    271             Security.insertProviderAt(newBouncyCastle, originalBouncyCastleIndex);
    272             // Set the target API limit such that the BC algorithms are disallowed
    273             Providers.setMaximumAllowableApiLevelForBcDeprecation(
    274                     VMRuntime.getRuntime().getTargetSdkVersion() - 1);
    275             for (Algorithm a : BC_ALGORITHMS) {
    276                 Object result = a.getInstance();
    277                 assertEquals("BC", getProvider(result).getName());
    278             }
    279             for (Algorithm a : CONSCRYPT_ALGORITHMS) {
    280                 Object result = a.getInstance();
    281                 assertEquals("AndroidOpenSSL", getProvider(result).getName());
    282             }
    283         } finally {
    284             Providers.setMaximumAllowableApiLevelForBcDeprecation(
    285                     Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
    286             Security.removeProvider("BC");
    287             Security.insertProviderAt(originalBouncyCastle, originalBouncyCastleIndex);
    288         }
    289     }
    290 }