|
26 | 26 | import com.wolfssl.provider.jsse.WolfSSLProvider; |
27 | 27 | import java.io.EOFException; |
28 | 28 | import java.io.IOException; |
| 29 | +import java.lang.reflect.Field; |
29 | 30 | import java.net.InetAddress; |
30 | 31 | import java.nio.ByteBuffer; |
31 | 32 | import java.nio.channels.ServerSocketChannel; |
@@ -3508,4 +3509,115 @@ public void testBufferOverflowSmallOutput() |
3508 | 3509 |
|
3509 | 3510 | pass("\t\t... passed"); |
3510 | 3511 | } |
| 3512 | + |
| 3513 | + @Test |
| 3514 | + public void testWrapPartialDrainOffsetUpdate() |
| 3515 | + throws NoSuchProviderException, NoSuchAlgorithmException, |
| 3516 | + KeyManagementException, KeyStoreException, |
| 3517 | + CertificateException, IOException, |
| 3518 | + UnrecoverableKeyException, |
| 3519 | + NoSuchFieldException, IllegalAccessException { |
| 3520 | + |
| 3521 | + /* CopyOutPacket() must decrement internalIOSendBufOffset after a |
| 3522 | + * partial drain, otherwise stale ciphertext gets re-emitted. */ |
| 3523 | + System.out.print("\tTesting wrap() partial drain offset"); |
| 3524 | + |
| 3525 | + this.ctx = tf.createSSLContext("TLS", engineProvider); |
| 3526 | + SSLEngine server = this.ctx.createSSLEngine(); |
| 3527 | + SSLEngine client = this.ctx.createSSLEngine("wolfSSL test", 11111); |
| 3528 | + |
| 3529 | + server.setUseClientMode(false); |
| 3530 | + server.setNeedClientAuth(false); |
| 3531 | + client.setUseClientMode(true); |
| 3532 | + |
| 3533 | + server.beginHandshake(); |
| 3534 | + client.beginHandshake(); |
| 3535 | + |
| 3536 | + int ret = tf.testConnection(server, client, null, null, |
| 3537 | + "partial drain test"); |
| 3538 | + if (ret != 0) { |
| 3539 | + error("\t... failed"); |
| 3540 | + fail("failed to create connection"); |
| 3541 | + } |
| 3542 | + |
| 3543 | + /* Inject marker of size packetSz + 1000 so first drain copies |
| 3544 | + * packetSz bytes and leaves 1000 bytes queued. */ |
| 3545 | + int packetSz = client.getSession().getPacketBufferSize(); |
| 3546 | + int queuedSz = packetSz + 1000; |
| 3547 | + byte[] marker = new byte[queuedSz]; |
| 3548 | + for (int i = 0; i < queuedSz; i++) { |
| 3549 | + marker[i] = (byte)((i * 31) & 0xFF); |
| 3550 | + } |
| 3551 | + |
| 3552 | + Field bufField = |
| 3553 | + WolfSSLEngine.class.getDeclaredField("internalIOSendBuf"); |
| 3554 | + Field bufSzField = |
| 3555 | + WolfSSLEngine.class.getDeclaredField("internalIOSendBufSz"); |
| 3556 | + Field offField = |
| 3557 | + WolfSSLEngine.class.getDeclaredField( |
| 3558 | + "internalIOSendBufOffset"); |
| 3559 | + bufField.setAccessible(true); |
| 3560 | + bufSzField.setAccessible(true); |
| 3561 | + offField.setAccessible(true); |
| 3562 | + |
| 3563 | + bufField.set(client, marker); |
| 3564 | + bufSzField.setInt(client, queuedSz); |
| 3565 | + offField.setInt(client, queuedSz); |
| 3566 | + |
| 3567 | + /* First wrap: out buffer sized exactly to packetBufferSize so |
| 3568 | + * the guard passes but CopyOutPacket partial-drains. */ |
| 3569 | + ByteBuffer empty = ByteBuffer.allocate(0); |
| 3570 | + ByteBuffer firstOut = ByteBuffer.allocate(packetSz); |
| 3571 | + SSLEngineResult result = client.wrap(empty, firstOut); |
| 3572 | + |
| 3573 | + if (result.bytesProduced() != packetSz) { |
| 3574 | + error("\t... failed"); |
| 3575 | + fail("first wrap expected " + packetSz + |
| 3576 | + " produced, got " + result.bytesProduced()); |
| 3577 | + } |
| 3578 | + |
| 3579 | + /* Fix-sensitive check: unfixed code leaves offset at queuedSz. */ |
| 3580 | + int offAfterFirst = offField.getInt(client); |
| 3581 | + if (offAfterFirst != queuedSz - packetSz) { |
| 3582 | + error("\t... failed"); |
| 3583 | + fail("internalIOSendBufOffset not decremented after " + |
| 3584 | + "partial drain: expected " + (queuedSz - packetSz) + |
| 3585 | + ", got " + offAfterFirst); |
| 3586 | + } |
| 3587 | + |
| 3588 | + /* Second wrap: must drain only the remainder; unfixed code |
| 3589 | + * would re-emit packetSz stale bytes instead. */ |
| 3590 | + ByteBuffer secondOut = ByteBuffer.allocate(packetSz); |
| 3591 | + result = client.wrap(empty, secondOut); |
| 3592 | + |
| 3593 | + int expectedRemainder = queuedSz - packetSz; |
| 3594 | + if (result.bytesProduced() != expectedRemainder) { |
| 3595 | + error("\t... failed"); |
| 3596 | + fail("second wrap expected " + expectedRemainder + |
| 3597 | + " produced (remainder only), got " + |
| 3598 | + result.bytesProduced() + |
| 3599 | + " - stale bytes re-sent after partial drain"); |
| 3600 | + } |
| 3601 | + |
| 3602 | + if (offField.getInt(client) != 0) { |
| 3603 | + error("\t... failed"); |
| 3604 | + fail("internalIOSendBufOffset not reset to 0 after " + |
| 3605 | + "remainder drained"); |
| 3606 | + } |
| 3607 | + |
| 3608 | + /* Full integrity check: concatenated drained output must equal |
| 3609 | + * the original injected marker exactly. */ |
| 3610 | + firstOut.flip(); |
| 3611 | + secondOut.flip(); |
| 3612 | + byte[] drained = new byte[queuedSz]; |
| 3613 | + firstOut.get(drained, 0, packetSz); |
| 3614 | + secondOut.get(drained, packetSz, expectedRemainder); |
| 3615 | + if (!Arrays.equals(marker, drained)) { |
| 3616 | + error("\t... failed"); |
| 3617 | + fail("drained output does not match injected queue"); |
| 3618 | + } |
| 3619 | + |
| 3620 | + pass("\t... passed"); |
| 3621 | + } |
| 3622 | + |
3511 | 3623 | } |
0 commit comments