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 }