|
|
@@ -26,14 +26,16 @@ import java.security.SecureRandom;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
|
import java.util.List;
|
|
|
-import java.util.function.Supplier;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
+import java.util.function.IntFunction;
|
|
|
|
|
|
public class SSLDriverTests extends ESTestCase {
|
|
|
|
|
|
- private final Supplier<Page> pageSupplier = () -> new Page(ByteBuffer.allocate(1 << 14), () -> {});
|
|
|
- private InboundChannelBuffer serverBuffer = new InboundChannelBuffer(pageSupplier);
|
|
|
- private InboundChannelBuffer clientBuffer = new InboundChannelBuffer(pageSupplier);
|
|
|
- private InboundChannelBuffer genericBuffer = new InboundChannelBuffer(pageSupplier);
|
|
|
+ private final IntFunction<Page> pageAllocator = (n) -> new Page(ByteBuffer.allocate(n), () -> {});
|
|
|
+
|
|
|
+ private final InboundChannelBuffer networkReadBuffer = new InboundChannelBuffer(pageAllocator);
|
|
|
+ private final InboundChannelBuffer applicationBuffer = new InboundChannelBuffer(pageAllocator);
|
|
|
+ private final AtomicInteger openPages = new AtomicInteger(0);
|
|
|
|
|
|
public void testPingPongAndClose() throws Exception {
|
|
|
SSLContext sslContext = getSSLContext();
|
|
|
@@ -44,19 +46,36 @@ public class SSLDriverTests extends ESTestCase {
|
|
|
handshake(clientDriver, serverDriver);
|
|
|
|
|
|
ByteBuffer[] buffers = {ByteBuffer.wrap("ping".getBytes(StandardCharsets.UTF_8))};
|
|
|
- sendAppData(clientDriver, serverDriver, buffers);
|
|
|
- serverDriver.read(serverBuffer);
|
|
|
- assertEquals(ByteBuffer.wrap("ping".getBytes(StandardCharsets.UTF_8)), serverBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ sendAppData(clientDriver, buffers);
|
|
|
+ serverDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
+ assertEquals(ByteBuffer.wrap("ping".getBytes(StandardCharsets.UTF_8)), applicationBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ applicationBuffer.release(4);
|
|
|
|
|
|
ByteBuffer[] buffers2 = {ByteBuffer.wrap("pong".getBytes(StandardCharsets.UTF_8))};
|
|
|
- sendAppData(serverDriver, clientDriver, buffers2);
|
|
|
- clientDriver.read(clientBuffer);
|
|
|
- assertEquals(ByteBuffer.wrap("pong".getBytes(StandardCharsets.UTF_8)), clientBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ sendAppData(serverDriver, buffers2);
|
|
|
+ clientDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
+ assertEquals(ByteBuffer.wrap("pong".getBytes(StandardCharsets.UTF_8)), applicationBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ applicationBuffer.release(4);
|
|
|
|
|
|
assertFalse(clientDriver.needsNonApplicationWrite());
|
|
|
normalClose(clientDriver, serverDriver);
|
|
|
}
|
|
|
|
|
|
+ public void testDataStoredInOutboundBufferIsClosed() throws Exception {
|
|
|
+ SSLContext sslContext = getSSLContext();
|
|
|
+
|
|
|
+ SSLDriver clientDriver = getDriver(sslContext.createSSLEngine(), true);
|
|
|
+ SSLDriver serverDriver = getDriver(sslContext.createSSLEngine(), false);
|
|
|
+
|
|
|
+ handshake(clientDriver, serverDriver);
|
|
|
+
|
|
|
+ ByteBuffer[] buffers = {ByteBuffer.wrap("ping".getBytes(StandardCharsets.UTF_8))};
|
|
|
+ serverDriver.write(new FlushOperation(buffers, (v, e) -> {}));
|
|
|
+
|
|
|
+ expectThrows(SSLException.class, serverDriver::close);
|
|
|
+ assertEquals(0, openPages.get());
|
|
|
+ }
|
|
|
+
|
|
|
public void testRenegotiate() throws Exception {
|
|
|
SSLContext sslContext = getSSLContext();
|
|
|
|
|
|
@@ -73,9 +92,10 @@ public class SSLDriverTests extends ESTestCase {
|
|
|
handshake(clientDriver, serverDriver);
|
|
|
|
|
|
ByteBuffer[] buffers = {ByteBuffer.wrap("ping".getBytes(StandardCharsets.UTF_8))};
|
|
|
- sendAppData(clientDriver, serverDriver, buffers);
|
|
|
- serverDriver.read(serverBuffer);
|
|
|
- assertEquals(ByteBuffer.wrap("ping".getBytes(StandardCharsets.UTF_8)), serverBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ sendAppData(clientDriver, buffers);
|
|
|
+ serverDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
+ assertEquals(ByteBuffer.wrap("ping".getBytes(StandardCharsets.UTF_8)), applicationBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ applicationBuffer.release(4);
|
|
|
|
|
|
clientDriver.renegotiate();
|
|
|
assertTrue(clientDriver.isHandshaking());
|
|
|
@@ -83,17 +103,20 @@ public class SSLDriverTests extends ESTestCase {
|
|
|
|
|
|
// This tests that the client driver can still receive data based on the prior handshake
|
|
|
ByteBuffer[] buffers2 = {ByteBuffer.wrap("pong".getBytes(StandardCharsets.UTF_8))};
|
|
|
- sendAppData(serverDriver, clientDriver, buffers2);
|
|
|
- clientDriver.read(clientBuffer);
|
|
|
- assertEquals(ByteBuffer.wrap("pong".getBytes(StandardCharsets.UTF_8)), clientBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ sendAppData(serverDriver, buffers2);
|
|
|
+ clientDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
+ assertEquals(ByteBuffer.wrap("pong".getBytes(StandardCharsets.UTF_8)), applicationBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ applicationBuffer.release(4);
|
|
|
|
|
|
handshake(clientDriver, serverDriver, true);
|
|
|
- sendAppData(clientDriver, serverDriver, buffers);
|
|
|
- serverDriver.read(serverBuffer);
|
|
|
- assertEquals(ByteBuffer.wrap("ping".getBytes(StandardCharsets.UTF_8)), serverBuffer.sliceBuffersTo(4)[0]);
|
|
|
- sendAppData(serverDriver, clientDriver, buffers2);
|
|
|
- clientDriver.read(clientBuffer);
|
|
|
- assertEquals(ByteBuffer.wrap("pong".getBytes(StandardCharsets.UTF_8)), clientBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ sendAppData(clientDriver, buffers);
|
|
|
+ serverDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
+ assertEquals(ByteBuffer.wrap("ping".getBytes(StandardCharsets.UTF_8)), applicationBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ applicationBuffer.release(4);
|
|
|
+ sendAppData(serverDriver, buffers2);
|
|
|
+ clientDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
+ assertEquals(ByteBuffer.wrap("pong".getBytes(StandardCharsets.UTF_8)), applicationBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ applicationBuffer.release(4);
|
|
|
|
|
|
normalClose(clientDriver, serverDriver);
|
|
|
}
|
|
|
@@ -108,18 +131,22 @@ public class SSLDriverTests extends ESTestCase {
|
|
|
|
|
|
ByteBuffer buffer = ByteBuffer.allocate(1 << 15);
|
|
|
for (int i = 0; i < (1 << 15); ++i) {
|
|
|
- buffer.put((byte) i);
|
|
|
+ buffer.put((byte) (i % 127));
|
|
|
}
|
|
|
+ buffer.flip();
|
|
|
ByteBuffer[] buffers = {buffer};
|
|
|
- sendAppData(clientDriver, serverDriver, buffers);
|
|
|
- serverDriver.read(serverBuffer);
|
|
|
- assertEquals(16384, serverBuffer.sliceBuffersFrom(0)[0].limit());
|
|
|
- assertEquals(16384, serverBuffer.sliceBuffersFrom(0)[1].limit());
|
|
|
+ sendAppData(clientDriver, buffers);
|
|
|
+ serverDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
+ ByteBuffer[] buffers1 = applicationBuffer.sliceBuffersFrom(0);
|
|
|
+ assertEquals((byte) (16383 % 127), buffers1[0].get(16383));
|
|
|
+ assertEquals((byte) (32767 % 127), buffers1[1].get(16383));
|
|
|
+ applicationBuffer.release(1 << 15);
|
|
|
|
|
|
ByteBuffer[] buffers2 = {ByteBuffer.wrap("pong".getBytes(StandardCharsets.UTF_8))};
|
|
|
- sendAppData(serverDriver, clientDriver, buffers2);
|
|
|
- clientDriver.read(clientBuffer);
|
|
|
- assertEquals(ByteBuffer.wrap("pong".getBytes(StandardCharsets.UTF_8)), clientBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ sendAppData(serverDriver, buffers2);
|
|
|
+ clientDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
+ assertEquals(ByteBuffer.wrap("pong".getBytes(StandardCharsets.UTF_8)), applicationBuffer.sliceBuffersTo(4)[0]);
|
|
|
+ applicationBuffer.release(4);
|
|
|
|
|
|
assertFalse(clientDriver.needsNonApplicationWrite());
|
|
|
normalClose(clientDriver, serverDriver);
|
|
|
@@ -193,16 +220,16 @@ public class SSLDriverTests extends ESTestCase {
|
|
|
serverDriver.initiateClose();
|
|
|
assertTrue(serverDriver.needsNonApplicationWrite());
|
|
|
assertFalse(serverDriver.isClosed());
|
|
|
- sendNonApplicationWrites(serverDriver, clientDriver);
|
|
|
+ sendNonApplicationWrites(serverDriver);
|
|
|
// We are immediately fully closed due to SSLEngine inconsistency
|
|
|
assertTrue(serverDriver.isClosed());
|
|
|
- // This should not throw exception yet as the SSLEngine will not UNWRAP data while attempting to WRAP
|
|
|
- clientDriver.read(clientBuffer);
|
|
|
- sendNonApplicationWrites(clientDriver, serverDriver);
|
|
|
- clientDriver.read(clientBuffer);
|
|
|
- sendNonApplicationWrites(clientDriver, serverDriver);
|
|
|
- serverDriver.read(serverBuffer);
|
|
|
+
|
|
|
+ SSLException sslException = expectThrows(SSLException.class, () -> clientDriver.read(networkReadBuffer, applicationBuffer));
|
|
|
+ assertEquals("Received close_notify during handshake", sslException.getMessage());
|
|
|
+ sendNonApplicationWrites(clientDriver);
|
|
|
assertTrue(clientDriver.isClosed());
|
|
|
+
|
|
|
+ serverDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
}
|
|
|
|
|
|
public void testCloseDuringHandshakePreJDK11() throws Exception {
|
|
|
@@ -226,17 +253,17 @@ public class SSLDriverTests extends ESTestCase {
|
|
|
serverDriver.initiateClose();
|
|
|
assertTrue(serverDriver.needsNonApplicationWrite());
|
|
|
assertFalse(serverDriver.isClosed());
|
|
|
- sendNonApplicationWrites(serverDriver, clientDriver);
|
|
|
+ sendNonApplicationWrites(serverDriver);
|
|
|
// We are immediately fully closed due to SSLEngine inconsistency
|
|
|
assertTrue(serverDriver.isClosed());
|
|
|
// This should not throw exception yet as the SSLEngine will not UNWRAP data while attempting to WRAP
|
|
|
- clientDriver.read(clientBuffer);
|
|
|
- sendNonApplicationWrites(clientDriver, serverDriver);
|
|
|
- SSLException sslException = expectThrows(SSLException.class, () -> clientDriver.read(clientBuffer));
|
|
|
+ clientDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
+ sendNonApplicationWrites(clientDriver);
|
|
|
+ SSLException sslException = expectThrows(SSLException.class, () -> clientDriver.read(networkReadBuffer, applicationBuffer));
|
|
|
assertEquals("Received close_notify during handshake", sslException.getMessage());
|
|
|
assertTrue(clientDriver.needsNonApplicationWrite());
|
|
|
- sendNonApplicationWrites(clientDriver, serverDriver);
|
|
|
- serverDriver.read(serverBuffer);
|
|
|
+ sendNonApplicationWrites(clientDriver);
|
|
|
+ serverDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
assertTrue(clientDriver.isClosed());
|
|
|
}
|
|
|
|
|
|
@@ -244,11 +271,11 @@ public class SSLDriverTests extends ESTestCase {
|
|
|
assertTrue(sendDriver.needsNonApplicationWrite());
|
|
|
assertFalse(sendDriver.isClosed());
|
|
|
|
|
|
- sendNonApplicationWrites(sendDriver, receiveDriver);
|
|
|
+ sendNonApplicationWrites(sendDriver);
|
|
|
assertTrue(sendDriver.isClosed());
|
|
|
sendDriver.close();
|
|
|
|
|
|
- SSLException sslException = expectThrows(SSLException.class, () -> receiveDriver.read(genericBuffer));
|
|
|
+ SSLException sslException = expectThrows(SSLException.class, () -> receiveDriver.read(networkReadBuffer, applicationBuffer));
|
|
|
assertTrue("Expected one of the following exception messages: " + messages + ". Found: " + sslException.getMessage(),
|
|
|
messages.stream().anyMatch(m -> sslException.getMessage().equals(m)));
|
|
|
if (receiveDriver.needsNonApplicationWrite() == false) {
|
|
|
@@ -277,29 +304,30 @@ public class SSLDriverTests extends ESTestCase {
|
|
|
sendDriver.initiateClose();
|
|
|
assertFalse(sendDriver.readyForApplicationWrites());
|
|
|
assertTrue(sendDriver.needsNonApplicationWrite());
|
|
|
- sendNonApplicationWrites(sendDriver, receiveDriver);
|
|
|
+ sendNonApplicationWrites(sendDriver);
|
|
|
assertFalse(sendDriver.isClosed());
|
|
|
|
|
|
- receiveDriver.read(genericBuffer);
|
|
|
+ receiveDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
assertFalse(receiveDriver.isClosed());
|
|
|
|
|
|
assertFalse(receiveDriver.readyForApplicationWrites());
|
|
|
assertTrue(receiveDriver.needsNonApplicationWrite());
|
|
|
- sendNonApplicationWrites(receiveDriver, sendDriver);
|
|
|
+ sendNonApplicationWrites(receiveDriver);
|
|
|
assertTrue(receiveDriver.isClosed());
|
|
|
|
|
|
- sendDriver.read(genericBuffer);
|
|
|
+ sendDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
assertTrue(sendDriver.isClosed());
|
|
|
|
|
|
sendDriver.close();
|
|
|
receiveDriver.close();
|
|
|
+ assertEquals(0, openPages.get());
|
|
|
}
|
|
|
|
|
|
- private void sendNonApplicationWrites(SSLDriver sendDriver, SSLDriver receiveDriver) throws SSLException {
|
|
|
+ private void sendNonApplicationWrites(SSLDriver sendDriver) throws SSLException {
|
|
|
SSLOutboundBuffer outboundBuffer = sendDriver.getOutboundBuffer();
|
|
|
while (sendDriver.needsNonApplicationWrite() || outboundBuffer.hasEncryptedBytesToFlush()) {
|
|
|
if (outboundBuffer.hasEncryptedBytesToFlush()) {
|
|
|
- sendData(outboundBuffer.buildNetworkFlushOperation(), receiveDriver);
|
|
|
+ sendData(outboundBuffer.buildNetworkFlushOperation());
|
|
|
} else {
|
|
|
sendDriver.nonApplicationWrite();
|
|
|
}
|
|
|
@@ -345,8 +373,8 @@ public class SSLDriverTests extends ESTestCase {
|
|
|
|
|
|
while (sendDriver.needsNonApplicationWrite() || outboundBuffer.hasEncryptedBytesToFlush()) {
|
|
|
if (outboundBuffer.hasEncryptedBytesToFlush()) {
|
|
|
- sendData(outboundBuffer.buildNetworkFlushOperation(), receiveDriver);
|
|
|
- receiveDriver.read(genericBuffer);
|
|
|
+ sendData(outboundBuffer.buildNetworkFlushOperation());
|
|
|
+ receiveDriver.read(networkReadBuffer, applicationBuffer);
|
|
|
} else {
|
|
|
sendDriver.nonApplicationWrite();
|
|
|
}
|
|
|
@@ -356,37 +384,46 @@ public class SSLDriverTests extends ESTestCase {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void sendAppData(SSLDriver sendDriver, SSLDriver receiveDriver, ByteBuffer[] message) throws IOException {
|
|
|
+ private void sendAppData(SSLDriver sendDriver, ByteBuffer[] message) throws IOException {
|
|
|
assertFalse(sendDriver.needsNonApplicationWrite());
|
|
|
|
|
|
- int bytesToEncrypt = Arrays.stream(message).mapToInt(Buffer::remaining).sum();
|
|
|
- SSLOutboundBuffer outboundBuffer = sendDriver.getOutboundBuffer();
|
|
|
FlushOperation flushOperation = new FlushOperation(message, (r, l) -> {});
|
|
|
|
|
|
- int bytesEncrypted = 0;
|
|
|
- while (bytesToEncrypt > bytesEncrypted) {
|
|
|
- bytesEncrypted += sendDriver.write(flushOperation);
|
|
|
- sendData(outboundBuffer.buildNetworkFlushOperation(), receiveDriver);
|
|
|
+ while (flushOperation.isFullyFlushed() == false) {
|
|
|
+ sendDriver.write(flushOperation);
|
|
|
}
|
|
|
+ sendData(sendDriver.getOutboundBuffer().buildNetworkFlushOperation());
|
|
|
}
|
|
|
|
|
|
- private void sendData(FlushOperation flushOperation, SSLDriver receiveDriver) {
|
|
|
- ByteBuffer readBuffer = receiveDriver.getNetworkReadBuffer();
|
|
|
+ private void sendData(FlushOperation flushOperation) {
|
|
|
ByteBuffer[] writeBuffers = flushOperation.getBuffersToWrite();
|
|
|
- int bytesToEncrypt = Arrays.stream(writeBuffers).mapToInt(Buffer::remaining).sum();
|
|
|
- assert bytesToEncrypt < readBuffer.capacity() : "Flush operation must be less that read buffer";
|
|
|
+ int bytesToCopy = Arrays.stream(writeBuffers).mapToInt(Buffer::remaining).sum();
|
|
|
+ networkReadBuffer.ensureCapacity(bytesToCopy + networkReadBuffer.getIndex());
|
|
|
+ ByteBuffer[] byteBuffers = networkReadBuffer.sliceBuffersFrom(0);
|
|
|
assert writeBuffers.length > 0 : "No write buffers";
|
|
|
|
|
|
- for (ByteBuffer writeBuffer : writeBuffers) {
|
|
|
- int written = writeBuffer.remaining();
|
|
|
+ int r = 0;
|
|
|
+ while (flushOperation.isFullyFlushed() == false) {
|
|
|
+ ByteBuffer readBuffer = byteBuffers[r];
|
|
|
+ ByteBuffer writeBuffer = flushOperation.getBuffersToWrite()[0];
|
|
|
+ int toWrite = Math.min(writeBuffer.remaining(), readBuffer.remaining());
|
|
|
+ writeBuffer.limit(writeBuffer.position() + toWrite);
|
|
|
readBuffer.put(writeBuffer);
|
|
|
- flushOperation.incrementIndex(written);
|
|
|
+ flushOperation.incrementIndex(toWrite);
|
|
|
+ if (readBuffer.remaining() == 0) {
|
|
|
+ r++;
|
|
|
+ }
|
|
|
}
|
|
|
+ networkReadBuffer.incrementIndex(bytesToCopy);
|
|
|
|
|
|
assertTrue(flushOperation.isFullyFlushed());
|
|
|
+ flushOperation.getListener().accept(null, null);
|
|
|
}
|
|
|
|
|
|
private SSLDriver getDriver(SSLEngine engine, boolean isClient) {
|
|
|
- return new SSLDriver(engine, isClient);
|
|
|
+ return new SSLDriver(engine, (n) -> {
|
|
|
+ openPages.incrementAndGet();
|
|
|
+ return new Page(ByteBuffer.allocate(n), openPages::decrementAndGet);
|
|
|
+ }, isClient);
|
|
|
}
|
|
|
}
|