diff options
author | Victor Chang <vichang@google.com> | 2024-02-20 16:14:21 +0000 |
---|---|---|
committer | Victor Chang <vichang@google.com> | 2024-02-20 16:28:35 +0000 |
commit | 507e1943658c76ca1063be4f2e0ed65d8acbe94e (patch) | |
tree | 2e0a5c76071b99bea9a4dc9db909f4b8b8059de8 | |
parent | 67e6f8d249230b8ed43888e1561d70763bafb719 (diff) | |
download | libcore-507e1943658c76ca1063be4f2e0ed65d8acbe94e.tar.gz |
Update Provider to accept SecureRandomParameters for SecureRandom
Provider.Service.newInstance() needs to be updated to create
SecureRandomSpi with an optional (not mandatory)
SecureRandomParameters parameter.
Bug: 260847206
Fix: 323463952
Fix: 323464220
Fix: 323463883
Fix: 323464441
Fix: 323464638
Fix: 323464250
Test: atest CtsLibcoreTestCases:libcore.java.security.SecureRandomSpiTest
Change-Id: If8b76b7566e63795ceb4c641ee8b28e38d3f57d7
-rw-r--r-- | luni/src/test/java/libcore/java/security/SecureRandomSpiTest.java | 160 | ||||
-rw-r--r-- | ojluni/src/main/java/java/security/Provider.java | 142 |
2 files changed, 247 insertions, 55 deletions
diff --git a/luni/src/test/java/libcore/java/security/SecureRandomSpiTest.java b/luni/src/test/java/libcore/java/security/SecureRandomSpiTest.java new file mode 100644 index 00000000000..60df82c226a --- /dev/null +++ b/luni/src/test/java/libcore/java/security/SecureRandomSpiTest.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libcore.java.security; + +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.SecureRandomParameters; +import java.security.SecureRandomSpi; +import java.security.Security; + +@RunWith(JUnit4.class) +public class SecureRandomSpiTest { + + public static class MySecureRandomSpi extends SecureRandomSpi { + + public final SecureRandomParameters myParams; + + public MySecureRandomSpi() { + myParams = null; + } + + public MySecureRandomSpi(SecureRandomParameters params) { + super(params); + myParams = params; + } + + @Override + protected void engineSetSeed(byte[] seed) {} + + @Override + protected void engineNextBytes(byte[] bytes) {} + + @Override + protected byte[] engineGenerateSeed(int numBytes) { + return new byte[0]; + } + + public void engineReseed(SecureRandomParameters params) { + super.engineReseed(params); + } + public SecureRandomParameters engineGetParameters() { + return super.engineGetParameters(); + } + + @Override + public String toString() { + return myParams == null ? "null!" : myParams.toString(); + } + } + + + public static class MySecureRandomProvider extends Provider { + + public static final String NAME = "SecureRandomSpiTest.MySecureRandomProvider"; + + public static final String ALGORITHM = "MySecureRandomSpi"; + MySecureRandomProvider() { + super(NAME, 1.0, "Provider for SecureRandomSpi testing"); + putService(new Provider.Service(this, "SecureRandom", ALGORITHM, + MySecureRandomSpi.class.getName(), null, null)); + } + } + + private static final SecureRandomParameters MY_PARAMS = new SecureRandomParameters() { + @Override + public String toString() { + return "SecureRandomSpiTest.testGetInstance"; + } + }; + + private final MySecureRandomSpi mSpi = new MySecureRandomSpi(); + + @Before + public void setUp() { + Security.addProvider(new MySecureRandomProvider()); + } + + @After + public void teatDown() { + Security.removeProvider(MySecureRandomProvider.NAME); + } + + @Test + public void testEngineReseed() { + Assert.assertThrows(UnsupportedOperationException.class, () -> mSpi.engineReseed(null)); + } + + @Test + public void testEngineGetParameters() { + Assert.assertNull(mSpi.engineGetParameters()); + } + + @Test + public void testConstructor() { + SecureRandomParameters myParams = new SecureRandomParameters() {}; + MySecureRandomSpi mySpi = new MySecureRandomSpi(myParams); + Assert.assertNull(mySpi.engineGetParameters()); + } + + @Test + public void testGetInstanceWithProviderAndSecureRandomParameters() + throws NoSuchAlgorithmException { + Provider provider = Security.getProvider(MySecureRandomProvider.NAME); + SecureRandom secureRandom = SecureRandom.getInstance(MySecureRandomProvider.ALGORITHM, + MY_PARAMS, provider); + // Indirectly + assertEquals(MY_PARAMS.toString(), secureRandom.toString()); + } + + @Test + public void testGetInstanceWithSecureRandomParameters() throws NoSuchAlgorithmException { + SecureRandom secureRandom = SecureRandom.getInstance(MySecureRandomProvider.ALGORITHM, + MY_PARAMS); + // Indirectly + assertEquals(MY_PARAMS.toString(), secureRandom.toString()); + } + + @Test + public void testGetInstanceWithNameAndSecureRandomParameters() + throws NoSuchAlgorithmException, NoSuchProviderException { + SecureRandom secureRandom = SecureRandom.getInstance(MySecureRandomProvider.ALGORITHM, + MY_PARAMS, MySecureRandomProvider.NAME); + // Indirectly + assertEquals(MY_PARAMS.toString(), secureRandom.toString()); + } + + @Test + public void testSecureRandomGetNextBytesWithSecureRandomParameters() + throws NoSuchAlgorithmException { + SecureRandom secureRandom = SecureRandom.getInstance(MySecureRandomProvider.ALGORITHM, + MY_PARAMS); + Assert.assertThrows(UnsupportedOperationException.class, + () -> secureRandom.nextBytes(new byte[0], MY_PARAMS)); + } +} diff --git a/ojluni/src/main/java/java/security/Provider.java b/ojluni/src/main/java/java/security/Provider.java index 8239cb7257c..b4df5f5b185 100644 --- a/ojluni/src/main/java/java/security/Provider.java +++ b/ojluni/src/main/java/java/security/Provider.java @@ -132,6 +132,8 @@ public abstract class Provider extends Properties { private transient boolean initialized; + private static final Object[] EMPTY = new Object[0]; + /** * Constructs a provider with the specified name, version number, * and information. @@ -1349,7 +1351,8 @@ public abstract class Provider extends Properties { addEngine("KeyPairGenerator", false, null); addEngine("KeyStore", false, null); addEngine("MessageDigest", false, null); - addEngine("SecureRandom", false, null); + addEngine("SecureRandom", false, + "java.security.SecureRandomParameters"); addEngine("Signature", true, null); addEngine("CertificateFactory", false, null); addEngine("CertPathBuilder", false, null); @@ -1435,6 +1438,11 @@ public abstract class Provider extends Properties { // Reference to the cached implementation Class object private volatile Reference<Class<?>> classRef; + // Will be a Constructor if this service is loaded from the built-in + // classloader (unloading not possible), otherwise a WeakReference to + // a Constructor + private Object constructorCache; + // flag indicating whether this service has its attributes for // supportedKeyFormats or supportedKeyClasses set // if null, the values have not been initialized @@ -1615,39 +1623,38 @@ public abstract class Provider extends Properties { } registered = true; } + Class<?> ctrParamClz; try { + // Android-changed: TODO: Cache cap in ctor when this class is updated to openjdk9+. + // EngineDescription cap = engineDescription; EngineDescription cap = knownEngines.get(type); if (cap == null) { // unknown engine type, use generic code // this is the code path future for non-core // optional packages - return newInstanceGeneric(constructorParameter); - } - if (cap.constructorParameterClassName == null) { - if (constructorParameter != null) { - throw new InvalidParameterException - ("constructorParameter not used with " + type - + " engines"); - } - Class<?> clazz = getImplClass(); - Class<?>[] empty = {}; - Constructor<?> con = clazz.getConstructor(empty); - return con.newInstance(); + ctrParamClz = constructorParameter == null? + null : constructorParameter.getClass(); } else { - Class<?> paramClass = cap.getConstructorParameterClass(); + ctrParamClz = cap.constructorParameterClassName == null? + null : Class.forName(cap.constructorParameterClassName); if (constructorParameter != null) { - Class<?> argClass = constructorParameter.getClass(); - if (paramClass.isAssignableFrom(argClass) == false) { + if (ctrParamClz == null) { throw new InvalidParameterException - ("constructorParameter must be instanceof " - + cap.constructorParameterClassName.replace('$', '.') - + " for engine type " + type); + ("constructorParameter not used with " + type + + " engines"); + } else { + Class<?> argClass = constructorParameter.getClass(); + if (ctrParamClz.isAssignableFrom(argClass) == false) { + throw new InvalidParameterException + ("constructorParameter must be instanceof " + + cap.constructorParameterClassName.replace('$', '.') + + " for engine type " + type); + } } } - Class<?> clazz = getImplClass(); - Constructor<?> cons = clazz.getConstructor(paramClass); - return cons.newInstance(constructorParameter); } + // constructorParameter can be null if not provided + return newInstanceUtil(ctrParamClz, constructorParameter); } catch (NoSuchAlgorithmException e) { throw e; } catch (InvocationTargetException e) { @@ -1663,6 +1670,48 @@ public abstract class Provider extends Properties { } } + private Object newInstanceOf() throws Exception { + Constructor<?> con = getDefaultConstructor(); + return con.newInstance(EMPTY); + } + + private Object newInstanceUtil(Class<?> ctrParamClz, Object ctorParamObj) + throws Exception + { + if (ctrParamClz == null) { + return newInstanceOf(); + } else { + // Looking for the constructor with a params first and fallback + // to one without if not found. This is to support the enhanced + // SecureRandom where both styles of constructors are supported. + // Before jdk9, there was no params support (only getInstance(alg)) + // and an impl only had the params-less constructor. Since jdk9, + // there is getInstance(alg,params) and an impl can contain + // an Impl(params) constructor. + try { + Constructor<?> con = getImplClass().getConstructor(ctrParamClz); + return con.newInstance(ctorParamObj); + } catch (NoSuchMethodException nsme) { + // For pre-jdk9 SecureRandom implementations, they only + // have params-less constructors which still works when + // the input ctorParamObj is null. + // + // For other primitives using params, ctorParamObj should not + // be null and nsme is thrown, just like before. + if (ctorParamObj == null) { + try { + return newInstanceOf(); + } catch (NoSuchMethodException nsme2) { + nsme.addSuppressed(nsme2); + throw nsme; + } + } else { + throw nsme; + } + } + } + } + // return the implementation Class object for this service private Class<?> getImplClass() throws NoSuchAlgorithmException { try { @@ -1690,41 +1739,24 @@ public abstract class Provider extends Properties { } } - /** - * Generic code path for unknown engine types. Call the - * no-args constructor if constructorParameter is null, otherwise - * use the first matching constructor. - */ - private Object newInstanceGeneric(Object constructorParameter) - throws Exception { - Class<?> clazz = getImplClass(); - if (constructorParameter == null) { - // create instance with public no-arg constructor if it exists - try { - Class<?>[] empty = {}; - Constructor<?> con = clazz.getConstructor(empty); - return con.newInstance(); - } catch (NoSuchMethodException e) { - throw new NoSuchAlgorithmException("No public no-arg " - + "constructor found in class " + className); - } + private Constructor<?> getDefaultConstructor() + throws NoSuchAlgorithmException, NoSuchMethodException + { + Object cache = constructorCache; + if (cache instanceof Constructor<?> con) { + return con; } - Class<?> argClass = constructorParameter.getClass(); - Constructor[] cons = clazz.getConstructors(); - // find first public constructor that can take the - // argument as parameter - for (Constructor<?> con : cons) { - Class<?>[] paramTypes = con.getParameterTypes(); - if (paramTypes.length != 1) { - continue; - } - if (paramTypes[0].isAssignableFrom(argClass) == false) { - continue; - } - return con.newInstance(constructorParameter); + Constructor<?> con = null; + if (cache instanceof WeakReference<?> ref){ + con = (Constructor<?>)ref.get(); + } + if (con == null) { + Class<?> clazz = getImplClass(); + con = clazz.getConstructor(); + constructorCache = (clazz.getClassLoader() == null) + ? con : new WeakReference<Constructor<?>>(con); } - throw new NoSuchAlgorithmException("No public constructor matching " - + argClass.getName() + " found in class " + className); + return con; } /** |