Skip to content

Commit f4ac40f

Browse files
committed
JCE: initialize this.rng (Rng) inside WolfCryptRandom if needed, fixes some serialization use cases
1 parent d7426e0 commit f4ac40f

File tree

2 files changed

+113
-10
lines changed

2 files changed

+113
-10
lines changed

src/main/java/com/wolfssl/provider/jce/WolfCryptRandom.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,7 @@ public final class WolfCryptRandom extends SecureRandomSpi {
4444
* Create new WolfCryptRandom object
4545
*/
4646
public WolfCryptRandom() {
47-
48-
this.rng = new Rng();
49-
this.rng.init();
50-
47+
checkRngInitialized();
5148
log("initialized new object");
5249
}
5350

@@ -69,6 +66,8 @@ protected synchronized byte[] engineGenerateSeed(int numBytes)
6966
Rng.RNG_MAX_BLOCK_LEN);
7067
}
7168

69+
checkRngInitialized();
70+
7271
return rng.generateBlock(numBytes);
7372
}
7473

@@ -79,6 +78,8 @@ protected synchronized void engineNextBytes(byte[] bytes) {
7978
throw new NullPointerException("Input byte[] should not be null");
8079
}
8180

81+
checkRngInitialized();
82+
8283
rng.generateBlock(bytes);
8384
}
8485

@@ -94,6 +95,18 @@ protected synchronized void engineSetSeed(byte[] seed) {
9495

9596
}
9697

98+
/**
99+
* Initialize the RNG if needed (null). This handles cases where the object
100+
* was created through deserialization, reflection, etc. and the
101+
* constructor was not called.
102+
*/
103+
private void checkRngInitialized() {
104+
if (this.rng == null) {
105+
this.rng = new Rng();
106+
this.rng.init();
107+
}
108+
}
109+
97110
private void log(String msg) {
98111
WolfCryptDebug.log(getClass(), WolfCryptDebug.INFO, () -> msg);
99112
}
@@ -148,12 +161,9 @@ private synchronized void writeObject(ObjectOutputStream out)
148161
private synchronized void readObject(ObjectInputStream in)
149162
throws IOException, ClassNotFoundException {
150163

151-
if (rng == null) {
152-
this.rng = new Rng();
153-
this.rng.init();
154-
}
155-
156164
in.defaultReadObject();
165+
166+
checkRngInitialized();
157167
}
158168

159169
@Override
@@ -163,7 +173,7 @@ public String toString() {
163173
* SHA-256 = hash function used in Hash_DRBG implementation
164174
* 128 = security strength in bits
165175
* reseed_only = NIST implementation default, prediction resistance
166-
* not enabled for every generate call, onlky when explicitly
176+
* not enabled for every generate call, only when explicitly
167177
* reseeded.
168178
*
169179
* This output format matches other JCE providers, some callers

src/test/java/com/wolfssl/provider/jce/test/WolfCryptRandomTest.java

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@
3636
import java.util.concurrent.CountDownLatch;
3737
import java.util.concurrent.LinkedBlockingQueue;
3838

39+
import java.io.ByteArrayInputStream;
40+
import java.io.ByteArrayOutputStream;
41+
import java.io.IOException;
42+
import java.io.ObjectInputStream;
43+
import java.io.ObjectOutputStream;
44+
3945
import java.security.Security;
4046
import java.security.Provider;
4147
import java.security.SecureRandom;
@@ -345,5 +351,92 @@ public void testGenerateSeedWithZeroArgument()
345351
assertNotNull(seed);
346352
assertEquals(0, seed.length);
347353
}
354+
355+
@Test
356+
public void testSerializationDeserialization()
357+
throws NoSuchProviderException, NoSuchAlgorithmException,
358+
IOException, ClassNotFoundException {
359+
360+
/* Create original SecureRandom instance */
361+
SecureRandom orig = SecureRandom.getInstance("HashDRBG", "wolfJCE");
362+
assertNotNull(orig);
363+
364+
/* Serialize the SecureRandom instance */
365+
byte[] serialized = serialize(orig);
366+
assertNotNull(serialized);
367+
assertTrue(serialized.length > 0);
368+
369+
/* Deserialize the SecureRandom instance */
370+
SecureRandom copy = deserialize(serialized);
371+
assertNotNull(copy);
372+
373+
/* Test that both original and deserialized instances can generate
374+
* random numbers without throwing NullPointerException.
375+
* This test would fail without the lazy loading fix in
376+
* WolfCryptRandom.engineNextBytes() and engineGenerateSeed() */
377+
byte[] origBytes = new byte[32];
378+
byte[] copyBytes = new byte[32];
379+
380+
/* This should not throw NullPointerException */
381+
orig.nextBytes(origBytes);
382+
copy.nextBytes(copyBytes);
383+
384+
/* Also test generateSeed() to ensure it works after deserialization */
385+
byte[] origSeed = orig.generateSeed(16);
386+
byte[] copySeed = copy.generateSeed(16);
387+
388+
assertNotNull(origSeed);
389+
assertNotNull(copySeed);
390+
assertEquals(16, origSeed.length);
391+
assertEquals(16, copySeed.length);
392+
393+
/* Verify we got actual random data */
394+
assertNotNull(origBytes);
395+
assertNotNull(copyBytes);
396+
assertEquals(32, origBytes.length);
397+
assertEquals(32, copyBytes.length);
398+
399+
/* Random bytes should not be all zeros */
400+
boolean origHasNonZero = false;
401+
boolean copyHasNonZero = false;
402+
for (int i = 0; i < 32; i++) {
403+
if (origBytes[i] != 0) {
404+
origHasNonZero = true;
405+
}
406+
if (copyBytes[i] != 0) {
407+
copyHasNonZero = true;
408+
}
409+
}
410+
assertTrue("Original random bytes should not be all zeros",
411+
origHasNonZero);
412+
assertTrue("Deserialized random bytes should not be all zeros",
413+
copyHasNonZero);
414+
415+
/* The two instances should generate different random sequences */
416+
assertFalse("Original and deserialized instances should generate " +
417+
"different random sequences", Arrays.equals(origBytes, copyBytes));
418+
}
419+
420+
/*
421+
* Serialize a SecureRandom object to byte array
422+
*/
423+
private byte[] serialize(SecureRandom sr) throws IOException {
424+
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
425+
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
426+
oos.writeObject(sr);
427+
return bos.toByteArray();
428+
}
429+
}
430+
431+
/*
432+
* Deserialize a SecureRandom object from byte array
433+
*/
434+
private SecureRandom deserialize(byte[] serialized)
435+
throws IOException, ClassNotFoundException {
436+
try (ByteArrayInputStream bis = new ByteArrayInputStream(serialized);
437+
ObjectInputStream ois = new ObjectInputStream(bis)) {
438+
return (SecureRandom) ois.readObject();
439+
}
440+
}
348441
}
349442

0 commit comments

Comments
 (0)