Merge pull request #365 from JeremiahM37/fenrir-fixes-4

unwrap bounds, CBIO error returns, key zeroization
pull/366/head
Chris Conlon 2026-05-04 10:31:09 -06:00 committed by GitHub
commit 1bea4bec2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 196 additions and 23 deletions

View File

@ -368,6 +368,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCRL_X509_1CRL_1sign
const WOLFSSL_EVP_MD* md = NULL;
unsigned char* rsaPrivBuf = NULL;
const char* mdName = NULL;
jboolean isCopy = JNI_FALSE;
int ret = WOLFSSL_SUCCESS;
(void)jcl;
@ -375,7 +376,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCRL_X509_1CRL_1sign
return WOLFSSL_FAILURE;
}
keyBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, keyBytes, NULL);
keyBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, keyBytes, &isCopy);
if ((*jenv)->ExceptionOccurred(jenv)) {
(*jenv)->ExceptionClear(jenv);
return WOLFSSL_FAILURE;
@ -471,6 +472,15 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCRL_X509_1CRL_1sign
(*jenv)->ReleaseStringUTFChars(jenv, digestAlg, mdName);
}
if (keyBuf != NULL) {
/* Only zero if JVM made a copy */
if (isCopy == JNI_TRUE) {
#if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \
!defined(WOLFSSL_NO_FORCE_ZERO)
wc_ForceZero(keyBuf, keySz);
#else
XMEMSET(keyBuf, 0, keySz);
#endif
}
(*jenv)->ReleaseByteArrayElements(jenv, keyBytes, (jbyte*)keyBuf,
JNI_ABORT);
}

View File

@ -247,6 +247,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1sign
const WOLFSSL_EVP_MD* md = NULL;
unsigned char* rsaPrivBuf = NULL;
const char* mdName = NULL;
jboolean isCopy = JNI_FALSE;
int ret = WOLFSSL_SUCCESS;
(void)jcl;
@ -255,7 +256,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1sign
return WOLFSSL_FAILURE;
}
keyBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, keyBytes, NULL);
keyBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, keyBytes, &isCopy);
keySz = (*jenv)->GetArrayLength(jenv, keyBytes);
if (keyBuf == NULL || keySz == 0) {
@ -343,6 +344,15 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1sign
XFREE(derBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
if (keyBuf != NULL) {
/* Only zero if JVM made a copy */
if (isCopy == JNI_TRUE) {
#if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \
!defined(WOLFSSL_NO_FORCE_ZERO)
wc_ForceZero(keyBuf, keySz);
#else
XMEMSET(keyBuf, 0, keySz);
#endif
}
(*jenv)->ReleaseByteArrayElements(jenv, keyBytes,
(jbyte*)keyBuf, JNI_ABORT);
}

View File

@ -888,6 +888,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1sign
const WOLFSSL_EVP_MD* md = NULL;
unsigned char* rsaPrivBuf = NULL;
const char* mdName = NULL;
jboolean isCopy = JNI_FALSE;
int ret = WOLFSSL_SUCCESS;
(void)jcl;
@ -897,7 +898,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1sign
return WOLFSSL_FAILURE;
}
fileBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, fileBytes, NULL);
fileBuf = (byte*)(*jenv)->GetByteArrayElements(jenv, fileBytes, &isCopy);
fileSz = (*jenv)->GetArrayLength(jenv, fileBytes);
if (fileBuf == NULL || fileSz == 0) {
@ -990,6 +991,15 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1sign
XFREE(derBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
if (fileBuf != NULL) {
/* Only zero if JVM made a copy */
if (isCopy == JNI_TRUE) {
#if (LIBWOLFSSL_VERSION_HEX >= 0x05008004) && \
!defined(WOLFSSL_NO_FORCE_ZERO)
wc_ForceZero(fileBuf, fileSz);
#else
XMEMSET(fileBuf, 0, fileSz);
#endif
}
(*jenv)->ReleaseByteArrayElements(jenv, fileBytes,
(jbyte*)fileBuf, JNI_ABORT);
}

View File

@ -1117,7 +1117,7 @@ int NativeIORecvCb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
"NativeIORecvCb");
if (needsDetach)
(*g_vm)->DetachCurrentThread(g_vm);
return 0;
return WOLFSSL_CBIO_ERR_GENERAL;
}
/* lookup WolfSSLSession class from object */
@ -1329,7 +1329,7 @@ int NativeIOSendCb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
"NativeIOSendCb");
if (needsDetach)
(*g_vm)->DetachCurrentThread(g_vm);
return 0;
return WOLFSSL_CBIO_ERR_GENERAL;
}
/* lookup WolfSSLSession class from object */

View File

