@@ -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 ("\t Test unwrap consumed != BUFFER_UNDERFLOW " );
3056+ System .out .print ("\t Test 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 ("\t Test 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 ("\t Test 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