diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java index 789133726..cad7a44e5 100644 --- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java +++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java @@ -929,35 +929,46 @@ public void test_SSLEngine_setSSLParameters() throws Exception { c.close(); } + /* + * The preconditions for the wrap() methods of SSLEngine have a relatively + * complex mapping from precondition to the exception thrown on failure. This method + * checks that all the failure cases throw the correct exception and that + * corner cases which should not throw are also are handled correctly. + */ @Test public void wrapPreconditions() throws Exception { int bufferSize = 128; int arrayLength = 5; - ByteBuffer buffer = ByteBuffer.allocate(bufferSize); - ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer(); + ByteBuffer destBuffer = ByteBuffer.allocate(bufferSize); + ByteBuffer readOnlyDestBuffer = destBuffer.asReadOnlyBuffer(); ByteBuffer[] buffers = BufferType.HEAP.newRandomBufferArray(arrayLength, bufferSize); + for (int i = 0; i < arrayLength; i++) { + buffers[i] = buffers[i].asReadOnlyBuffer(); + } ByteBuffer[] buffersWithNullEntry = Arrays.copyOf(buffers, buffers.length); int nullBufferIndex = 2; buffersWithNullEntry[nullBufferIndex] = null; + // Failure cases // Client/server mode not set => IllegalStateException - assertThrows( - IllegalStateException.class, () -> newUnconnectedEngine().wrap(buffer, buffer)); - assertThrows( - IllegalStateException.class, () -> newUnconnectedEngine().wrap(buffers, buffer)); assertThrows(IllegalStateException.class, - () -> newUnconnectedEngine().wrap(buffers, 0, 1, buffer)); + () -> newUnconnectedEngine().wrap(buffers[0], destBuffer)); + assertThrows(IllegalStateException.class, + () -> newUnconnectedEngine().wrap(buffers, destBuffer)); + assertThrows(IllegalStateException.class, + () -> newUnconnectedEngine().wrap(buffers, 0, 1, destBuffer)); // Read-only destination => ReadOnlyBufferException assertThrows(ReadOnlyBufferException.class, - () -> newConnectedEngine().wrap(buffer, readOnlyBuffer)); + () -> newConnectedEngine().wrap(buffers[0], readOnlyDestBuffer)); assertThrows(ReadOnlyBufferException.class, - () -> newConnectedEngine().wrap(buffers, readOnlyBuffer)); + () -> newConnectedEngine().wrap(buffers, readOnlyDestBuffer)); assertThrows(ReadOnlyBufferException.class, - () -> newConnectedEngine().wrap(buffers, 0, arrayLength, readOnlyBuffer)); + () -> newConnectedEngine().wrap(buffers, 0, arrayLength, readOnlyDestBuffer)); // Null destination => IllegalArgumentException - assertThrows(IllegalArgumentException.class, () -> newConnectedEngine().wrap(buffer, null)); + assertThrows( + IllegalArgumentException.class, () -> newConnectedEngine().wrap(buffers[0], null)); assertThrows( IllegalArgumentException.class, () -> newConnectedEngine().wrap(buffers, null)); assertThrows(IllegalArgumentException.class, @@ -965,32 +976,61 @@ public void wrapPreconditions() throws Exception { // Null source => IllegalArgumentException assertThrows(IllegalArgumentException.class, - () -> newConnectedEngine().wrap((ByteBuffer) null, buffer)); + () -> newConnectedEngine().wrap((ByteBuffer) null, destBuffer)); assertThrows(IllegalArgumentException.class, - () -> newConnectedEngine().wrap((ByteBuffer[]) null, buffer)); + () -> newConnectedEngine().wrap((ByteBuffer[]) null, destBuffer)); assertThrows(IllegalArgumentException.class, - () -> newConnectedEngine().wrap(null, 0, 1, buffer)); + () -> newConnectedEngine().wrap(null, 0, 1, destBuffer)); // Null entries in buffer array => IllegalArgumentException assertThrows(IllegalArgumentException.class, - () -> newConnectedEngine().wrap(buffersWithNullEntry, buffer)); + () -> newConnectedEngine().wrap(buffersWithNullEntry, destBuffer)); assertThrows(IllegalArgumentException.class, - () -> newConnectedEngine().wrap(buffersWithNullEntry, 0, arrayLength, buffer)); - // But not if they are outside the selected offset and length - newConnectedEngine().wrap(buffersWithNullEntry, 0, nullBufferIndex, buffer); - newConnectedEngine().wrap(buffersWithNullEntry, nullBufferIndex + 1, 1, buffer); + () -> newConnectedEngine().wrap(buffersWithNullEntry, 0, arrayLength, destBuffer)); // Bad offset or length => IndexOutOfBoundsException assertThrows(IndexOutOfBoundsException.class, - () -> newConnectedEngine().wrap(buffers, 0, arrayLength + 1, buffer)); + () -> newConnectedEngine().wrap(buffers, 0, arrayLength + 1, destBuffer)); assertThrows(IndexOutOfBoundsException.class, - () -> newConnectedEngine().wrap(buffers, arrayLength, 1, buffer)); + () -> newConnectedEngine().wrap(buffers, arrayLength, 1, destBuffer)); assertThrows(IndexOutOfBoundsException.class, - () -> newConnectedEngine().wrap(buffers, arrayLength - 1, 2, buffer)); - newConnectedEngine().wrap(buffers, 0, arrayLength, buffer); - // Zero length array is allowed - newConnectedEngine().wrap(buffers, 0, 0, buffer); - newConnectedEngine().wrap(buffers, arrayLength, 0, buffer); + () -> newConnectedEngine().wrap(buffers, arrayLength - 1, 2, destBuffer)); + + // Corner cases which should not throw + // Null entries should not throw if they are outside the selected offset and length + assertWrapSucceeds(buffersWithNullEntry, 0, nullBufferIndex); + assertWrapSucceeds(buffersWithNullEntry, nullBufferIndex + 1, 1); + + // Zero length arrays of input buffers should not throw + assertWrapSucceeds(buffers, 0, 0); + assertWrapSucceeds(buffers, arrayLength, 0); + } + + // Asserts that a wrap call with the given arguments succeeds and wraps the expected + // amount of data. + private void assertWrapSucceeds(ByteBuffer[] buffers, int offset, int length) throws Exception { + try (TestSSLEnginePair pair = TestSSLEnginePair.create()) { + assertConnected(pair); + + // Reset the selected buffers to their initial (unread) state and calculate the + // total amount of data that will be wrapped. + int dataSize = 0; + for (int i = offset; i < offset + length; i++) { + buffers[i].position(0); + dataSize += buffers[i].remaining(); + } + + // Ensure the data will fit into one TLS record so that only a single wrap is needed. + assertTrue(dataSize <= pair.client.getSession().getApplicationBufferSize()); + ByteBuffer ciphertext = + ByteBuffer.allocate(pair.client.getSession().getPacketBufferSize()); + + // Wrap the data and ensure the expected amount of data was consumed. + SSLEngineResult result = pair.client.wrap(buffers, offset, length, ciphertext); + assertEquals(Status.OK, result.getStatus()); + assertEquals(dataSize, result.bytesConsumed()); + // This method is only used for checking wrap preconditions so no need to unwrap. + } } @Test