Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correctly calculate the produced bytes in all cases when calling Refe… #10063

Merged
merged 4 commits into from
Feb 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -865,14 +865,18 @@ public final SSLEngineResult wrap(
bytesWritten = writePlaintextData(src, min(remaining, availableCapacityForWrap));
}

// Determine how much encrypted data was generated.
//
// Even if SSL_write doesn't consume any application data it is possible that OpenSSL will
// produce non-application data into the BIO. For example session tickets....
// See https://github.com/netty/netty/issues/10041
final int pendingNow = SSL.bioLengthByteBuffer(networkBIO);
bytesProduced += bioLengthBefore - pendingNow;
bioLengthBefore = pendingNow;

if (bytesWritten > 0) {
bytesConsumed += bytesWritten;

// Determine how much encrypted data was generated:
final int pendingNow = SSL.bioLengthByteBuffer(networkBIO);
bytesProduced += bioLengthBefore - pendingNow;
bioLengthBefore = pendingNow;

if (jdkCompatibilityMode || bytesProduced == dst.remaining()) {
return newResultMayFinishHandshake(status, bytesConsumed, bytesProduced);
}
Expand Down
113 changes: 113 additions & 0 deletions handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.CodecException;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.UnsupportedMessageTypeException;
Expand All @@ -57,12 +58,14 @@
import io.netty.util.concurrent.ImmediateEventExecutor;
import io.netty.util.concurrent.ImmediateExecutor;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.PlatformDependent;
import org.hamcrest.CoreMatchers;
import org.junit.Test;

import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
Expand Down Expand Up @@ -1067,4 +1070,114 @@ protected void initChannel(Channel ch) {
ReferenceCountUtil.release(sslClientCtx);
}
}

@Test(timeout = 5000L)
public void testSessionTicketsWithTLSv12() throws Throwable {
testSessionTickets(SslUtils.PROTOCOL_TLS_V1_2);
}

@Test(timeout = 5000L)
public void testSessionTicketsWithTLSv13() throws Throwable {
assumeTrue(OpenSsl.isTlsv13Supported());
testSessionTickets(SslUtils.PROTOCOL_TLS_V1_3);
}

private static void testSessionTickets(String protocol) throws Throwable {
assumeTrue(OpenSsl.isAvailable());
final SslContext sslClientCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(SslProvider.OPENSSL)
.protocols(protocol)
.build();

final SelfSignedCertificate cert = new SelfSignedCertificate();
final SslContext sslServerCtx = SslContextBuilder.forServer(cert.key(), cert.cert())
.sslProvider(SslProvider.OPENSSL)
.protocols(protocol)
.build();

OpenSslSessionTicketKey key = new OpenSslSessionTicketKey(new byte[OpenSslSessionTicketKey.NAME_SIZE],
new byte[OpenSslSessionTicketKey.HMAC_KEY_SIZE], new byte[OpenSslSessionTicketKey.AES_KEY_SIZE]);
((OpenSslSessionContext) sslClientCtx.sessionContext()).setTicketKeys(key);
((OpenSslSessionContext) sslServerCtx.sessionContext()).setTicketKeys(key);

EventLoopGroup group = new NioEventLoopGroup();
Channel sc = null;
Channel cc = null;
final SslHandler clientSslHandler = sslClientCtx.newHandler(UnpooledByteBufAllocator.DEFAULT);
final SslHandler serverSslHandler = sslServerCtx.newHandler(UnpooledByteBufAllocator.DEFAULT);

final BlockingQueue<Object> queue = new LinkedBlockingQueue<Object>();
final byte[] bytes = new byte[96];
PlatformDependent.threadLocalRandom().nextBytes(bytes);
try {
sc = new ServerBootstrap()
.group(group)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) {
ch.pipeline().addLast(serverSslHandler);
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
if (evt instanceof SslHandshakeCompletionEvent) {
ctx.writeAndFlush(Unpooled.wrappedBuffer(bytes));
}
}
});
}
})
.bind(new InetSocketAddress(0)).syncUninterruptibly().channel();

ChannelFuture future = new Bootstrap()
.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) {
ch.pipeline().addLast(clientSslHandler);
ch.pipeline().addLast(new ByteToMessageDecoder() {

@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
if (in.readableBytes() == bytes.length) {
queue.add(in.readBytes(bytes.length));
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
queue.add(cause);
}
});
}
}).connect(sc.localAddress());
cc = future.syncUninterruptibly().channel();

assertTrue(clientSslHandler.handshakeFuture().await().isSuccess());
assertTrue(serverSslHandler.handshakeFuture().await().isSuccess());
Object obj = queue.take();
if (obj instanceof ByteBuf) {
ByteBuf buffer = (ByteBuf) obj;
ByteBuf expected = Unpooled.wrappedBuffer(bytes);
try {
assertEquals(expected, buffer);
} finally {
expected.release();
}
} else {
throw (Throwable) obj;
}
} finally {
if (cc != null) {
cc.close().syncUninterruptibly();
}
if (sc != null) {
sc.close().syncUninterruptibly();
}
group.shutdownGracefully();
ReferenceCountUtil.release(sslClientCtx);
}
}
}