@ -6613,7 +6613,7 @@ int NativeSSLIORecvCb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
if (needsDetach) {
(*g_vm)->DetachCurrentThread(g_vm);
}
return 0;
return WOLFSSL_CBIO_ERR_GENERAL;
}
/* Detect if we should use ByteBuffer or byte[] I/O callbacks */
@ -6789,7 +6789,7 @@ int NativeSSLIOSendCb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
if (needsDetach) {
(*g_vm)->DetachCurrentThread(g_vm);
}
return 0;
return WOLFSSL_CBIO_ERR_GENERAL;
}
/* Detect if we should use ByteBuffer or byte[] I/O callbacks */

View File

@ -28,6 +28,7 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
@ -561,9 +562,13 @@ public class WolfSSLCRL implements Serializable {
"PrivateKey does not support encoding");
}
synchronized (crlLock) {
ret = X509_CRL_sign(this.crlPtr, evpKeyType, encodedKey,
WolfSSL.SSL_FILETYPE_ASN1, digestAlg);
try {
synchronized (crlLock) {
ret = X509_CRL_sign(this.crlPtr, evpKeyType, encodedKey,
WolfSSL.SSL_FILETYPE_ASN1, digestAlg);
}
} finally {
Arrays.fill(encodedKey, (byte)0);
}
return ret;

View File

@ -23,6 +23,7 @@ package com.wolfssl;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.interfaces.RSAPublicKey;
@ -634,7 +635,11 @@ public class WolfSSLCertRequest {
"Failed to read bytes from file: " + filePath);
}
signRequest(fileBytes, keyType, format, digestAlg);
try {
signRequest(fileBytes, keyType, format, digestAlg);
} finally {
Arrays.fill(fileBytes, (byte)0);
}
}
/**
@ -751,9 +756,13 @@ public class WolfSSLCertRequest {
throw new WolfSSLException("PrivateKey does not support encoding");
}
synchronized (x509ReqLock) {
ret = X509_REQ_sign(this.x509ReqPtr, evpKeyType, encodedKey,
WolfSSL.SSL_FILETYPE_ASN1, digestAlg);
try {
synchronized (x509ReqLock) {
ret = X509_REQ_sign(this.x509ReqPtr, evpKeyType, encodedKey,
WolfSSL.SSL_FILETYPE_ASN1, digestAlg);
}
} finally {
Arrays.fill(encodedKey, (byte)0);
}
if (ret != WolfSSL.SSL_SUCCESS) {

View File

@ -1345,7 +1345,11 @@ public class WolfSSLCertificate implements Serializable {
"Failed to read bytes from file: " + filePath);
}
signCert(fileBytes, keyType, format, digestAlg);
try {
signCert(fileBytes, keyType, format, digestAlg);
} finally {
Arrays.fill(fileBytes, (byte)0);
}
}
/**
@ -1461,9 +1465,13 @@ public class WolfSSLCertificate implements Serializable {
throw new WolfSSLException("PrivateKey does not support encoding");
}
synchronized (x509Lock) {
ret = X509_sign(this.x509Ptr, evpKeyType, encodedKey,
WolfSSL.SSL_FILETYPE_ASN1, digestAlg);
try {
synchronized (x509Lock) {
ret = X509_sign(this.x509Ptr, evpKeyType, encodedKey,
WolfSSL.SSL_FILETYPE_ASN1, digestAlg);
}
} finally {
Arrays.fill(encodedKey, (byte)0);
}
if (ret != WolfSSL.SSL_SUCCESS) {

View File

@ -1276,7 +1276,7 @@ public class WolfSSLEngine extends SSLEngine {
/* Copy from intermediate buffer to output bufs */
for (i = 0; i < ret;) {
if (idx + ofst >= length) {
if (idx >= length) {
/* no more output buffers left */
break;
}
@ -1585,9 +1585,9 @@ public class WolfSSLEngine extends SSLEngine {
if (outputSpace >= this.pendingAppDataLen) {
/* Serve stashed data to output buffers */
int idx2 = 0;
for (int pos = 0;
pos < this.pendingAppDataLen;) {
if (idx2 + ofst >= length) break;
int pos = 0;
for (; pos < this.pendingAppDataLen;) {
if (idx2 >= length) break;
int space = out[idx2 + ofst].remaining();
if (space == 0) { idx2++; continue; }
int sz2 = Math.min(space,
@ -1597,7 +1597,7 @@ public class WolfSSLEngine extends SSLEngine {
pos += sz2;
if (pos < this.pendingAppDataLen) idx2++;
}
produced += this.pendingAppDataLen;
produced += pos;
/* Advance past previously consumed bytes */
synchronized (netDataLock) {
in.position(in.position() +

View File

@ -3420,5 +3420,126 @@ public class WolfSSLEngineTest {
s.unwrap(ByteBuffer.allocateDirect(
s.getSession().getPacketBufferSize()), out, 1, 1);
}
/* Verify unwrap() with ofst > 0 placed all `data` bytes into
* out[2] and out[3], and that bytesProduced matches. Regression
* for the `idx + ofst >= length` copy-loop bound in unwrap. */
private void assertOffsetUnwrapOk(SSLEngineResult result,
ByteBuffer[] outArr, byte[] data) {
assertEquals(data.length, result.bytesProduced());
assertEquals(0, outArr[0].position());
assertEquals(0, outArr[1].position());
outArr[2].flip();
outArr[3].flip();
int n2 = outArr[2].remaining();
int n3 = outArr[3].remaining();
assertEquals(data.length, n2 + n3);
byte[] got = new byte[n2 + n3];
outArr[2].get(got, 0, n2);
outArr[3].get(got, n2, n3);
assertTrue(java.util.Arrays.equals(got, data));
}
/* Wrap `data` from client and return server's network buffer
* positioned for unwrap(). */
private ByteBuffer wrapForServer(SSLEngine client, byte[] data)
throws SSLException {
ByteBuffer netBuf = ByteBuffer.allocateDirect(
client.getSession().getPacketBufferSize());
SSLEngineResult r = client.wrap(ByteBuffer.wrap(data), netBuf);
assertEquals(SSLEngineResult.Status.OK, r.getStatus());
netBuf.flip();
return netBuf;
}
@Test
public void testUnwrapOffsetMultipleBuffers() throws Exception {
this.ctx = tf.createSSLContext("TLS", engineProvider);
SSLEngine server = this.ctx.createSSLEngine();
SSLEngine client = this.ctx.createSSLEngine("wolfSSL test", 11111);
server.setUseClientMode(false);
client.setUseClientMode(true);
assertEquals(0, tf.testConnection(server, client, null, null, "x"));
byte[] data = new byte[1024];
new Random().nextBytes(data);
ByteBuffer netBuf = wrapForServer(client, data);
ByteBuffer[] outArr = new ByteBuffer[] {
ByteBuffer.allocate(600), ByteBuffer.allocate(600),
ByteBuffer.allocate(600), ByteBuffer.allocate(600)
};
SSLEngineResult r = server.unwrap(netBuf, outArr, 2, 2);
assertEquals(SSLEngineResult.Status.OK, r.getStatus());
assertOffsetUnwrapOk(r, outArr, data);
}
@Test
public void testUnwrapPendingAppDataWithOffset() throws Exception {
this.ctx = tf.createSSLContext("TLS", engineProvider);
SSLEngine server = this.ctx.createSSLEngine();
SSLEngine client = this.ctx.createSSLEngine("wolfSSL test", 11111);
server.setUseClientMode(false);
client.setUseClientMode(true);
assertEquals(0, tf.testConnection(server, client, null, null, "x"));
byte[] data = new byte[1024];
new Random().nextBytes(data);
ByteBuffer netBuf = wrapForServer(client, data);
/* First unwrap into too-small output stashes pendingAppData */
SSLEngineResult r = server.unwrap(netBuf, ByteBuffer.allocate(64));
assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, r.getStatus());
ByteBuffer[] outArr = new ByteBuffer[] {
ByteBuffer.allocate(600), ByteBuffer.allocate(600),
ByteBuffer.allocate(600), ByteBuffer.allocate(600)
};
r = server.unwrap(netBuf, outArr, 2, 2);
assertOffsetUnwrapOk(r, outArr, data);
}
@Test
public void testUnwrapPendingAppDataReStashWithOffset()
throws Exception {
this.ctx = tf.createSSLContext("TLS", engineProvider);
SSLEngine server = this.ctx.createSSLEngine();
SSLEngine client = this.ctx.createSSLEngine("wolfSSL test", 11111);
server.setUseClientMode(false);
client.setUseClientMode(true);
assertEquals(0, tf.testConnection(server, client, null, null, "x"));
byte[] data = new byte[1024];
new Random().nextBytes(data);
ByteBuffer netBuf = wrapForServer(client, data);
/* First unwrap stashes pendingAppData */
SSLEngineResult r = server.unwrap(netBuf, ByteBuffer.allocate(64));
assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, r.getStatus());
/* Second unwrap: ofst > 0 but total still too small;
* pendingAppData must survive intact for a later call */
ByteBuffer[] tooSmall = new ByteBuffer[] {
ByteBuffer.allocate(200), ByteBuffer.allocate(200),
ByteBuffer.allocate(200), ByteBuffer.allocate(200)
};
r = server.unwrap(netBuf, tooSmall, 2, 2);
assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, r.getStatus());
/* Third unwrap: ofst > 0 with room drains the stash */
ByteBuffer[] outArr = new ByteBuffer[] {
ByteBuffer.allocate(600), ByteBuffer.allocate(600),
ByteBuffer.allocate(600), ByteBuffer.allocate(600)
};
r = server.unwrap(netBuf, outArr, 2, 2);
assertOffsetUnwrapOk(r, outArr, data);
}
}