From 440d68e0781771c79c5d1dfca9ded97e2fef1378 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Tue, 20 Oct 2026 10:43:48 -0600 Subject: [PATCH 1/2] Run FIPS CASTs once per process in WolfSSL() instead of on every init --- src/java/com/wolfssl/WolfSSL.java | 28 ++++++++- src/test/com/wolfssl/test/WolfSSLTest.java | 69 ++++++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/java/com/wolfssl/WolfSSL.java b/src/java/com/wolfssl/WolfSSL.java index d464998c..513701fb 100644 --- a/src/java/com/wolfssl/WolfSSL.java +++ b/src/java/com/wolfssl/WolfSSL.java @@ -676,11 +676,17 @@ public enum TLS_VERSION { /* Track if library loading was skipped via system property */ private static volatile boolean libraryLoadSkipped = false; + /* Have FIPS CASTs passed in init(), guarded by castLock */ + private static boolean fipsCastsDone = false; + /* ---------------------------- locks ------------------------------- */ /* lock for cleanup */ private final Object cleanupLock = new Object(); + /* lock for running FIPS CASTs in init() */ + private static final Object castLock = new Object(); + /* ------------------------ constructors ---------------------------- */ /** @@ -698,6 +704,9 @@ public enum TLS_VERSION { * {@code Security.setProperty("wolfjsse.skipFIPSCAST", "true")} or * in the {@code java.security} configuration file. * + * CASTs are run at most once per process. After one WolfSSL object runs + * them successfully, later objects skip them. + * * @throws com.wolfssl.WolfSSLException if wolfSSL library fails to * initialize correctly */ @@ -721,7 +730,7 @@ public WolfSSL() throws WolfSSLException { "init (wolfjsse.skipFIPSCAST=true)"); } - ret = init(skipCAST); + ret = initOnce(skipCAST); if (ret != SSL_SUCCESS) { throw new WolfSSLException( "Failed to initialize wolfSSL library: " + ret); @@ -797,6 +806,23 @@ public WolfSSL() throws WolfSSLException { private native int init(int skipFIPSCAST); + /* Run CASTs only once per process */ + private int initOnce(int skipCAST) { + int ret; + + synchronized (castLock) { + if (fipsCastsDone) { + skipCAST = 1; + } + ret = init(skipCAST); + if (ret == SSL_SUCCESS && skipCAST == 0) { + fipsCastsDone = true; + } + } + + return ret; + } + /** * Free native memory allocated at pointer provided. * @param ptr native pointer diff --git a/src/test/com/wolfssl/test/WolfSSLTest.java b/src/test/com/wolfssl/test/WolfSSLTest.java index e0b7c0c8..b816a52f 100644 --- a/src/test/com/wolfssl/test/WolfSSLTest.java +++ b/src/test/com/wolfssl/test/WolfSSLTest.java @@ -31,11 +31,15 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import com.wolfssl.WolfSSL; +import com.wolfssl.WolfSSLContext; import com.wolfssl.WolfSSLException; +import com.wolfssl.WolfSSLSession; import com.wolfssl.WolfSSLLoggingCallback; import com.wolfssl.WolfSSLFIPSErrorCallback; @@ -67,6 +71,71 @@ public void test_WolfSSL_new() { } } + /* Creating WolfSSL objects must not break other threads creating sessions. + * On FIPS builds, rerunning CASTs in each constructor fails other threads + * using those algorithms at the same time. */ + @Test + public void test_WolfSSL_newWithConcurrentSessions() + throws InterruptedException, WolfSSLException { + + /* First object runs the CASTs before session threads start */ + new WolfSSL(); + + final int numThreads = 4; + final AtomicBoolean stop = new AtomicBoolean(false); + final AtomicInteger sessions = new AtomicInteger(0); + final AtomicReference firstError = + new AtomicReference(); + Thread[] threads = new Thread[numThreads]; + + for (int i = 0; i < numThreads; i++) { + threads[i] = new Thread(new Runnable() { + public void run() { + try { + while (!stop.get()) { + WolfSSLContext ctx = new WolfSSLContext( + WolfSSL.SSLv23_ClientMethod()); + WolfSSLSession ssl = new WolfSSLSession(ctx); + ssl.freeSSL(); + ctx.free(); + sessions.incrementAndGet(); + } + } catch (Throwable t) { + firstError.compareAndSet(null, t); + } + } + }); + threads[i].start(); + } + + try { + /* Wait for session threads to start, up to 5 seconds */ + for (int i = 0; i < 5000 && sessions.get() < 100 && + firstError.get() == null; i++) { + Thread.sleep(1); + } + for (int i = 0; i < 20 && firstError.get() == null; i++) { + new WolfSSL(); + } + } catch (WolfSSLException e) { + firstError.compareAndSet(null, e); + } finally { + stop.set(true); + for (int i = 0; i < numThreads; i++) { + threads[i].join(); + } + } + + Throwable err = firstError.get(); + if (err != null) { + AssertionError ae = new AssertionError( + "WolfSSL() with concurrent sessions failed: " + + err.getMessage()); + ae.initCause(err); + throw ae; + } + } + @Test public void test_WolfSSL_bulkCipherEnumsDistinct() throws WolfSSLException { From 3946bd231235624087e76e1d40f603dcc7483db2 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Tue, 20 Oct 2026 13:15:23 -0600 Subject: [PATCH 2/2] JNI: create WolfSSL in cert test classes so NID values are set under Maven --- src/test/com/wolfssl/test/WolfSSLCertRequestTest.java | 4 ++++ src/test/com/wolfssl/test/WolfSSLCertificateTest.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/test/com/wolfssl/test/WolfSSLCertRequestTest.java b/src/test/com/wolfssl/test/WolfSSLCertRequestTest.java index 765d70f3..d7317570 100644 --- a/src/test/com/wolfssl/test/WolfSSLCertRequestTest.java +++ b/src/test/com/wolfssl/test/WolfSSLCertRequestTest.java @@ -65,6 +65,9 @@ public class WolfSSLCertRequestTest { public static String cliEccKeyDer = "examples/certs/ecc-client-key.der"; public static String cliEccKeyPem = "examples/certs/ecc-client-key.pem"; + /* WolfSSL() sets the NID values these tests use */ + private static WolfSSL sslLib = null; + @BeforeClass public static void setCertPaths() throws WolfSSLException { @@ -75,6 +78,7 @@ public static void setCertPaths() throws WolfSSLException { } catch (UnsatisfiedLinkError ule) { fail("failed to load native JNI library"); } + sslLib = new WolfSSL(); cliKeyDer = WolfSSLTestCommon.getPath(cliKeyDer); cliKeyPem = WolfSSLTestCommon.getPath(cliKeyPem); diff --git a/src/test/com/wolfssl/test/WolfSSLCertificateTest.java b/src/test/com/wolfssl/test/WolfSSLCertificateTest.java index 2a7e6426..fb51d9be 100644 --- a/src/test/com/wolfssl/test/WolfSSLCertificateTest.java +++ b/src/test/com/wolfssl/test/WolfSSLCertificateTest.java @@ -141,6 +141,9 @@ private void runOrAllowNotCompiled(ThrowingRunnable r) @Rule public TestRule testWatcher = TimedTestWatcher.create(); + /* WolfSSL() sets the NID values these tests use */ + private static WolfSSL sslLib = null; + @BeforeClass public static void setCertPaths() throws WolfSSLException { @@ -151,6 +154,7 @@ public static void setCertPaths() throws WolfSSLException { } catch (UnsatisfiedLinkError ule) { fail("failed to load native JNI library"); } + sslLib = new WolfSSL(); cliCertDer = WolfSSLTestCommon.getPath(cliCertDer); cliCertPem = WolfSSLTestCommon.getPath(cliCertPem);