JSSE: add unit test for instance when multiple records are queued and partial drain of CopyOutPacket() is necesssary

pull/359/head
Ruby Martin 2026-04-21 18:39:16 -06:00
parent 5cf3d7572a
commit 837436cfd8
1 changed files with 112 additions and 0 deletions

View File

@ -26,6 +26,7 @@ import com.wolfssl.WolfSSLException;
import com.wolfssl.provider.jsse.WolfSSLProvider;
import java.io.EOFException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
@ -3508,4 +3509,115 @@ public class WolfSSLEngineTest {
pass("\t\t... passed");
}
@Test
public void testWrapPartialDrainOffsetUpdate()
throws NoSuchProviderException, NoSuchAlgorithmException,
KeyManagementException, KeyStoreException,
CertificateException, IOException,
UnrecoverableKeyException,
NoSuchFieldException, IllegalAccessException {
/* CopyOutPacket() must decrement internalIOSendBufOffset after a
* partial drain, otherwise stale ciphertext gets re-emitted. */
System.out.print("\tTesting wrap() partial drain offset");
this.ctx = tf.createSSLContext("TLS", engineProvider);
SSLEngine server = this.ctx.createSSLEngine();
SSLEngine client = this.ctx.createSSLEngine("wolfSSL test", 11111);
server.setUseClientMode(false);
server.setNeedClientAuth(false);
client.setUseClientMode(true);
server.beginHandshake();
client.beginHandshake();
int ret = tf.testConnection(server, client, null, null,
"partial drain test");
if (ret != 0) {
error("\t... failed");
fail("failed to create connection");
}
/* Inject marker of size packetSz + 1000 so first drain copies
* packetSz bytes and leaves 1000 bytes queued. */
int packetSz = client.getSession().getPacketBufferSize();
int queuedSz = packetSz + 1000;
byte[] marker = new byte[queuedSz];
for (int i = 0; i < queuedSz; i++) {
marker[i] = (byte)((i * 31) & 0xFF);
}
Field bufField =
WolfSSLEngine.class.getDeclaredField("internalIOSendBuf");
Field bufSzField =
WolfSSLEngine.class.getDeclaredField("internalIOSendBufSz");
Field offField =
WolfSSLEngine.class.getDeclaredField(
"internalIOSendBufOffset");
bufField.setAccessible(true);
bufSzField.setAccessible(true);
offField.setAccessible(true);
bufField.set(client, marker);
bufSzField.setInt(client, queuedSz);
offField.setInt(client, queuedSz);
/* First wrap: out buffer sized exactly to packetBufferSize so
* the guard passes but CopyOutPacket partial-drains. */
ByteBuffer empty = ByteBuffer.allocate(0);
ByteBuffer firstOut = ByteBuffer.allocate(packetSz);
SSLEngineResult result = client.wrap(empty, firstOut);
if (result.bytesProduced() != packetSz) {
error("\t... failed");
fail("first wrap expected " + packetSz +
" produced, got " + result.bytesProduced());
}
/* Fix-sensitive check: unfixed code leaves offset at queuedSz. */
int offAfterFirst = offField.getInt(client);
if (offAfterFirst != queuedSz - packetSz) {
error("\t... failed");
fail("internalIOSendBufOffset not decremented after " +
"partial drain: expected " + (queuedSz - packetSz) +
", got " + offAfterFirst);
}
/* Second wrap: must drain only the remainder; unfixed code
* would re-emit packetSz stale bytes instead. */
ByteBuffer secondOut = ByteBuffer.allocate(packetSz);
result = client.wrap(empty, secondOut);
int expectedRemainder = queuedSz - packetSz;
if (result.bytesProduced() != expectedRemainder) {
error("\t... failed");
fail("second wrap expected " + expectedRemainder +
" produced (remainder only), got " +
result.bytesProduced() +
" - stale bytes re-sent after partial drain");
}
if (offField.getInt(client) != 0) {
error("\t... failed");
fail("internalIOSendBufOffset not reset to 0 after " +
"remainder drained");
}
/* Full integrity check: concatenated drained output must equal
* the original injected marker exactly. */
firstOut.flip();
secondOut.flip();
byte[] drained = new byte[queuedSz];
firstOut.get(drained, 0, packetSz);
secondOut.get(drained, packetSz, expectedRemainder);
if (!Arrays.equals(marker, drained)) {
error("\t... failed");
fail("drained output does not match injected queue");
}
pass("\t... passed");
}
}