Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 45 additions & 9 deletions src/main/java/com/wolfssl/provider/jce/WolfCryptRandom.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ public final class WolfCryptRandom extends SecureRandomSpi {
* Create new WolfCryptRandom object
*/
public WolfCryptRandom() {

this.rng = new Rng();
this.rng.init();

checkRngInitialized();
log("initialized new object");
}

Expand All @@ -69,19 +66,45 @@ protected synchronized byte[] engineGenerateSeed(int numBytes)
Rng.RNG_MAX_BLOCK_LEN);
}

checkRngInitialized();

return rng.generateBlock(numBytes);
}

@Override
protected synchronized void engineNextBytes(byte[] bytes) {

if (bytes == null) {
throw new NullPointerException("Input byte[] should not be null");
}

checkRngInitialized();

rng.generateBlock(bytes);
}

@Override
protected synchronized void engineSetSeed(byte[] seed) {

if (seed == null) {
throw new NullPointerException("Input seed[] should not be null");
}

/* wolfCrypt reseeds internally automatically */
log("setSeed() not supported by wolfJCE");

}

/**
* Initialize the RNG if needed (null). This handles cases where the object
* was created through deserialization, reflection, etc. and the
* constructor was not called.
*/
private void checkRngInitialized() {
if (this.rng == null) {
this.rng = new Rng();
this.rng.init();
}
}

private void log(String msg) {
Expand Down Expand Up @@ -138,12 +161,25 @@ private synchronized void writeObject(ObjectOutputStream out)
private synchronized void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {

if (rng == null) {
this.rng = new Rng();
this.rng.init();
}

in.defaultReadObject();

checkRngInitialized();
}

@Override
public String toString() {
/* Native wolfCrypt DRBG details:
* Hash_DRBG = DRBG implementation
* SHA-256 = hash function used in Hash_DRBG implementation
* 128 = security strength in bits
* reseed_only = NIST implementation default, prediction resistance
* not enabled for every generate call, only when explicitly
* reseeded.
*
* This output format matches other JCE providers, some callers
* may expect this format.
*/
return "Hash_DRBG,SHA-256,128,reseed_only";
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import java.security.Security;
import java.security.Provider;
import java.security.SecureRandom;
Expand Down Expand Up @@ -345,5 +351,92 @@ public void testGenerateSeedWithZeroArgument()
assertNotNull(seed);
assertEquals(0, seed.length);
}

@Test
public void testSerializationDeserialization()
throws NoSuchProviderException, NoSuchAlgorithmException,
IOException, ClassNotFoundException {

/* Create original SecureRandom instance */
SecureRandom orig = SecureRandom.getInstance("HashDRBG", "wolfJCE");
assertNotNull(orig);

/* Serialize the SecureRandom instance */
byte[] serialized = serialize(orig);
assertNotNull(serialized);
assertTrue(serialized.length > 0);

/* Deserialize the SecureRandom instance */
SecureRandom copy = deserialize(serialized);
assertNotNull(copy);

/* Test that both original and deserialized instances can generate
* random numbers without throwing NullPointerException.
* This test would fail without the lazy loading fix in
* WolfCryptRandom.engineNextBytes() and engineGenerateSeed() */
byte[] origBytes = new byte[32];
byte[] copyBytes = new byte[32];

/* This should not throw NullPointerException */
orig.nextBytes(origBytes);
copy.nextBytes(copyBytes);

/* Also test generateSeed() to ensure it works after deserialization */
byte[] origSeed = orig.generateSeed(16);
byte[] copySeed = copy.generateSeed(16);

assertNotNull(origSeed);
assertNotNull(copySeed);
assertEquals(16, origSeed.length);
assertEquals(16, copySeed.length);

/* Verify we got actual random data */
assertNotNull(origBytes);
assertNotNull(copyBytes);
assertEquals(32, origBytes.length);
assertEquals(32, copyBytes.length);

/* Random bytes should not be all zeros */
boolean origHasNonZero = false;
boolean copyHasNonZero = false;
for (int i = 0; i < 32; i++) {
if (origBytes[i] != 0) {
origHasNonZero = true;
}
if (copyBytes[i] != 0) {
copyHasNonZero = true;
}
}
assertTrue("Original random bytes should not be all zeros",
origHasNonZero);
assertTrue("Deserialized random bytes should not be all zeros",
copyHasNonZero);

/* The two instances should generate different random sequences */
assertFalse("Original and deserialized instances should generate " +
"different random sequences", Arrays.equals(origBytes, copyBytes));
}

/*
* Serialize a SecureRandom object to byte array
*/
private byte[] serialize(SecureRandom sr) throws IOException {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(sr);
return bos.toByteArray();
}
}

/*
* Deserialize a SecureRandom object from byte array
*/
private SecureRandom deserialize(byte[] serialized)
throws IOException, ClassNotFoundException {
try (ByteArrayInputStream bis = new ByteArrayInputStream(serialized);
ObjectInputStream ois = new ObjectInputStream(bis)) {
return (SecureRandom) ois.readObject();
}
}
}

Loading