@@ -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,297 @@ 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 );
31483155
3149- /* BUFFER_UNDERFLOW must not be returned when input was
3150- * consumed - regression for inRemaining == 0 guard fix */
3151- if (result .getStatus () ==
3152- SSLEngineResult .Status .BUFFER_UNDERFLOW ) {
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 ) {
31533167 error ("\t ... failed" );
3154- fail ("unwrap() with consumed handshake data must not " +
3155- "return BUFFER_UNDERFLOW (regression: inRemaining" +
3156- " == 0 guard), bytesConsumed=" +
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+ /* Server flight must contain more than one record so we
3187+ * can feed continuation bytes and exercise contPartialRecord.
3188+ * TLSv1.3 server flight (ServerHello + EncryptedExtensions +
3189+ * Certificate + CertificateVerify + Finished) always spans
3190+ * multiple TLS records. */
3191+ if (srvFlight .remaining () == 0 ) {
3192+ error ("\t ... failed" );
3193+ fail ("server flight is a single record; cannot " +
3194+ "test contPartialRecord continuation path" );
3195+ }
3196+
3197+ /* contPartialRecord=true from first unwrap skips
3198+ * peek; DoHandshake() gets raw continuation bytes. */
3199+ int contLen = Math .min (3 , srvFlight .remaining ());
3200+ byte [] contBytes = new byte [contLen ];
3201+ srvFlight .get (contBytes );
3202+ ByteBuffer contBuf = ByteBuffer .wrap (contBytes );
3203+ ByteBuffer cliAppBuf2 =
3204+ ByteBuffer .allocateDirect (appBufSize );
3205+ result = client .unwrap (contBuf , cliAppBuf2 );
3206+
3207+ /* wolfSSL consumed continuation bytes, needs more:
3208+ * must be Status.OK, not BUFFER_UNDERFLOW. */
3209+ if (result .getStatus () !=
3210+ SSLEngineResult .Status .OK ) {
3211+ error ("\t ... failed" );
3212+ fail ("expected Status.OK for continuation " +
3213+ "bytes, got: " + result .getStatus ());
3214+ }
3215+
3216+ /* Handshake still needs more data. */
3217+ if (result .getHandshakeStatus () !=
3218+ SSLEngineResult .HandshakeStatus .NEED_UNWRAP ) {
3219+ error ("\t ... failed" );
3220+ fail ("expected NEED_UNWRAP for continuation " +
3221+ "bytes, got: " +
3222+ result .getHandshakeStatus ());
3223+ }
3224+
3225+ /* All continuation bytes consumed. */
3226+ if (result .bytesConsumed () != contLen ) {
3227+ error ("\t ... failed" );
3228+ fail ("expected " + contLen + " continuation " +
3229+ "bytes consumed, got: " +
31573230 result .bytesConsumed ());
31583231 }
31593232
3160- /* Input was non-empty so at least some bytes must have
3161- * been consumed */
3162- if (result .bytesConsumed () == 0 ) {
3233+ /* No application data produced. */
3234+ if (result .bytesProduced () != 0 ) {
3235+ error ("\t ... failed" );
3236+ fail ("expected 0 bytes produced, got: " +
3237+ result .bytesProduced ());
3238+ }
3239+
3240+ pass ("\t ... passed" );
3241+ }
3242+
3243+ @ Test
3244+ public void testHandshakeUnwrapPartialHeader ()
3245+ throws NoSuchProviderException , NoSuchAlgorithmException ,
3246+ KeyManagementException , KeyStoreException ,
3247+ CertificateException , IOException ,
3248+ UnrecoverableKeyException {
3249+
3250+ /* Test that feeding <5 bytes to client.unwrap() during
3251+ * handshake returns BUFFER_UNDERFLOW with 0 bytes consumed.
3252+ * Exercises the inRemaining < TLS_RECORD_HEADER_LEN branch
3253+ * of peekTlsRecordHeader(). */
3254+ System .out .print ("\t Test unwrap partial header underflow" );
3255+
3256+ if (!enabledProtocols .contains ("TLSv1.3" )) {
3257+ System .out .println ("\t ... skipped" );
3258+ return ;
3259+ }
3260+
3261+ SSLContext ctx13 = tf .createSSLContext ("TLSv1.3" , engineProvider );
3262+ SSLEngine server = ctx13 .createSSLEngine ();
3263+ SSLEngine client = ctx13 .createSSLEngine ("localhost" , 11111 );
3264+
3265+ server .setUseClientMode (false );
3266+ server .setNeedClientAuth (false );
3267+ client .setUseClientMode (true );
3268+
3269+ server .beginHandshake ();
3270+ client .beginHandshake ();
3271+
3272+ int packetBufSize = client .getSession ().getPacketBufferSize ();
3273+ int appBufSize = client .getSession ().getApplicationBufferSize ();
3274+
3275+ /* Wrap ClientHello to put client into NEED_UNWRAP state */
3276+ ByteBuffer cliToSrv =
3277+ ByteBuffer .allocateDirect (packetBufSize );
3278+ ByteBuffer emptyApp = ByteBuffer .allocate (0 );
3279+ SSLEngineResult result = client .wrap (emptyApp , cliToSrv );
3280+ if (result .getStatus () != SSLEngineResult .Status .OK ) {
3281+ error ("\t ... failed" );
3282+ fail ("client wrap (ClientHello) failed: " +
3283+ result .getStatus ());
3284+ }
3285+
3286+ if (client .getHandshakeStatus () !=
3287+ SSLEngineResult .HandshakeStatus .NEED_UNWRAP ) {
3288+ error ("\t ... failed" );
3289+ fail ("expected NEED_UNWRAP after ClientHello, got: " +
3290+ client .getHandshakeStatus ());
3291+ }
3292+
3293+ /* Feed 2 bytes: less than the 5-byte TLS record header.
3294+ * Peek must signal BUFFER_UNDERFLOW without calling into
3295+ * wolfSSL, so 0 bytes are consumed. */
3296+ ByteBuffer partialHeader =
3297+ ByteBuffer .wrap (new byte [] { 0x16 , 0x03 });
3298+ ByteBuffer cliAppBuf =
3299+ ByteBuffer .allocateDirect (appBufSize );
3300+ result = client .unwrap (partialHeader , cliAppBuf );
3301+
3302+ if (result .getStatus () !=
3303+ SSLEngineResult .Status .BUFFER_UNDERFLOW ) {
3304+ error ("\t ... failed" );
3305+ fail ("expected BUFFER_UNDERFLOW for partial header, " +
3306+ "got: " + result .getStatus ());
3307+ }
3308+
3309+ if (result .bytesConsumed () != 0 ) {
3310+ error ("\t ... failed" );
3311+ fail ("BUFFER_UNDERFLOW must consume 0 bytes, " +
3312+ "consumed: " + result .bytesConsumed ());
3313+ }
3314+
3315+ /* Engine must remain in NEED_UNWRAP after underflow. */
3316+ if (result .getHandshakeStatus () !=
3317+ SSLEngineResult .HandshakeStatus .NEED_UNWRAP ) {
3318+ error ("\t ... failed" );
3319+ fail ("expected NEED_UNWRAP after underflow, got: " +
3320+ result .getHandshakeStatus ());
3321+ }
3322+
3323+ pass ("\t ... passed" );
3324+ }
3325+
3326+ @ Test
3327+ public void testHandshakeUnwrapOversizedRecord ()
3328+ throws NoSuchProviderException , NoSuchAlgorithmException ,
3329+ KeyManagementException , KeyStoreException ,
3330+ CertificateException , IOException ,
3331+ UnrecoverableKeyException {
3332+
3333+ /* Test that a partial record whose header claims
3334+ * recLen > MAX_RECORD_SIZE (but <= packetBufferSize - 5)
3335+ * returns BUFFER_UNDERFLOW with 0 bytes consumed. Prior to
3336+ * fixing the MAX_RECORD_SIZE cap, this range bypassed the
3337+ * peek entirely and wolfSSL would consume partial bytes. */
3338+ System .out .print ("\t Test unwrap oversized record underflow" );
3339+
3340+ if (!enabledProtocols .contains ("TLSv1.3" )) {
3341+ System .out .println ("\t ... skipped" );
3342+ return ;
3343+ }
3344+
3345+ SSLContext ctx13 = tf .createSSLContext ("TLSv1.3" , engineProvider );
3346+ SSLEngine server = ctx13 .createSSLEngine ();
3347+ SSLEngine client = ctx13 .createSSLEngine ("localhost" , 11111 );
3348+
3349+ server .setUseClientMode (false );
3350+ server .setNeedClientAuth (false );
3351+ client .setUseClientMode (true );
3352+
3353+ server .beginHandshake ();
3354+ client .beginHandshake ();
3355+
3356+ int packetBufSize = client .getSession ().getPacketBufferSize ();
3357+ int appBufSize = client .getSession ().getApplicationBufferSize ();
3358+
3359+ /* Wrap ClientHello to put client into NEED_UNWRAP state */
3360+ ByteBuffer cliToSrv =
3361+ ByteBuffer .allocateDirect (packetBufSize );
3362+ ByteBuffer emptyApp = ByteBuffer .allocate (0 );
3363+ SSLEngineResult result = client .wrap (emptyApp , cliToSrv );
3364+ if (result .getStatus () != SSLEngineResult .Status .OK ) {
3365+ error ("\t ... failed" );
3366+ fail ("client wrap (ClientHello) failed: " +
3367+ result .getStatus ());
3368+ }
3369+
3370+ if (client .getHandshakeStatus () !=
3371+ SSLEngineResult .HandshakeStatus .NEED_UNWRAP ) {
3372+ error ("\t ... failed" );
3373+ fail ("expected NEED_UNWRAP after ClientHello, got: " +
3374+ client .getHandshakeStatus ());
3375+ }
3376+
3377+ /* recLen = MAX_RECORD_SIZE + 1 must fit within
3378+ * packetBufferSize (header not counted). Skip if not,
3379+ * which would indicate an unusual build configuration. */
3380+ int oversizedRecLen = WolfSSL .MAX_RECORD_SIZE + 1 ;
3381+ if (oversizedRecLen > packetBufSize - 5 ) {
3382+ System .out .println ("\t ... skipped" );
3383+ return ;
3384+ }
3385+
3386+ /* Craft a 5-byte TLS record header: content type
3387+ * handshake(0x16), version TLS 1.2 record layer(0x0303),
3388+ * length = MAX_RECORD_SIZE + 1. Valid content-type and
3389+ * version bytes ensure the plausibility guard in
3390+ * peekTlsRecordHeader() does not filter the header before
3391+ * the length check. */
3392+ byte recLenHi = (byte ) ((oversizedRecLen >> 8 ) & 0xFF );
3393+ byte recLenLo = (byte ) (oversizedRecLen & 0xFF );
3394+ ByteBuffer fakeHeader = ByteBuffer .wrap (new byte [] {
3395+ 0x16 , 0x03 , 0x03 , recLenHi , recLenLo
3396+ });
3397+
3398+ ByteBuffer cliAppBuf =
3399+ ByteBuffer .allocateDirect (appBufSize );
3400+ result = client .unwrap (fakeHeader , cliAppBuf );
3401+
3402+ if (result .getStatus () !=
3403+ SSLEngineResult .Status .BUFFER_UNDERFLOW ) {
3404+ error ("\t ... failed" );
3405+ fail ("expected BUFFER_UNDERFLOW for oversized partial " +
3406+ "record, got: " + result .getStatus ());
3407+ }
3408+
3409+ if (result .bytesConsumed () != 0 ) {
3410+ error ("\t ... failed" );
3411+ fail ("BUFFER_UNDERFLOW must consume 0 bytes, " +
3412+ "consumed: " + result .bytesConsumed ());
3413+ }
3414+
3415+ /* Engine must remain in NEED_UNWRAP after underflow. */
3416+ if (result .getHandshakeStatus () !=
3417+ SSLEngineResult .HandshakeStatus .NEED_UNWRAP ) {
31633418 error ("\t ... failed" );
3164- fail ("unwrap() consumed 0 bytes from a non-empty " +
3165- "handshake buffer" );
3419+ fail ("expected NEED_UNWRAP after underflow, got: " +
3420+ result . getHandshakeStatus () );
31663421 }
31673422
31683423 pass ("\t ... passed" );
0 commit comments