Skip to content

Commit 1aecfdd

Browse files
committed
update regression test to better test when inRemaining == 0 and add on additional continuation bytes testing
add partial header unwrap test and oversized record test
1 parent 0e28002 commit 1aecfdd

File tree

2 files changed

+257
-26
lines changed

2 files changed

+257
-26
lines changed

src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public class WolfSSLEngine extends SSLEngine {
110110

111111
/* session stored (WOLFSSL_SESSION), relevant on client side */
112112
private boolean sessionStored = false;
113+
/* Skip record header peek in next unwrap, continue same TLS record */
113114
private boolean contPartialRecord = false;
114115

115116
/* TLS 1.3 session ticket received (on client side) */

src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java

Lines changed: 256 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3047,13 +3047,13 @@ public void testBufferUnderflowPartialRecord()
30473047
}
30483048

30493049
@Test
3050-
public void testHandshakeUnwrapConsumedNotBufferUnderflow()
3050+
public void testHandshakeUnwrapIncrementalStatus()
30513051
throws NoSuchProviderException, NoSuchAlgorithmException,
30523052
KeyManagementException, KeyStoreException,
30533053
CertificateException, IOException,
30543054
UnrecoverableKeyException {
30553055

3056-
System.out.print("\tTest unwrap consumed != BUFFER_UNDERFLOW");
3056+
System.out.print("\tTest unwrap incremental status");
30573057

30583058
if (!enabledProtocols.contains("TLSv1.3")) {
30593059
System.out.println("\t... skipped");
@@ -3127,42 +3127,272 @@ public void testHandshakeUnwrapConsumedNotBufferUnderflow()
31273127
srvFlight.flip();
31283128

31293129
int total = srvFlight.remaining();
3130-
if (total < 2) {
3130+
/* Parse first TLS record header to find exact boundary:
3131+
* type[1] + version[2] + length[2] = 5 bytes. */
3132+
if (total < 5) {
31313133
error("\t... failed");
3132-
fail("server flight too small to split: " + total);
3134+
fail("server flight too small: " + total);
3135+
}
3136+
int firstRecPayload =
3137+
((srvFlight.get(srvFlight.position() + 3) & 0xFF) << 8)
3138+
| (srvFlight.get(srvFlight.position() + 4) & 0xFF);
3139+
int firstRecordLen = 5 + firstRecPayload;
3140+
if (total < firstRecordLen) {
3141+
error("\t... failed");
3142+
fail("first TLS record extends beyond server flight");
31333143
}
31343144

3135-
/* Feed the first half of the server flight to
3136-
* client.unwrap(). wolfSSL will consume these bytes through
3137-
* the I/O callback, exhaust the buffer, and return
3138-
* SSL_ERROR_WANT_READ. With the fix, BUFFER_UNDERFLOW must
3139-
* NOT be set because inRemaining > 0 (data was provided). */
3140-
int half = total / 2;
3141-
byte[] halfBytes = new byte[half];
3142-
srvFlight.get(halfBytes);
3143-
ByteBuffer firstHalf = ByteBuffer.wrap(halfBytes);
3145+
/* Feed exactly the first TLS record to client.unwrap().
3146+
* Peek confirms it is complete, DoHandshake() runs,
3147+
* wolfSSL consumes all bytes and returns WANT_READ. */
3148+
byte[] firstRecBytes = new byte[firstRecordLen];
3149+
srvFlight.get(firstRecBytes);
3150+
ByteBuffer firstRecord = ByteBuffer.wrap(firstRecBytes);
31443151

31453152
ByteBuffer cliAppBuf =
31463153
ByteBuffer.allocateDirect(appBufSize);
3147-
result = client.unwrap(firstHalf, cliAppBuf);
3154+
result = client.unwrap(firstRecord, cliAppBuf);
3155+
3156+
/* wolfSSL processed first record but needs more data:
3157+
* status must be OK, not BUFFER_UNDERFLOW. */
3158+
if (result.getStatus() != SSLEngineResult.Status.OK) {
3159+
error("\t... failed");
3160+
fail("expected Status.OK, got: " +
3161+
result.getStatus());
3162+
}
3163+
3164+
/* Handshake needs more data to continue. */
3165+
if (result.getHandshakeStatus() !=
3166+
SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
3167+
error("\t... failed");
3168+
fail("expected NEED_UNWRAP, got: " +
3169+
result.getHandshakeStatus());
3170+
}
3171+
3172+
/* All bytes consumed via I/O callback. */
3173+
if (result.bytesConsumed() != firstRecordLen) {
3174+
error("\t... failed");
3175+
fail("expected " + firstRecordLen + " bytes " +
3176+
"consumed, got: " + result.bytesConsumed());
3177+
}
3178+
3179+
/* No application data produced during handshake. */
3180+
if (result.bytesProduced() != 0) {
3181+
error("\t... failed");
3182+
fail("expected 0 bytes produced, got: " +
3183+
result.bytesProduced());
3184+
}
3185+
3186+
/* contPartialRecord=true from first unwrap skips
3187+
* peek; DoHandshake() gets raw continuation bytes. */
3188+
int contLen = Math.min(3, srvFlight.remaining());
3189+
if (contLen > 0) {
3190+
byte[] contBytes = new byte[contLen];
3191+
srvFlight.get(contBytes);
3192+
ByteBuffer contBuf = ByteBuffer.wrap(contBytes);
3193+
ByteBuffer cliAppBuf2 =
3194+
ByteBuffer.allocateDirect(appBufSize);
3195+
result = client.unwrap(contBuf, cliAppBuf2);
3196+
3197+
/* wolfSSL consumed continuation bytes, needs more:
3198+
* must be Status.OK, not BUFFER_UNDERFLOW. */
3199+
if (result.getStatus() !=
3200+
SSLEngineResult.Status.OK) {
3201+
error("\t... failed");
3202+
fail("expected Status.OK for continuation " +
3203+
"bytes, got: " + result.getStatus());
3204+
}
3205+
3206+
/* Handshake still needs more data. */
3207+
if (result.getHandshakeStatus() !=
3208+
SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
3209+
error("\t... failed");
3210+
fail("expected NEED_UNWRAP for continuation " +
3211+
"bytes, got: " +
3212+
result.getHandshakeStatus());
3213+
}
3214+
3215+
/* All continuation bytes consumed. */
3216+
if (result.bytesConsumed() != contLen) {
3217+
error("\t... failed");
3218+
fail("expected " + contLen + " continuation " +
3219+
"bytes consumed, got: " +
3220+
result.bytesConsumed());
3221+
}
3222+
3223+
/* No application data produced. */
3224+
if (result.bytesProduced() != 0) {
3225+
error("\t... failed");
3226+
fail("expected 0 bytes produced, got: " +
3227+
result.bytesProduced());
3228+
}
3229+
}
3230+
3231+
pass("\t... passed");
3232+
}
3233+
3234+
@Test
3235+
public void testHandshakeUnwrapPartialHeader()
3236+
throws NoSuchProviderException, NoSuchAlgorithmException,
3237+
KeyManagementException, KeyStoreException,
3238+
CertificateException, IOException,
3239+
UnrecoverableKeyException {
3240+
3241+
/* Test that feeding <5 bytes to client.unwrap() during
3242+
* handshake returns BUFFER_UNDERFLOW with 0 bytes consumed.
3243+
* Exercises the inRemaining < TLS_RECORD_HEADER_LEN branch
3244+
* of peekTlsRecordHeader(). */
3245+
System.out.print("\tTest unwrap partial header underflow");
3246+
3247+
if (!enabledProtocols.contains("TLSv1.3")) {
3248+
System.out.println("\t... skipped");
3249+
return;
3250+
}
3251+
3252+
SSLContext ctx13 = tf.createSSLContext("TLSv1.3", engineProvider);
3253+
SSLEngine server = ctx13.createSSLEngine();
3254+
SSLEngine client = ctx13.createSSLEngine("localhost", 11111);
3255+
3256+
server.setUseClientMode(false);
3257+
server.setNeedClientAuth(false);
3258+
client.setUseClientMode(true);
3259+
3260+
server.beginHandshake();
3261+
client.beginHandshake();
3262+
3263+
int packetBufSize = client.getSession().getPacketBufferSize();
3264+
int appBufSize = client.getSession().getApplicationBufferSize();
3265+
3266+
/* Wrap ClientHello to put client into NEED_UNWRAP state */
3267+
ByteBuffer cliToSrv =
3268+
ByteBuffer.allocateDirect(packetBufSize);
3269+
ByteBuffer emptyApp = ByteBuffer.allocate(0);
3270+
SSLEngineResult result = client.wrap(emptyApp, cliToSrv);
3271+
if (result.getStatus() != SSLEngineResult.Status.OK) {
3272+
error("\t... failed");
3273+
fail("client wrap (ClientHello) failed: " +
3274+
result.getStatus());
3275+
}
31483276

3149-
/* BUFFER_UNDERFLOW must not be returned when input was
3150-
* consumed - regression for inRemaining == 0 guard fix */
3151-
if (result.getStatus() ==
3277+
if (client.getHandshakeStatus() !=
3278+
SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
3279+
error("\t... failed");
3280+
fail("expected NEED_UNWRAP after ClientHello, got: " +
3281+
client.getHandshakeStatus());
3282+
}
3283+
3284+
/* Feed 2 bytes: less than the 5-byte TLS record header.
3285+
* Peek must signal BUFFER_UNDERFLOW without calling into
3286+
* wolfSSL, so 0 bytes are consumed. */
3287+
ByteBuffer partialHeader =
3288+
ByteBuffer.wrap(new byte[] { 0x16, 0x03 });
3289+
ByteBuffer cliAppBuf =
3290+
ByteBuffer.allocateDirect(appBufSize);
3291+
result = client.unwrap(partialHeader, cliAppBuf);
3292+
3293+
if (result.getStatus() !=
31523294
SSLEngineResult.Status.BUFFER_UNDERFLOW) {
31533295
error("\t... failed");
3154-
fail("unwrap() with consumed handshake data must not " +
3155-
"return BUFFER_UNDERFLOW (regression: inRemaining" +
3156-
" == 0 guard), bytesConsumed=" +
3157-
result.bytesConsumed());
3296+
fail("expected BUFFER_UNDERFLOW for partial header, " +
3297+
"got: " + result.getStatus());
31583298
}
31593299

3160-
/* Input was non-empty so at least some bytes must have
3161-
* been consumed */
3162-
if (result.bytesConsumed() == 0) {
3300+
if (result.bytesConsumed() != 0) {
3301+
error("\t... failed");
3302+
fail("BUFFER_UNDERFLOW must consume 0 bytes, " +
3303+
"consumed: " + result.bytesConsumed());
3304+
}
3305+
3306+
pass("\t... passed");
3307+
}
3308+
3309+
@Test
3310+
public void testHandshakeUnwrapOversizedRecord()
3311+
throws NoSuchProviderException, NoSuchAlgorithmException,
3312+
KeyManagementException, KeyStoreException,
3313+
CertificateException, IOException,
3314+
UnrecoverableKeyException {
3315+
3316+
/* Test that a partial record whose header claims
3317+
* recLen > MAX_RECORD_SIZE (but <= packetBufferSize - 5)
3318+
* returns BUFFER_UNDERFLOW with 0 bytes consumed. Prior to
3319+
* fixing the MAX_RECORD_SIZE cap, this range bypassed the
3320+
* peek entirely and wolfSSL would consume partial bytes. */
3321+
System.out.print("\tTest unwrap oversized record underflow");
3322+
3323+
if (!enabledProtocols.contains("TLSv1.3")) {
3324+
System.out.println("\t... skipped");
3325+
return;
3326+
}
3327+
3328+
SSLContext ctx13 = tf.createSSLContext("TLSv1.3", engineProvider);
3329+
SSLEngine server = ctx13.createSSLEngine();
3330+
SSLEngine client = ctx13.createSSLEngine("localhost", 11111);
3331+
3332+
server.setUseClientMode(false);
3333+
server.setNeedClientAuth(false);
3334+
client.setUseClientMode(true);
3335+
3336+
server.beginHandshake();
3337+
client.beginHandshake();
3338+
3339+
int packetBufSize = client.getSession().getPacketBufferSize();
3340+
int appBufSize = client.getSession().getApplicationBufferSize();
3341+
3342+
/* Wrap ClientHello to put client into NEED_UNWRAP state */
3343+
ByteBuffer cliToSrv =
3344+
ByteBuffer.allocateDirect(packetBufSize);
3345+
ByteBuffer emptyApp = ByteBuffer.allocate(0);
3346+
SSLEngineResult result = client.wrap(emptyApp, cliToSrv);
3347+
if (result.getStatus() != SSLEngineResult.Status.OK) {
3348+
error("\t... failed");
3349+
fail("client wrap (ClientHello) failed: " +
3350+
result.getStatus());
3351+
}
3352+
3353+
if (client.getHandshakeStatus() !=
3354+
SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
3355+
error("\t... failed");
3356+
fail("expected NEED_UNWRAP after ClientHello, got: " +
3357+
client.getHandshakeStatus());
3358+
}
3359+
3360+
/* recLen = MAX_RECORD_SIZE + 1 must fit within
3361+
* packetBufferSize (header not counted). Skip if not,
3362+
* which would indicate an unusual build configuration. */
3363+
int oversizedRecLen = WolfSSL.MAX_RECORD_SIZE + 1;
3364+
if (oversizedRecLen > packetBufSize - 5) {
3365+
System.out.println("\t... skipped");
3366+
return;
3367+
}
3368+
3369+
/* Craft a 5-byte TLS record header: content type
3370+
* handshake(0x16), version TLS 1.2 record layer(0x0303),
3371+
* length = MAX_RECORD_SIZE + 1. Valid content-type and
3372+
* version bytes ensure the plausibility guard in
3373+
* peekTlsRecordHeader() does not filter the header before
3374+
* the length check. */
3375+
byte recLenHi = (byte) ((oversizedRecLen >> 8) & 0xFF);
3376+
byte recLenLo = (byte) (oversizedRecLen & 0xFF);
3377+
ByteBuffer fakeHeader = ByteBuffer.wrap(new byte[] {
3378+
0x16, 0x03, 0x03, recLenHi, recLenLo
3379+
});
3380+
3381+
ByteBuffer cliAppBuf =
3382+
ByteBuffer.allocateDirect(appBufSize);
3383+
result = client.unwrap(fakeHeader, cliAppBuf);
3384+
3385+
if (result.getStatus() !=
3386+
SSLEngineResult.Status.BUFFER_UNDERFLOW) {
3387+
error("\t... failed");
3388+
fail("expected BUFFER_UNDERFLOW for oversized partial " +
3389+
"record, got: " + result.getStatus());
3390+
}
3391+
3392+
if (result.bytesConsumed() != 0) {
31633393
error("\t... failed");
3164-
fail("unwrap() consumed 0 bytes from a non-empty " +
3165-
"handshake buffer");
3394+
fail("BUFFER_UNDERFLOW must consume 0 bytes, " +
3395+
"consumed: " + result.bytesConsumed());
31663396
}
31673397

31683398
pass("\t... passed");

0 commit comments

Comments
 (0)