aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Chang <vichang@google.com>2024-02-20 16:14:21 +0000
committerVictor Chang <vichang@google.com>2024-02-20 16:28:35 +0000
commit507e1943658c76ca1063be4f2e0ed65d8acbe94e (patch)
tree2e0a5c76071b99bea9a4dc9db909f4b8b8059de8
parent67e6f8d249230b8ed43888e1561d70763bafb719 (diff)
downloadlibcore-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.java160
-rw-r--r--ojluni/src/main/java/java/security/Provider.java142
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;
}
/**