OpenSslEngine option to wrap/unwrap multiple packets per call
Motivation: The JDK SSLEngine documentation says that a call to wrap/unwrap "will attempt to consume one complete SSL/TLS network packet" [1]. This limitation can result in thrashing in the pipeline to decode and encode data that may be spread amongst multiple SSL/TLS network packets. ReferenceCountedOpenSslEngine also does not correct account for the overhead introduced by each individual SSL_write call if there are multiple ByteBuffers passed to the wrap() method. Modifications: - OpenSslEngine and SslHandler supports a mode to not comply with the limitation to only deal with a single SSL/TLS network packet per call - ReferenceCountedOpenSslEngine correctly accounts for the overhead of each call to SSL_write - SslHandler shouldn't cache maxPacketBufferSize as aggressively because this value may change before/after the handshake. Result: OpenSslEngine and SslHanadler can handle multiple SSL/TLS network packet per call. [1] https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html
This commit is contained in:
parent
df568c739e
commit
f7b3caeddc
@ -45,8 +45,8 @@ public abstract class OpenSslContext extends ReferenceCountedOpenSslContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
final SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort) {
|
final SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort, boolean jdkCompatibilityMode) {
|
||||||
return new OpenSslEngine(this, alloc, peerHost, peerPort);
|
return new OpenSslEngine(this, alloc, peerHost, peerPort, jdkCompatibilityMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -27,8 +27,9 @@ import javax.net.ssl.SSLEngine;
|
|||||||
* and manually release the native memory see {@link ReferenceCountedOpenSslEngine}.
|
* and manually release the native memory see {@link ReferenceCountedOpenSslEngine}.
|
||||||
*/
|
*/
|
||||||
public final class OpenSslEngine extends ReferenceCountedOpenSslEngine {
|
public final class OpenSslEngine extends ReferenceCountedOpenSslEngine {
|
||||||
OpenSslEngine(OpenSslContext context, ByteBufAllocator alloc, String peerHost, int peerPort) {
|
OpenSslEngine(OpenSslContext context, ByteBufAllocator alloc, String peerHost, int peerPort,
|
||||||
super(context, alloc, peerHost, peerPort, false);
|
boolean jdkCompatibilityMode) {
|
||||||
|
super(context, alloc, peerHost, peerPort, jdkCompatibilityMode, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -409,11 +409,21 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
|
public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
|
||||||
return newEngine0(alloc, peerHost, peerPort);
|
return newEngine0(alloc, peerHost, peerPort, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort) {
|
@Override
|
||||||
return new ReferenceCountedOpenSslEngine(this, alloc, peerHost, peerPort, true);
|
final SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) {
|
||||||
|
return new SslHandler(newEngine0(alloc, null, -1, false), startTls, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) {
|
||||||
|
return new SslHandler(newEngine0(alloc, peerHost, peerPort, false), startTls, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort, boolean jdkCompatibilityMode) {
|
||||||
|
return new ReferenceCountedOpenSslEngine(this, alloc, peerHost, peerPort, jdkCompatibilityMode, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract OpenSslKeyMaterialManager keyMaterialManager();
|
abstract OpenSslKeyMaterialManager keyMaterialManager();
|
||||||
|
@ -43,8 +43,8 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||||
|
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLEngineResult;
|
import javax.net.ssl.SSLEngineResult;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
@ -58,10 +58,10 @@ import javax.net.ssl.SSLSessionContext;
|
|||||||
import javax.security.cert.X509Certificate;
|
import javax.security.cert.X509Certificate;
|
||||||
|
|
||||||
import static io.netty.handler.ssl.OpenSsl.memoryAddress;
|
import static io.netty.handler.ssl.OpenSsl.memoryAddress;
|
||||||
import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH;
|
|
||||||
import static io.netty.util.internal.EmptyArrays.EMPTY_CERTIFICATES;
|
import static io.netty.util.internal.EmptyArrays.EMPTY_CERTIFICATES;
|
||||||
import static io.netty.util.internal.EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES;
|
import static io.netty.util.internal.EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES;
|
||||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||||
|
import static java.lang.Integer.MAX_VALUE;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED;
|
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED;
|
||||||
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
|
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
|
||||||
@ -98,25 +98,10 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
*/
|
*/
|
||||||
private static final int DEFAULT_HOSTNAME_VALIDATION_FLAGS = 0;
|
private static final int DEFAULT_HOSTNAME_VALIDATION_FLAGS = 0;
|
||||||
|
|
||||||
static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the maximum overhead when encrypting plaintext as defined by
|
* Depends upon tcnative ... only use if tcnative is available!
|
||||||
* <a href="https://www.ietf.org/rfc/rfc5246.txt">rfc5264</a>,
|
|
||||||
* <a href="https://www.ietf.org/rfc/rfc5289.txt">rfc5289</a> and openssl implementation itself.
|
|
||||||
*
|
|
||||||
* Please note that we use a padding of 16 here as openssl uses PKC#5 which uses 16 bytes while the spec itself
|
|
||||||
* allow up to 255 bytes. 16 bytes is the max for PKC#5 (which handles it the same way as PKC#7) as we use a block
|
|
||||||
* size of 16. See <a href="https://tools.ietf.org/html/rfc5652#section-6.3">rfc5652#section-6.3</a>.
|
|
||||||
*
|
|
||||||
* TLS Header (5) + 16 (IV) + 48 (MAC) + 1 (Padding_length field) + 15 (Padding) + 1 (ContentType) +
|
|
||||||
* 2 (ProtocolVersion) + 2 (Length)
|
|
||||||
*
|
|
||||||
* TODO: We may need to review this calculation once TLS 1.3 becomes available.
|
|
||||||
*/
|
*/
|
||||||
static final int MAX_TLS_RECORD_OVERHEAD_LENGTH = SSL_RECORD_HEADER_LENGTH + 16 + 48 + 1 + 15 + 1 + 2 + 2;
|
static final int MAX_PLAINTEXT_LENGTH = SSL.SSL_MAX_PLAINTEXT_LENGTH;
|
||||||
|
|
||||||
static final int MAX_ENCRYPTED_PACKET_LENGTH = MAX_PLAINTEXT_LENGTH + MAX_TLS_RECORD_OVERHEAD_LENGTH;
|
|
||||||
|
|
||||||
private static final AtomicIntegerFieldUpdater<ReferenceCountedOpenSslEngine> DESTROYED_UPDATER =
|
private static final AtomicIntegerFieldUpdater<ReferenceCountedOpenSslEngine> DESTROYED_UPDATER =
|
||||||
AtomicIntegerFieldUpdater.newUpdater(ReferenceCountedOpenSslEngine.class, "destroyed");
|
AtomicIntegerFieldUpdater.newUpdater(ReferenceCountedOpenSslEngine.class, "destroyed");
|
||||||
@ -146,7 +131,6 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
* Started via {@link #beginHandshake()}.
|
* Started via {@link #beginHandshake()}.
|
||||||
*/
|
*/
|
||||||
STARTED_EXPLICITLY,
|
STARTED_EXPLICITLY,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handshake is finished.
|
* Handshake is finished.
|
||||||
*/
|
*/
|
||||||
@ -198,6 +182,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
private boolean isInboundDone;
|
private boolean isInboundDone;
|
||||||
private boolean outboundClosed;
|
private boolean outboundClosed;
|
||||||
|
|
||||||
|
private final boolean jdkCompatibilityMode;
|
||||||
private final boolean clientMode;
|
private final boolean clientMode;
|
||||||
private final ByteBufAllocator alloc;
|
private final ByteBufAllocator alloc;
|
||||||
private final OpenSslEngineMap engineMap;
|
private final OpenSslEngineMap engineMap;
|
||||||
@ -209,6 +194,8 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
private final ByteBuffer[] singleDstBuffer = new ByteBuffer[1];
|
private final ByteBuffer[] singleDstBuffer = new ByteBuffer[1];
|
||||||
private final OpenSslKeyMaterialManager keyMaterialManager;
|
private final OpenSslKeyMaterialManager keyMaterialManager;
|
||||||
private final boolean enableOcsp;
|
private final boolean enableOcsp;
|
||||||
|
private int maxWrapOverhead;
|
||||||
|
private int maxWrapBufferSize;
|
||||||
|
|
||||||
// This is package-private as we set it from OpenSslContext if an exception is thrown during
|
// This is package-private as we set it from OpenSslContext if an exception is thrown during
|
||||||
// the verification step.
|
// the verification step.
|
||||||
@ -220,10 +207,14 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
* @param alloc The allocator to use.
|
* @param alloc The allocator to use.
|
||||||
* @param peerHost The peer host name.
|
* @param peerHost The peer host name.
|
||||||
* @param peerPort The peer port.
|
* @param peerPort The peer port.
|
||||||
|
* @param jdkCompatibilityMode {@code true} to behave like described in
|
||||||
|
* https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html.
|
||||||
|
* {@code false} allows for partial and/or multiple packets to be process in a single
|
||||||
|
* wrap or unwrap call.
|
||||||
* @param leakDetection {@code true} to enable leak detection of this object.
|
* @param leakDetection {@code true} to enable leak detection of this object.
|
||||||
*/
|
*/
|
||||||
ReferenceCountedOpenSslEngine(ReferenceCountedOpenSslContext context, ByteBufAllocator alloc, String peerHost,
|
ReferenceCountedOpenSslEngine(ReferenceCountedOpenSslContext context, ByteBufAllocator alloc, String peerHost,
|
||||||
int peerPort, boolean leakDetection) {
|
int peerPort, boolean jdkCompatibilityMode, boolean leakDetection) {
|
||||||
super(peerHost, peerPort);
|
super(peerHost, peerPort);
|
||||||
OpenSsl.ensureAvailability();
|
OpenSsl.ensureAvailability();
|
||||||
leak = leakDetection ? leakDetector.track(this) : null;
|
leak = leakDetection ? leakDetector.track(this) : null;
|
||||||
@ -236,7 +227,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
localCerts = context.keyCertChain;
|
localCerts = context.keyCertChain;
|
||||||
keyMaterialManager = context.keyMaterialManager();
|
keyMaterialManager = context.keyMaterialManager();
|
||||||
enableOcsp = context.enableOcsp;
|
enableOcsp = context.enableOcsp;
|
||||||
|
this.jdkCompatibilityMode = jdkCompatibilityMode;
|
||||||
Lock readerLock = context.ctxLock.readLock();
|
Lock readerLock = context.ctxLock.readLock();
|
||||||
readerLock.lock();
|
readerLock.lock();
|
||||||
try {
|
try {
|
||||||
@ -244,6 +235,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
} finally {
|
} finally {
|
||||||
readerLock.unlock();
|
readerLock.unlock();
|
||||||
}
|
}
|
||||||
|
ssl = SSL.newSSL(context.ctx, !context.isClient());
|
||||||
try {
|
try {
|
||||||
networkBIO = SSL.bioNewByteBuffer(ssl, context.getBioNonApplicationBufferSize());
|
networkBIO = SSL.bioNewByteBuffer(ssl, context.getBioNonApplicationBufferSize());
|
||||||
|
|
||||||
@ -264,6 +256,13 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
if (enableOcsp) {
|
if (enableOcsp) {
|
||||||
SSL.enableOcsp(ssl);
|
SSL.enableOcsp(ssl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!jdkCompatibilityMode) {
|
||||||
|
SSL.setMode(ssl, SSL.getMode(ssl) | SSL.SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// setMode may impact the overhead.
|
||||||
|
calculateMaxWrapOverhead();
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
SSL.freeSSL(ssl);
|
SSL.freeSSL(ssl);
|
||||||
PlatformDependent.throwException(cause);
|
PlatformDependent.throwException(cause);
|
||||||
@ -461,7 +460,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final int limit = dst.limit();
|
final int limit = dst.limit();
|
||||||
final int len = min(MAX_ENCRYPTED_PACKET_LENGTH, limit - pos);
|
final int len = min(maxEncryptedPacketLength0(), limit - pos);
|
||||||
final ByteBuf buf = alloc.directBuffer(len);
|
final ByteBuf buf = alloc.directBuffer(len);
|
||||||
try {
|
try {
|
||||||
sslRead = SSL.readFromSSL(ssl, memoryAddress(buf), len);
|
sslRead = SSL.readFromSSL(ssl, memoryAddress(buf), len);
|
||||||
@ -478,6 +477,65 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
return sslRead;
|
return sslRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible only for testing!
|
||||||
|
*/
|
||||||
|
final synchronized int maxWrapOverhead() {
|
||||||
|
return maxWrapOverhead;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible only for testing!
|
||||||
|
*/
|
||||||
|
final synchronized int maxEncryptedPacketLength() {
|
||||||
|
return maxEncryptedPacketLength0();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is intentionally not synchronized, only use if you know you are in the EventLoop
|
||||||
|
* thread and visibility on {@link #maxWrapOverhead} is achieved via other synchronized blocks.
|
||||||
|
*/
|
||||||
|
final int maxEncryptedPacketLength0() {
|
||||||
|
return maxWrapOverhead + MAX_PLAINTEXT_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is intentionally not synchronized, only use if you know you are in the EventLoop
|
||||||
|
* thread and visibility on {@link #maxWrapBufferSize} and {@link #maxWrapOverhead} is achieved
|
||||||
|
* via other synchronized blocks.
|
||||||
|
*/
|
||||||
|
final int calculateMaxLengthForWrap(int plaintextLength, int numComponents) {
|
||||||
|
return (int) min(maxWrapBufferSize, plaintextLength + (long) maxWrapOverhead * numComponents);
|
||||||
|
}
|
||||||
|
|
||||||
|
final synchronized int sslPending() {
|
||||||
|
return sslPending0();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It is assumed this method is called in a synchronized block (or the constructor)!
|
||||||
|
*/
|
||||||
|
private void calculateMaxWrapOverhead() {
|
||||||
|
maxWrapOverhead = SSL.getMaxWrapOverhead(ssl);
|
||||||
|
|
||||||
|
// maxWrapBufferSize must be set after maxWrapOverhead because there is a dependency on this value.
|
||||||
|
// If jdkCompatibility mode is off we allow enough space to encrypt 16 buffers at a time. This could be
|
||||||
|
// configurable in the future if necessary.
|
||||||
|
maxWrapBufferSize = jdkCompatibilityMode ? maxEncryptedPacketLength0() : maxEncryptedPacketLength0() << 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int sslPending0() {
|
||||||
|
// OpenSSL has a limitation where if you call SSL_pending before the handshake is complete OpenSSL will throw a
|
||||||
|
// "called a function you should not call" error. Using the TLS_method instead of SSLv23_method may solve this
|
||||||
|
// issue but this API is only available in 1.1.0+ [1].
|
||||||
|
// [1] https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_new.html
|
||||||
|
return handshakeState != HandshakeState.FINISHED ? 0 : SSL.sslPending(ssl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBytesAvailableEnoughForWrap(int bytesAvailable, int plaintextLength, int numComponents) {
|
||||||
|
return bytesAvailable - (long) maxWrapOverhead * numComponents >= plaintextLength;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final SSLEngineResult wrap(
|
public final SSLEngineResult wrap(
|
||||||
final ByteBuffer[] srcs, int offset, final int length, final ByteBuffer dst) throws SSLException {
|
final ByteBuffer[] srcs, int offset, final int length, final ByteBuffer dst) throws SSLException {
|
||||||
@ -602,8 +660,9 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int srcsLen = 0;
|
|
||||||
final int endOffset = offset + length;
|
final int endOffset = offset + length;
|
||||||
|
if (jdkCompatibilityMode) {
|
||||||
|
int srcsLen = 0;
|
||||||
for (int i = offset; i < endOffset; ++i) {
|
for (int i = offset; i < endOffset; ++i) {
|
||||||
final ByteBuffer src = srcs[i];
|
final ByteBuffer src = srcs[i];
|
||||||
if (src == null) {
|
if (src == null) {
|
||||||
@ -622,11 +681,12 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we will only produce a single TLS packet, and we don't aggregate src buffers,
|
// jdkCompatibilityMode will only produce a single TLS packet, and we don't aggregate src buffers,
|
||||||
// so we always fix the number of buffers to 1 when checking if the dst buffer is large enough.
|
// so we always fix the number of buffers to 1 when checking if the dst buffer is large enough.
|
||||||
if (dst.remaining() < calculateOutNetBufSize(srcsLen, 1)) {
|
if (!isBytesAvailableEnoughForWrap(dst.remaining(), srcsLen, 1)) {
|
||||||
return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0);
|
return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// There was no pending data in the network BIO -- encrypt any application data
|
// There was no pending data in the network BIO -- encrypt any application data
|
||||||
int bytesConsumed = 0;
|
int bytesConsumed = 0;
|
||||||
@ -639,8 +699,23 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write plaintext application data to the SSL engine
|
final int bytesWritten;
|
||||||
int bytesWritten = writePlaintextData(src, min(remaining, MAX_PLAINTEXT_LENGTH - bytesConsumed));
|
if (jdkCompatibilityMode) {
|
||||||
|
// Write plaintext application data to the SSL engine. We don't have to worry about checking
|
||||||
|
// if there is enough space if jdkCompatibilityMode because we only wrap at most
|
||||||
|
// MAX_PLAINTEXT_LENGTH and we loop over the input before hand and check if there is space.
|
||||||
|
bytesWritten = writePlaintextData(src, min(remaining, MAX_PLAINTEXT_LENGTH - bytesConsumed));
|
||||||
|
} else {
|
||||||
|
// OpenSSL's SSL_write keeps state between calls. We should make sure the amount we attempt to
|
||||||
|
// write is guaranteed to succeed so we don't have to worry about keeping state consistent
|
||||||
|
// between calls.
|
||||||
|
final int availableCapacityForWrap = dst.remaining() - bytesProduced - maxWrapOverhead;
|
||||||
|
if (availableCapacityForWrap <= 0) {
|
||||||
|
return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed,
|
||||||
|
bytesProduced);
|
||||||
|
}
|
||||||
|
bytesWritten = writePlaintextData(src, min(remaining, availableCapacityForWrap));
|
||||||
|
}
|
||||||
|
|
||||||
if (bytesWritten > 0) {
|
if (bytesWritten > 0) {
|
||||||
bytesConsumed += bytesWritten;
|
bytesConsumed += bytesWritten;
|
||||||
@ -650,7 +725,9 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
bytesProduced += bioLengthBefore - pendingNow;
|
bytesProduced += bioLengthBefore - pendingNow;
|
||||||
bioLengthBefore = pendingNow;
|
bioLengthBefore = pendingNow;
|
||||||
|
|
||||||
|
if (jdkCompatibilityMode || bytesProduced == dst.remaining()) {
|
||||||
return newResultMayFinishHandshake(status, bytesConsumed, bytesProduced);
|
return newResultMayFinishHandshake(status, bytesConsumed, bytesProduced);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
int sslError = SSL.getError(ssl, bytesWritten);
|
int sslError = SSL.getError(ssl, bytesWritten);
|
||||||
if (sslError == SSL.SSL_ERROR_ZERO_RETURN) {
|
if (sslError == SSL.SSL_ERROR_ZERO_RETURN) {
|
||||||
@ -686,10 +763,10 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
// "When using a buffering BIO, like a BIO pair, data must be written into or retrieved
|
// "When using a buffering BIO, like a BIO pair, data must be written into or retrieved
|
||||||
// out of the BIO before being able to continue."
|
// out of the BIO before being able to continue."
|
||||||
//
|
//
|
||||||
// So we attempt to drain the BIO buffer below, but if there is no data this condition
|
// In practice this means the destination buffer doesn't have enough space for OpenSSL
|
||||||
// is undefined and we assume their is a fatal error with the openssl engine and close.
|
// to write encrypted data to. This is an OVERFLOW condition.
|
||||||
// [1] https://www.openssl.org/docs/manmaster/ssl/SSL_write.html
|
// [1] https://www.openssl.org/docs/manmaster/ssl/SSL_write.html
|
||||||
return newResult(NEED_WRAP, bytesConsumed, bytesProduced);
|
return newResult(BUFFER_OVERFLOW, status, bytesConsumed, bytesProduced);
|
||||||
} else {
|
} else {
|
||||||
// Everything else is considered as error
|
// Everything else is considered as error
|
||||||
throw shutdownWithError("SSL_write");
|
throw shutdownWithError("SSL_write");
|
||||||
@ -835,17 +912,23 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len < SSL_RECORD_HEADER_LENGTH) {
|
int sslPending = sslPending0();
|
||||||
|
int packetLength;
|
||||||
|
// The JDK implies that only a single SSL packet should be processed per unwrap call [1]. If we are in
|
||||||
|
// JDK compatibility mode then we should honor this, but if not we just wrap as much as possible. If there
|
||||||
|
// are multiple records or partial records this may reduce thrashing events through the pipeline.
|
||||||
|
// [1] https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html
|
||||||
|
if (jdkCompatibilityMode) {
|
||||||
|
if (len < SslUtils.SSL_RECORD_HEADER_LENGTH) {
|
||||||
return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0);
|
return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int packetLength = SslUtils.getEncryptedPacketLength(srcs, srcsOffset);
|
packetLength = SslUtils.getEncryptedPacketLength(srcs, srcsOffset);
|
||||||
|
|
||||||
if (packetLength == SslUtils.NOT_ENCRYPTED) {
|
if (packetLength == SslUtils.NOT_ENCRYPTED) {
|
||||||
throw new NotSslRecordException("not an SSL/TLS record");
|
throw new NotSslRecordException("not an SSL/TLS record");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packetLength - SSL_RECORD_HEADER_LENGTH > capacity) {
|
if (packetLength - SslUtils.SSL_RECORD_HEADER_LENGTH > capacity) {
|
||||||
// No enough space in the destination buffer so signal the caller
|
// No enough space in the destination buffer so signal the caller
|
||||||
// that the buffer needs to be increased.
|
// that the buffer needs to be increased.
|
||||||
return newResultMayFinishHandshake(BUFFER_OVERFLOW, status, 0, 0);
|
return newResultMayFinishHandshake(BUFFER_OVERFLOW, status, 0, 0);
|
||||||
@ -856,6 +939,13 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
// the whole packet.
|
// the whole packet.
|
||||||
return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0);
|
return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0);
|
||||||
}
|
}
|
||||||
|
} else if (len == 0 && sslPending <= 0) {
|
||||||
|
return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0);
|
||||||
|
} else if (capacity == 0) {
|
||||||
|
return newResultMayFinishHandshake(BUFFER_OVERFLOW, status, 0, 0);
|
||||||
|
} else {
|
||||||
|
packetLength = (int) min(MAX_VALUE, len);
|
||||||
|
}
|
||||||
|
|
||||||
// This must always be the case when we reached here as if not we returned BUFFER_UNDERFLOW.
|
// This must always be the case when we reached here as if not we returned BUFFER_UNDERFLOW.
|
||||||
assert srcsOffset < srcsEndOffset;
|
assert srcsOffset < srcsEndOffset;
|
||||||
@ -867,24 +957,38 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
int bytesProduced = 0;
|
int bytesProduced = 0;
|
||||||
int bytesConsumed = 0;
|
int bytesConsumed = 0;
|
||||||
try {
|
try {
|
||||||
for (; srcsOffset < srcsEndOffset; ++srcsOffset) {
|
srcLoop:
|
||||||
|
for (;;) {
|
||||||
ByteBuffer src = srcs[srcsOffset];
|
ByteBuffer src = srcs[srcsOffset];
|
||||||
int remaining = src.remaining();
|
int remaining = src.remaining();
|
||||||
|
final ByteBuf bioWriteCopyBuf;
|
||||||
|
int pendingEncryptedBytes;
|
||||||
if (remaining == 0) {
|
if (remaining == 0) {
|
||||||
|
if (sslPending <= 0) {
|
||||||
// We must skip empty buffers as BIO_write will return 0 if asked to write something
|
// We must skip empty buffers as BIO_write will return 0 if asked to write something
|
||||||
// with length 0.
|
// with length 0.
|
||||||
continue;
|
if (++srcsOffset >= srcsEndOffset) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
bioWriteCopyBuf = null;
|
||||||
|
pendingEncryptedBytes = SSL.bioLengthByteBuffer(networkBIO);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Write more encrypted data into the BIO. Ensure we only read one packet at a time as
|
// Write more encrypted data into the BIO. Ensure we only read one packet at a time as
|
||||||
// stated in the SSLEngine javadocs.
|
// stated in the SSLEngine javadocs.
|
||||||
int pendingEncryptedBytes = min(packetLength, remaining);
|
pendingEncryptedBytes = min(packetLength, remaining);
|
||||||
ByteBuf bioWriteCopyBuf = writeEncryptedData(src, pendingEncryptedBytes);
|
bioWriteCopyBuf = writeEncryptedData(src, pendingEncryptedBytes);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
readLoop:
|
for (;;) {
|
||||||
for (; dstsOffset < dstsEndOffset; ++dstsOffset) {
|
|
||||||
ByteBuffer dst = dsts[dstsOffset];
|
ByteBuffer dst = dsts[dstsOffset];
|
||||||
if (!dst.hasRemaining()) {
|
if (!dst.hasRemaining()) {
|
||||||
// No space left in the destination buffer, skip it.
|
// No space left in the destination buffer, skip it.
|
||||||
|
if (++dstsOffset >= dstsEndOffset) {
|
||||||
|
break srcLoop;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -903,22 +1007,27 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
bytesProduced += bytesRead;
|
bytesProduced += bytesRead;
|
||||||
|
|
||||||
if (!dst.hasRemaining()) {
|
if (!dst.hasRemaining()) {
|
||||||
|
sslPending = sslPending0();
|
||||||
// Move to the next dst buffer as this one is full.
|
// Move to the next dst buffer as this one is full.
|
||||||
continue;
|
if (++dstsOffset >= dstsEndOffset) {
|
||||||
}
|
return sslPending > 0 ?
|
||||||
if (packetLength == 0) {
|
newResult(BUFFER_OVERFLOW, status, bytesConsumed, bytesProduced) :
|
||||||
// We read everything return now.
|
newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status,
|
||||||
return newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status,
|
|
||||||
bytesConsumed, bytesProduced);
|
bytesConsumed, bytesProduced);
|
||||||
}
|
}
|
||||||
|
} else if (packetLength == 0) {
|
||||||
|
// We read everything return now.
|
||||||
|
break srcLoop;
|
||||||
|
} else if (jdkCompatibilityMode) {
|
||||||
// try to write again to the BIO. stop reading from it by break out of the readLoop.
|
// try to write again to the BIO. stop reading from it by break out of the readLoop.
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
int sslError = SSL.getError(ssl, bytesRead);
|
int sslError = SSL.getError(ssl, bytesRead);
|
||||||
if (sslError == SSL.SSL_ERROR_WANT_READ || sslError == SSL.SSL_ERROR_WANT_WRITE) {
|
if (sslError == SSL.SSL_ERROR_WANT_READ || sslError == SSL.SSL_ERROR_WANT_WRITE) {
|
||||||
// break to the outer loop as we want to read more data which means we need to
|
// break to the outer loop as we want to read more data which means we need to
|
||||||
// write more to the BIO.
|
// write more to the BIO.
|
||||||
break readLoop;
|
break;
|
||||||
} else if (sslError == SSL.SSL_ERROR_ZERO_RETURN) {
|
} else if (sslError == SSL.SSL_ERROR_ZERO_RETURN) {
|
||||||
// This means the connection was shutdown correctly, close inbound and outbound
|
// This means the connection was shutdown correctly, close inbound and outbound
|
||||||
if (!receivedShutdown) {
|
if (!receivedShutdown) {
|
||||||
@ -933,8 +1042,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Either we have no more dst buffers to put the data, or no more data to generate; we are done.
|
if (++srcsOffset >= srcsEndOffset) {
|
||||||
if (dstsOffset >= dstsEndOffset || packetLength == 0) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -1054,7 +1162,6 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
public final Runnable getDelegatedTask() {
|
public final Runnable getDelegatedTask() {
|
||||||
// Currently, we do not delegate SSL computation tasks
|
// Currently, we do not delegate SSL computation tasks
|
||||||
// TODO: in the future, possibly create tasks to do encrypt / decrypt async
|
// TODO: in the future, possibly create tasks to do encrypt / decrypt async
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1329,6 +1436,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
// for renegotiation.
|
// for renegotiation.
|
||||||
|
|
||||||
handshakeState = HandshakeState.STARTED_EXPLICITLY; // Next time this method is invoked by the user,
|
handshakeState = HandshakeState.STARTED_EXPLICITLY; // Next time this method is invoked by the user,
|
||||||
|
calculateMaxWrapOverhead();
|
||||||
// we should raise an exception.
|
// we should raise an exception.
|
||||||
break;
|
break;
|
||||||
case STARTED_EXPLICITLY:
|
case STARTED_EXPLICITLY:
|
||||||
@ -1375,6 +1483,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
case NOT_STARTED:
|
case NOT_STARTED:
|
||||||
handshakeState = HandshakeState.STARTED_EXPLICITLY;
|
handshakeState = HandshakeState.STARTED_EXPLICITLY;
|
||||||
handshake();
|
handshake();
|
||||||
|
calculateMaxWrapOverhead();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error();
|
throw new Error();
|
||||||
@ -1667,11 +1776,6 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
return destroyed != 0;
|
return destroyed != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int calculateOutNetBufSize(int pendingBytes, int numComponents) {
|
|
||||||
return (int) min(MAX_ENCRYPTED_PACKET_LENGTH,
|
|
||||||
pendingBytes + (long) MAX_TLS_RECORD_OVERHEAD_LENGTH * numComponents);
|
|
||||||
}
|
|
||||||
|
|
||||||
final boolean checkSniHostnameMatch(String hostname) {
|
final boolean checkSniHostnameMatch(String hostname) {
|
||||||
return Java8SslUtils.checkSniHostnameMatch(matchers, hostname);
|
return Java8SslUtils.checkSniHostnameMatch(matchers, hostname);
|
||||||
}
|
}
|
||||||
@ -1819,6 +1923,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
|
|
||||||
initPeerCerts();
|
initPeerCerts();
|
||||||
selectApplicationProtocol();
|
selectApplicationProtocol();
|
||||||
|
calculateMaxWrapOverhead();
|
||||||
|
|
||||||
handshakeState = HandshakeState.FINISHED;
|
handshakeState = HandshakeState.FINISHED;
|
||||||
} else {
|
} else {
|
||||||
@ -2026,7 +2131,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPacketBufferSize() {
|
public int getPacketBufferSize() {
|
||||||
return MAX_ENCRYPTED_PACKET_LENGTH;
|
return maxEncryptedPacketLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -882,11 +882,11 @@ public abstract class SslContext {
|
|||||||
* Creates a new {@link SslHandler}.
|
* Creates a new {@link SslHandler}.
|
||||||
* <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the returned {@link SslHandler} will release the engine
|
* <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the returned {@link SslHandler} will release the engine
|
||||||
* that is wrapped. If the returned {@link SslHandler} is not inserted into a pipeline then you may leak native
|
* that is wrapped. If the returned {@link SslHandler} is not inserted into a pipeline then you may leak native
|
||||||
* memory!</p>
|
* memory!
|
||||||
* <p><b>Beware</b>: the underlying generated {@link SSLEngine} won't have
|
* <p><b>Beware</b>: the underlying generated {@link SSLEngine} won't have
|
||||||
* <a href="https://wiki.openssl.org/index.php/Hostname_validation">hostname verification</a> enabled by default.
|
* <a href="https://wiki.openssl.org/index.php/Hostname_validation">hostname verification</a> enabled by default.
|
||||||
* If you create {@link SslHandler} for the client side and want proper security, we advice that you configure
|
* If you create {@link SslHandler} for the client side and want proper security, we advice that you configure
|
||||||
* the {@link SSLEngine} (see {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}):</p>
|
* the {@link SSLEngine} (see {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}):
|
||||||
* <pre>
|
* <pre>
|
||||||
* SSLEngine sslEngine = sslHandler.engine();
|
* SSLEngine sslEngine = sslHandler.engine();
|
||||||
* SSLParameters sslParameters = sslEngine.getSSLParameters();
|
* SSLParameters sslParameters = sslEngine.getSSLParameters();
|
||||||
@ -894,12 +894,22 @@ public abstract class SslContext {
|
|||||||
* sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
|
* sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
|
||||||
* sslEngine.setSSLParameters(sslParameters);
|
* sslEngine.setSSLParameters(sslParameters);
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
* <p>
|
||||||
|
* The underlying {@link SSLEngine} may not follow the restrictions imposed by the
|
||||||
|
* <a href="https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html">SSLEngine javadocs</a> which
|
||||||
|
* limits wrap/unwrap to operate on a single SSL/TLS packet.
|
||||||
* @param alloc If supported by the SSLEngine then the SSLEngine will use this to allocate ByteBuf objects.
|
* @param alloc If supported by the SSLEngine then the SSLEngine will use this to allocate ByteBuf objects.
|
||||||
*
|
|
||||||
* @return a new {@link SslHandler}
|
* @return a new {@link SslHandler}
|
||||||
*/
|
*/
|
||||||
public final SslHandler newHandler(ByteBufAllocator alloc) {
|
public final SslHandler newHandler(ByteBufAllocator alloc) {
|
||||||
|
return newHandler(alloc, startTls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new SslHandler.
|
||||||
|
* @see #newHandler(io.netty.buffer.ByteBufAllocator)
|
||||||
|
*/
|
||||||
|
SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) {
|
||||||
return new SslHandler(newEngine(alloc), startTls);
|
return new SslHandler(newEngine(alloc), startTls);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -907,11 +917,11 @@ public abstract class SslContext {
|
|||||||
* Creates a new {@link SslHandler} with advisory peer information.
|
* Creates a new {@link SslHandler} with advisory peer information.
|
||||||
* <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the returned {@link SslHandler} will release the engine
|
* <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the returned {@link SslHandler} will release the engine
|
||||||
* that is wrapped. If the returned {@link SslHandler} is not inserted into a pipeline then you may leak native
|
* that is wrapped. If the returned {@link SslHandler} is not inserted into a pipeline then you may leak native
|
||||||
* memory!</p>
|
* memory!
|
||||||
* <p><b>Beware</b>: the underlying generated {@link SSLEngine} won't have
|
* <p><b>Beware</b>: the underlying generated {@link SSLEngine} won't have
|
||||||
* <a href="https://wiki.openssl.org/index.php/Hostname_validation">hostname verification</a> enabled by default.
|
* <a href="https://wiki.openssl.org/index.php/Hostname_validation">hostname verification</a> enabled by default.
|
||||||
* If you create {@link SslHandler} for the client side and want proper security, we advice that you configure
|
* If you create {@link SslHandler} for the client side and want proper security, we advice that you configure
|
||||||
* the {@link SSLEngine} (see {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}):</p>
|
* the {@link SSLEngine} (see {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}):
|
||||||
* <pre>
|
* <pre>
|
||||||
* SSLEngine sslEngine = sslHandler.engine();
|
* SSLEngine sslEngine = sslHandler.engine();
|
||||||
* SSLParameters sslParameters = sslEngine.getSSLParameters();
|
* SSLParameters sslParameters = sslEngine.getSSLParameters();
|
||||||
@ -919,7 +929,10 @@ public abstract class SslContext {
|
|||||||
* sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
|
* sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
|
||||||
* sslEngine.setSSLParameters(sslParameters);
|
* sslEngine.setSSLParameters(sslParameters);
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
* <p>
|
||||||
|
* The underlying {@link SSLEngine} may not follow the restrictions imposed by the
|
||||||
|
* <a href="https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html">SSLEngine javadocs</a> which
|
||||||
|
* limits wrap/unwrap to operate on a single SSL/TLS packet.
|
||||||
* @param alloc If supported by the SSLEngine then the SSLEngine will use this to allocate ByteBuf objects.
|
* @param alloc If supported by the SSLEngine then the SSLEngine will use this to allocate ByteBuf objects.
|
||||||
* @param peerHost the non-authoritative name of the host
|
* @param peerHost the non-authoritative name of the host
|
||||||
* @param peerPort the non-authoritative port
|
* @param peerPort the non-authoritative port
|
||||||
@ -927,6 +940,14 @@ public abstract class SslContext {
|
|||||||
* @return a new {@link SslHandler}
|
* @return a new {@link SslHandler}
|
||||||
*/
|
*/
|
||||||
public final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort) {
|
public final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort) {
|
||||||
|
return newHandler(alloc, peerHost, peerPort, startTls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new SslHandler.
|
||||||
|
* @see #newHandler(io.netty.buffer.ByteBufAllocator, String, int, boolean)
|
||||||
|
*/
|
||||||
|
SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) {
|
||||||
return new SslHandler(newEngine(alloc, peerHost, peerPort), startTls);
|
return new SslHandler(newEngine(alloc, peerHost, peerPort), startTls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,12 +46,6 @@ import io.netty.util.internal.ThrowableUtil;
|
|||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
|
||||||
import javax.net.ssl.SSLEngineResult;
|
|
||||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
|
||||||
import javax.net.ssl.SSLEngineResult.Status;
|
|
||||||
import javax.net.ssl.SSLException;
|
|
||||||
import javax.net.ssl.SSLSession;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -65,6 +59,12 @@ import java.util.concurrent.Executor;
|
|||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import javax.net.ssl.SSLEngineResult;
|
||||||
|
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||||
|
import javax.net.ssl.SSLEngineResult.Status;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
|
||||||
import static io.netty.handler.ssl.SslUtils.getEncryptedPacketLength;
|
import static io.netty.handler.ssl.SslUtils.getEncryptedPacketLength;
|
||||||
|
|
||||||
@ -210,9 +210,21 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int getPacketBufferSize(SslHandler handler) {
|
||||||
|
return ((ReferenceCountedOpenSslEngine) handler.engine).maxEncryptedPacketLength0();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents) {
|
int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents) {
|
||||||
return ReferenceCountedOpenSslEngine.calculateOutNetBufSize(pendingBytes, numComponents);
|
return ((ReferenceCountedOpenSslEngine) handler.engine).calculateMaxLengthForWrap(pendingBytes,
|
||||||
|
numComponents);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int calculatePendingData(SslHandler handler, int guess) {
|
||||||
|
int sslPending = ((ReferenceCountedOpenSslEngine) handler.engine).sslPending();
|
||||||
|
return sslPending > 0 ? sslPending : guess;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CONSCRYPT(true, COMPOSITE_CUMULATOR) {
|
CONSCRYPT(true, COMPOSITE_CUMULATOR) {
|
||||||
@ -246,6 +258,11 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents) {
|
int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents) {
|
||||||
return ((ConscryptAlpnSslEngine) handler.engine).calculateOutNetBufSize(pendingBytes, numComponents);
|
return ((ConscryptAlpnSslEngine) handler.engine).calculateOutNetBufSize(pendingBytes, numComponents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int calculatePendingData(SslHandler handler, int guess) {
|
||||||
|
return guess;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
JDK(false, MERGE_CUMULATOR) {
|
JDK(false, MERGE_CUMULATOR) {
|
||||||
@Override
|
@Override
|
||||||
@ -260,18 +277,18 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents) {
|
int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents) {
|
||||||
return handler.maxPacketBufferSize;
|
return handler.engine.getSession().getPacketBufferSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int calculatePendingData(SslHandler handler, int guess) {
|
||||||
|
return guess;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static SslEngineType forEngine(SSLEngine engine) {
|
static SslEngineType forEngine(SSLEngine engine) {
|
||||||
if (engine instanceof ReferenceCountedOpenSslEngine) {
|
return engine instanceof ReferenceCountedOpenSslEngine ? TCNATIVE :
|
||||||
return TCNATIVE;
|
engine instanceof ConscryptAlpnSslEngine ? CONSCRYPT : JDK;
|
||||||
}
|
|
||||||
if (engine instanceof ConscryptAlpnSslEngine) {
|
|
||||||
return CONSCRYPT;
|
|
||||||
}
|
|
||||||
return JDK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SslEngineType(boolean wantsDirectBuffer, Cumulator cumulator) {
|
SslEngineType(boolean wantsDirectBuffer, Cumulator cumulator) {
|
||||||
@ -279,11 +296,17 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
this.cumulator = cumulator;
|
this.cumulator = cumulator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getPacketBufferSize(SslHandler handler) {
|
||||||
|
return handler.engine.getSession().getPacketBufferSize();
|
||||||
|
}
|
||||||
|
|
||||||
abstract SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, int len, ByteBuf out)
|
abstract SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, int len, ByteBuf out)
|
||||||
throws SSLException;
|
throws SSLException;
|
||||||
|
|
||||||
abstract int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents);
|
abstract int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents);
|
||||||
|
|
||||||
|
abstract int calculatePendingData(SslHandler handler, int guess);
|
||||||
|
|
||||||
// BEGIN Platform-dependent flags
|
// BEGIN Platform-dependent flags
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -307,8 +330,8 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
private volatile ChannelHandlerContext ctx;
|
private volatile ChannelHandlerContext ctx;
|
||||||
private final SSLEngine engine;
|
private final SSLEngine engine;
|
||||||
private final SslEngineType engineType;
|
private final SslEngineType engineType;
|
||||||
private final int maxPacketBufferSize;
|
|
||||||
private final Executor delegatedTaskExecutor;
|
private final Executor delegatedTaskExecutor;
|
||||||
|
private final boolean jdkCompatibilityMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used if {@link SSLEngine#wrap(ByteBuffer[], ByteBuffer)} and {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer[])}
|
* Used if {@link SSLEngine#wrap(ByteBuffer[], ByteBuffer)} and {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer[])}
|
||||||
@ -380,6 +403,14 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public SslHandler(SSLEngine engine, boolean startTls, Executor delegatedTaskExecutor) {
|
public SslHandler(SSLEngine engine, boolean startTls, Executor delegatedTaskExecutor) {
|
||||||
|
this(engine, startTls, true, delegatedTaskExecutor);
|
||||||
|
}
|
||||||
|
|
||||||
|
SslHandler(SSLEngine engine, boolean startTls, boolean jdkCompatibilityMode) {
|
||||||
|
this(engine, startTls, jdkCompatibilityMode, ImmediateExecutor.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
SslHandler(SSLEngine engine, boolean startTls, boolean jdkCompatibilityMode, Executor delegatedTaskExecutor) {
|
||||||
if (engine == null) {
|
if (engine == null) {
|
||||||
throw new NullPointerException("engine");
|
throw new NullPointerException("engine");
|
||||||
}
|
}
|
||||||
@ -390,7 +421,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
engineType = SslEngineType.forEngine(engine);
|
engineType = SslEngineType.forEngine(engine);
|
||||||
this.delegatedTaskExecutor = delegatedTaskExecutor;
|
this.delegatedTaskExecutor = delegatedTaskExecutor;
|
||||||
this.startTls = startTls;
|
this.startTls = startTls;
|
||||||
maxPacketBufferSize = engine.getSession().getPacketBufferSize();
|
this.jdkCompatibilityMode = jdkCompatibilityMode;
|
||||||
setCumulator(engineType.cumulator);
|
setCumulator(engineType.cumulator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,7 +909,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
|
|
||||||
switch (result.getStatus()) {
|
switch (result.getStatus()) {
|
||||||
case BUFFER_OVERFLOW:
|
case BUFFER_OVERFLOW:
|
||||||
out.ensureWritable(maxPacketBufferSize);
|
out.ensureWritable(engine.getSession().getPacketBufferSize());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return result;
|
return result;
|
||||||
@ -1014,75 +1045,63 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
return getEncryptedPacketLength(buffer, buffer.readerIndex()) != SslUtils.NOT_ENCRYPTED;
|
return getEncryptedPacketLength(buffer, buffer.readerIndex()) != SslUtils.NOT_ENCRYPTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void decodeJdkCompatible(ChannelHandlerContext ctx, ByteBuf in) throws NotSslRecordException {
|
||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws SSLException {
|
int packetLength = this.packetLength;
|
||||||
final int startOffset = in.readerIndex();
|
|
||||||
final int endOffset = in.writerIndex();
|
|
||||||
int offset = startOffset;
|
|
||||||
int totalLength = 0;
|
|
||||||
|
|
||||||
// If we calculated the length of the current SSL record before, use that information.
|
// If we calculated the length of the current SSL record before, use that information.
|
||||||
if (packetLength > 0) {
|
if (packetLength > 0) {
|
||||||
if (endOffset - startOffset < packetLength) {
|
if (in.readableBytes() < packetLength) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
offset += packetLength;
|
// Get the packet length and wait until we get a packets worth of data to unwrap.
|
||||||
totalLength = packetLength;
|
final int readableBytes = in.readableBytes();
|
||||||
packetLength = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean nonSslRecord = false;
|
|
||||||
|
|
||||||
while (totalLength < ReferenceCountedOpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH) {
|
|
||||||
final int readableBytes = endOffset - offset;
|
|
||||||
if (readableBytes < SslUtils.SSL_RECORD_HEADER_LENGTH) {
|
if (readableBytes < SslUtils.SSL_RECORD_HEADER_LENGTH) {
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
|
packetLength = getEncryptedPacketLength(in, in.readerIndex());
|
||||||
final int packetLength = getEncryptedPacketLength(in, offset);
|
|
||||||
if (packetLength == SslUtils.NOT_ENCRYPTED) {
|
if (packetLength == SslUtils.NOT_ENCRYPTED) {
|
||||||
nonSslRecord = true;
|
// Not an SSL/TLS packet
|
||||||
break;
|
NotSslRecordException e = new NotSslRecordException(
|
||||||
|
"not an SSL/TLS record: " + ByteBufUtil.hexDump(in));
|
||||||
|
in.skipBytes(in.readableBytes());
|
||||||
|
|
||||||
|
// First fail the handshake promise as we may need to have access to the SSLEngine which may
|
||||||
|
// be released because the user will remove the SslHandler in an exceptionCaught(...) implementation.
|
||||||
|
setHandshakeFailure(ctx, e);
|
||||||
|
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert packetLength > 0;
|
assert packetLength > 0;
|
||||||
|
|
||||||
if (packetLength > readableBytes) {
|
if (packetLength > readableBytes) {
|
||||||
// wait until the whole packet can be read
|
// wait until the whole packet can be read
|
||||||
this.packetLength = packetLength;
|
this.packetLength = packetLength;
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int newTotalLength = totalLength + packetLength;
|
// Reset the state of this class so we can get the length of the next packet. We assume the entire packet will
|
||||||
if (newTotalLength > ReferenceCountedOpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH) {
|
// be consumed by the SSLEngine.
|
||||||
// Don't read too much.
|
this.packetLength = 0;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have a whole packet.
|
|
||||||
// Increment the offset to handle the next packet.
|
|
||||||
offset += packetLength;
|
|
||||||
totalLength = newTotalLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalLength > 0) {
|
|
||||||
// The buffer contains one or more full SSL records.
|
|
||||||
// Slice out the whole packet so unwrap will only be called with complete packets.
|
|
||||||
// Also directly reset the packetLength. This is needed as unwrap(..) may trigger
|
|
||||||
// decode(...) again via:
|
|
||||||
// 1) unwrap(..) is called
|
|
||||||
// 2) wrap(...) is called from within unwrap(...)
|
|
||||||
// 3) wrap(...) calls unwrapLater(...)
|
|
||||||
// 4) unwrapLater(...) calls decode(...)
|
|
||||||
//
|
|
||||||
// See https://github.com/netty/netty/issues/1534
|
|
||||||
|
|
||||||
in.skipBytes(totalLength);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
firedChannelRead = unwrap(ctx, in, startOffset, totalLength) || firedChannelRead;
|
int bytesConsumed = unwrap(ctx, in, in.readerIndex(), packetLength);
|
||||||
|
assert bytesConsumed == packetLength || engine.isInboundDone() :
|
||||||
|
"we feed the SSLEngine a packets worth of data: " + packetLength + " but it only consumed: " +
|
||||||
|
bytesConsumed;
|
||||||
|
in.skipBytes(bytesConsumed);
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
|
handleUnwrapThrowable(ctx, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decodeNonJdkCompatible(ChannelHandlerContext ctx, ByteBuf in) {
|
||||||
|
try {
|
||||||
|
in.skipBytes(unwrap(ctx, in, in.readerIndex(), in.readableBytes()));
|
||||||
|
} catch (Throwable cause) {
|
||||||
|
handleUnwrapThrowable(ctx, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleUnwrapThrowable(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
try {
|
try {
|
||||||
// We need to flush one time as there may be an alert that we should send to the remote peer because
|
// We need to flush one time as there may be an alert that we should send to the remote peer because
|
||||||
// of the SSLException reported here.
|
// of the SSLException reported here.
|
||||||
@ -1095,19 +1114,13 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
}
|
}
|
||||||
PlatformDependent.throwException(cause);
|
PlatformDependent.throwException(cause);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (nonSslRecord) {
|
@Override
|
||||||
// Not an SSL/TLS packet
|
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws SSLException {
|
||||||
NotSslRecordException e = new NotSslRecordException(
|
if (jdkCompatibilityMode) {
|
||||||
"not an SSL/TLS record: " + ByteBufUtil.hexDump(in));
|
decodeJdkCompatible(ctx, in);
|
||||||
in.skipBytes(in.readableBytes());
|
} else {
|
||||||
|
decodeNonJdkCompatible(ctx, in);
|
||||||
// First fail the handshake promise as we may need to have access to the SSLEngine which may
|
|
||||||
// be released because the user will remove the SslHandler in an exceptionCaught(...) implementation.
|
|
||||||
setHandshakeFailure(ctx, e);
|
|
||||||
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1148,10 +1161,9 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
/**
|
/**
|
||||||
* Unwraps inbound SSL records.
|
* Unwraps inbound SSL records.
|
||||||
*/
|
*/
|
||||||
private boolean unwrap(
|
private int unwrap(
|
||||||
ChannelHandlerContext ctx, ByteBuf packet, int offset, int length) throws SSLException {
|
ChannelHandlerContext ctx, ByteBuf packet, int offset, int length) throws SSLException {
|
||||||
|
final int originalLength = length;
|
||||||
boolean decoded = false;
|
|
||||||
boolean wrapLater = false;
|
boolean wrapLater = false;
|
||||||
boolean notifyClosure = false;
|
boolean notifyClosure = false;
|
||||||
ByteBuf decodeOut = allocate(ctx, length);
|
ByteBuf decodeOut = allocate(ctx, length);
|
||||||
@ -1174,7 +1186,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
int readableBytes = decodeOut.readableBytes();
|
int readableBytes = decodeOut.readableBytes();
|
||||||
int bufferSize = engine.getSession().getApplicationBufferSize() - readableBytes;
|
int bufferSize = engine.getSession().getApplicationBufferSize() - readableBytes;
|
||||||
if (readableBytes > 0) {
|
if (readableBytes > 0) {
|
||||||
decoded = true;
|
firedChannelRead = true;
|
||||||
ctx.fireChannelRead(decodeOut);
|
ctx.fireChannelRead(decodeOut);
|
||||||
|
|
||||||
// This buffer was handled, null it out.
|
// This buffer was handled, null it out.
|
||||||
@ -1194,7 +1206,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
// Allocate a new buffer which can hold all the rest data and loop again.
|
// Allocate a new buffer which can hold all the rest data and loop again.
|
||||||
// TODO: We may want to reconsider how we calculate the length here as we may
|
// TODO: We may want to reconsider how we calculate the length here as we may
|
||||||
// have more then one ssl message to decode.
|
// have more then one ssl message to decode.
|
||||||
decodeOut = allocate(ctx, bufferSize);
|
decodeOut = allocate(ctx, engineType.calculatePendingData(this, bufferSize));
|
||||||
continue;
|
continue;
|
||||||
case CLOSED:
|
case CLOSED:
|
||||||
// notify about the CLOSED state of the SSLEngine. See #137
|
// notify about the CLOSED state of the SSLEngine. See #137
|
||||||
@ -1268,7 +1280,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
} finally {
|
} finally {
|
||||||
if (decodeOut != null) {
|
if (decodeOut != null) {
|
||||||
if (decodeOut.isReadable()) {
|
if (decodeOut.isReadable()) {
|
||||||
decoded = true;
|
firedChannelRead = true;
|
||||||
|
|
||||||
ctx.fireChannelRead(decodeOut);
|
ctx.fireChannelRead(decodeOut);
|
||||||
} else {
|
} else {
|
||||||
@ -1276,7 +1288,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return decoded;
|
return originalLength - length;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ByteBuffer toByteBuffer(ByteBuf out, int index, int len) {
|
private static ByteBuffer toByteBuffer(ByteBuf out, int index, int len) {
|
||||||
|
@ -43,8 +43,6 @@ import javax.net.ssl.SSLException;
|
|||||||
import javax.net.ssl.SSLParameters;
|
import javax.net.ssl.SSLParameters;
|
||||||
|
|
||||||
import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory;
|
import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory;
|
||||||
import static io.netty.handler.ssl.ReferenceCountedOpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH;
|
|
||||||
import static io.netty.handler.ssl.ReferenceCountedOpenSslEngine.MAX_TLS_RECORD_OVERHEAD_LENGTH;
|
|
||||||
import static io.netty.handler.ssl.ReferenceCountedOpenSslEngine.MAX_PLAINTEXT_LENGTH;
|
import static io.netty.handler.ssl.ReferenceCountedOpenSslEngine.MAX_PLAINTEXT_LENGTH;
|
||||||
import static io.netty.internal.tcnative.SSL.SSL_CVERIFY_IGNORED;
|
import static io.netty.internal.tcnative.SSL.SSL_CVERIFY_IGNORED;
|
||||||
import static java.lang.Integer.MAX_VALUE;
|
import static java.lang.Integer.MAX_VALUE;
|
||||||
@ -218,9 +216,9 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
|||||||
ByteBuffer src = allocateBuffer(srcLen);
|
ByteBuffer src = allocateBuffer(srcLen);
|
||||||
|
|
||||||
ByteBuffer dstTooSmall = allocateBuffer(
|
ByteBuffer dstTooSmall = allocateBuffer(
|
||||||
src.capacity() + MAX_TLS_RECORD_OVERHEAD_LENGTH - 1);
|
src.capacity() + ((ReferenceCountedOpenSslEngine) clientEngine).maxWrapOverhead() - 1);
|
||||||
ByteBuffer dst = allocateBuffer(
|
ByteBuffer dst = allocateBuffer(
|
||||||
src.capacity() + MAX_TLS_RECORD_OVERHEAD_LENGTH);
|
src.capacity() + ((ReferenceCountedOpenSslEngine) clientEngine).maxWrapOverhead());
|
||||||
|
|
||||||
// Check that we fail to wrap if the dst buffers capacity is not at least
|
// Check that we fail to wrap if the dst buffers capacity is not at least
|
||||||
// src.capacity() + ReferenceCountedOpenSslEngine.MAX_TLS_RECORD_OVERHEAD_LENGTH
|
// src.capacity() + ReferenceCountedOpenSslEngine.MAX_TLS_RECORD_OVERHEAD_LENGTH
|
||||||
@ -267,7 +265,7 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
|||||||
ByteBuffer src2 = src.duplicate();
|
ByteBuffer src2 = src.duplicate();
|
||||||
|
|
||||||
ByteBuffer dst = allocateBuffer(src.capacity()
|
ByteBuffer dst = allocateBuffer(src.capacity()
|
||||||
+ MAX_TLS_RECORD_OVERHEAD_LENGTH);
|
+ ((ReferenceCountedOpenSslEngine) clientEngine).maxWrapOverhead());
|
||||||
|
|
||||||
SSLEngineResult result = clientEngine.wrap(new ByteBuffer[] { src, src2 }, dst);
|
SSLEngineResult result = clientEngine.wrap(new ByteBuffer[] { src, src2 }, dst);
|
||||||
assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus());
|
assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus());
|
||||||
@ -311,8 +309,8 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ByteBuffer[] srcs = srcList.toArray(new ByteBuffer[srcList.size()]);
|
ByteBuffer[] srcs = srcList.toArray(new ByteBuffer[srcList.size()]);
|
||||||
|
ByteBuffer dst = allocateBuffer(
|
||||||
ByteBuffer dst = allocateBuffer(MAX_ENCRYPTED_PACKET_LENGTH - 1);
|
((ReferenceCountedOpenSslEngine) clientEngine).maxEncryptedPacketLength() - 1);
|
||||||
|
|
||||||
SSLEngineResult result = clientEngine.wrap(srcs, dst);
|
SSLEngineResult result = clientEngine.wrap(srcs, dst);
|
||||||
assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus());
|
assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus());
|
||||||
@ -330,21 +328,34 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCalculateOutNetBufSizeOverflow() {
|
public void testCalculateOutNetBufSizeOverflow() throws SSLException {
|
||||||
assertEquals(MAX_ENCRYPTED_PACKET_LENGTH,
|
clientSslCtx = SslContextBuilder.forClient()
|
||||||
ReferenceCountedOpenSslEngine.calculateOutNetBufSize(MAX_VALUE, 1));
|
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||||
|
.sslProvider(sslClientProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine clientEngine = null;
|
||||||
|
try {
|
||||||
|
clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
||||||
|
int value = ((ReferenceCountedOpenSslEngine) clientEngine).calculateMaxLengthForWrap(MAX_VALUE, 1);
|
||||||
|
assertTrue("unexpected value: " + value, value > 0);
|
||||||
|
} finally {
|
||||||
|
cleanupClientSslEngine(clientEngine);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCalculateOutNetBufSize0() {
|
public void testCalculateOutNetBufSize0() throws SSLException {
|
||||||
assertEquals(MAX_TLS_RECORD_OVERHEAD_LENGTH,
|
clientSslCtx = SslContextBuilder.forClient()
|
||||||
ReferenceCountedOpenSslEngine.calculateOutNetBufSize(0, 1));
|
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||||
|
.sslProvider(sslClientProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine clientEngine = null;
|
||||||
|
try {
|
||||||
|
clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
||||||
|
assertTrue(((ReferenceCountedOpenSslEngine) clientEngine).calculateMaxLengthForWrap(0, 1) > 0);
|
||||||
|
} finally {
|
||||||
|
cleanupClientSslEngine(clientEngine);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCalculateOutNetBufSizeMaxEncryptedPacketLength() {
|
|
||||||
assertEquals(MAX_ENCRYPTED_PACKET_LENGTH,
|
|
||||||
ReferenceCountedOpenSslEngine.calculateOutNetBufSize(MAX_ENCRYPTED_PACKET_LENGTH + 1, 2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -533,6 +544,323 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
|||||||
testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "ECDHE-RSA-RC4-SHA");
|
testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "ECDHE-RSA-RC4-SHA");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleRecordsInOneBufferWithNonZeroPositionJDKCompatabilityModeOff() throws Exception {
|
||||||
|
SelfSignedCertificate cert = new SelfSignedCertificate();
|
||||||
|
|
||||||
|
clientSslCtx = SslContextBuilder
|
||||||
|
.forClient()
|
||||||
|
.trustManager(cert.cert())
|
||||||
|
.sslProvider(sslClientProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine client = clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine();
|
||||||
|
|
||||||
|
serverSslCtx = SslContextBuilder
|
||||||
|
.forServer(cert.certificate(), cert.privateKey())
|
||||||
|
.sslProvider(sslServerProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine server = serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Choose buffer size small enough that we can put multiple buffers into one buffer and pass it into the
|
||||||
|
// unwrap call without exceed MAX_ENCRYPTED_PACKET_LENGTH.
|
||||||
|
final int plainClientOutLen = 1024;
|
||||||
|
ByteBuffer plainClientOut = allocateBuffer(plainClientOutLen);
|
||||||
|
ByteBuffer plainServerOut = allocateBuffer(server.getSession().getApplicationBufferSize());
|
||||||
|
|
||||||
|
ByteBuffer encClientToServer = allocateBuffer(client.getSession().getPacketBufferSize());
|
||||||
|
|
||||||
|
int positionOffset = 1;
|
||||||
|
// We need to be able to hold 2 records + positionOffset
|
||||||
|
ByteBuffer combinedEncClientToServer = allocateBuffer(
|
||||||
|
encClientToServer.capacity() * 2 + positionOffset);
|
||||||
|
combinedEncClientToServer.position(positionOffset);
|
||||||
|
|
||||||
|
handshake(client, server);
|
||||||
|
|
||||||
|
plainClientOut.limit(plainClientOut.capacity());
|
||||||
|
SSLEngineResult result = client.wrap(plainClientOut, encClientToServer);
|
||||||
|
assertEquals(plainClientOut.capacity(), result.bytesConsumed());
|
||||||
|
assertTrue(result.bytesProduced() > 0);
|
||||||
|
|
||||||
|
encClientToServer.flip();
|
||||||
|
|
||||||
|
// Copy the first record into the combined buffer
|
||||||
|
combinedEncClientToServer.put(encClientToServer);
|
||||||
|
|
||||||
|
plainClientOut.clear();
|
||||||
|
encClientToServer.clear();
|
||||||
|
|
||||||
|
result = client.wrap(plainClientOut, encClientToServer);
|
||||||
|
assertEquals(plainClientOut.capacity(), result.bytesConsumed());
|
||||||
|
assertTrue(result.bytesProduced() > 0);
|
||||||
|
|
||||||
|
encClientToServer.flip();
|
||||||
|
|
||||||
|
// Copy the first record into the combined buffer
|
||||||
|
combinedEncClientToServer.put(encClientToServer);
|
||||||
|
|
||||||
|
encClientToServer.clear();
|
||||||
|
|
||||||
|
combinedEncClientToServer.flip();
|
||||||
|
combinedEncClientToServer.position(positionOffset);
|
||||||
|
|
||||||
|
// Make sure the limit takes positionOffset into account to the content we are looking at is correct.
|
||||||
|
combinedEncClientToServer.limit(
|
||||||
|
combinedEncClientToServer.limit() - positionOffset);
|
||||||
|
final int combinedEncClientToServerLen = combinedEncClientToServer.remaining();
|
||||||
|
|
||||||
|
result = server.unwrap(combinedEncClientToServer, plainServerOut);
|
||||||
|
assertEquals(0, combinedEncClientToServer.remaining());
|
||||||
|
assertEquals(combinedEncClientToServerLen, result.bytesConsumed());
|
||||||
|
assertEquals(plainClientOutLen, result.bytesProduced());
|
||||||
|
} finally {
|
||||||
|
cert.delete();
|
||||||
|
cleanupClientSslEngine(client);
|
||||||
|
cleanupServerSslEngine(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInputTooBigAndFillsUpBuffersJDKCompatabilityModeOff() throws Exception {
|
||||||
|
SelfSignedCertificate cert = new SelfSignedCertificate();
|
||||||
|
|
||||||
|
clientSslCtx = SslContextBuilder
|
||||||
|
.forClient()
|
||||||
|
.trustManager(cert.cert())
|
||||||
|
.sslProvider(sslClientProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine client = clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine();
|
||||||
|
|
||||||
|
serverSslCtx = SslContextBuilder
|
||||||
|
.forServer(cert.certificate(), cert.privateKey())
|
||||||
|
.sslProvider(sslServerProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine server = serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine();
|
||||||
|
|
||||||
|
try {
|
||||||
|
ByteBuffer plainClient = allocateBuffer(MAX_PLAINTEXT_LENGTH + 100);
|
||||||
|
ByteBuffer plainClient2 = allocateBuffer(512);
|
||||||
|
ByteBuffer plainClientTotal = allocateBuffer(plainClient.capacity() + plainClient2.capacity());
|
||||||
|
plainClientTotal.put(plainClient);
|
||||||
|
plainClientTotal.put(plainClient2);
|
||||||
|
plainClient.clear();
|
||||||
|
plainClient2.clear();
|
||||||
|
plainClientTotal.flip();
|
||||||
|
|
||||||
|
// The capacity is designed to trigger an overflow condition.
|
||||||
|
ByteBuffer encClientToServerTooSmall = allocateBuffer(MAX_PLAINTEXT_LENGTH + 28);
|
||||||
|
ByteBuffer encClientToServer = allocateBuffer(client.getSession().getApplicationBufferSize());
|
||||||
|
ByteBuffer encClientToServerTotal = allocateBuffer(client.getSession().getApplicationBufferSize() << 1);
|
||||||
|
ByteBuffer plainServer = allocateBuffer(server.getSession().getApplicationBufferSize() << 1);
|
||||||
|
|
||||||
|
handshake(client, server);
|
||||||
|
|
||||||
|
int plainClientRemaining = plainClient.remaining();
|
||||||
|
int encClientToServerTooSmallRemaining = encClientToServerTooSmall.remaining();
|
||||||
|
SSLEngineResult result = client.wrap(plainClient, encClientToServerTooSmall);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(plainClientRemaining - plainClient.remaining(), result.bytesConsumed());
|
||||||
|
assertEquals(encClientToServerTooSmallRemaining - encClientToServerTooSmall.remaining(),
|
||||||
|
result.bytesProduced());
|
||||||
|
|
||||||
|
result = client.wrap(plainClient, encClientToServerTooSmall);
|
||||||
|
assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus());
|
||||||
|
assertEquals(0, result.bytesConsumed());
|
||||||
|
assertEquals(0, result.bytesProduced());
|
||||||
|
|
||||||
|
plainClientRemaining = plainClient.remaining();
|
||||||
|
int encClientToServerRemaining = encClientToServer.remaining();
|
||||||
|
result = client.wrap(plainClient, encClientToServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(plainClientRemaining, result.bytesConsumed());
|
||||||
|
assertEquals(encClientToServerRemaining - encClientToServer.remaining(), result.bytesProduced());
|
||||||
|
assertEquals(0, plainClient.remaining());
|
||||||
|
|
||||||
|
final int plainClient2Remaining = plainClient2.remaining();
|
||||||
|
encClientToServerRemaining = encClientToServer.remaining();
|
||||||
|
result = client.wrap(plainClient2, encClientToServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(plainClient2Remaining, result.bytesConsumed());
|
||||||
|
assertEquals(encClientToServerRemaining - encClientToServer.remaining(), result.bytesProduced());
|
||||||
|
|
||||||
|
// Concatenate the too small buffer
|
||||||
|
encClientToServerTooSmall.flip();
|
||||||
|
encClientToServer.flip();
|
||||||
|
encClientToServerTotal.put(encClientToServerTooSmall);
|
||||||
|
encClientToServerTotal.put(encClientToServer);
|
||||||
|
encClientToServerTotal.flip();
|
||||||
|
|
||||||
|
// Unwrap in a single call.
|
||||||
|
final int encClientToServerTotalRemaining = encClientToServerTotal.remaining();
|
||||||
|
result = server.unwrap(encClientToServerTotal, plainServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(encClientToServerTotalRemaining, result.bytesConsumed());
|
||||||
|
plainServer.flip();
|
||||||
|
assertEquals(plainClientTotal, plainServer);
|
||||||
|
} finally {
|
||||||
|
cert.delete();
|
||||||
|
cleanupClientSslEngine(client);
|
||||||
|
cleanupServerSslEngine(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPartialPacketUnwrapJDKCompatabilityModeOff() throws Exception {
|
||||||
|
SelfSignedCertificate cert = new SelfSignedCertificate();
|
||||||
|
|
||||||
|
clientSslCtx = SslContextBuilder
|
||||||
|
.forClient()
|
||||||
|
.trustManager(cert.cert())
|
||||||
|
.sslProvider(sslClientProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine client = clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine();
|
||||||
|
|
||||||
|
serverSslCtx = SslContextBuilder
|
||||||
|
.forServer(cert.certificate(), cert.privateKey())
|
||||||
|
.sslProvider(sslServerProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine server = serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine();
|
||||||
|
|
||||||
|
try {
|
||||||
|
ByteBuffer plainClient = allocateBuffer(1024);
|
||||||
|
ByteBuffer plainClient2 = allocateBuffer(512);
|
||||||
|
ByteBuffer plainClientTotal = allocateBuffer(plainClient.capacity() + plainClient2.capacity());
|
||||||
|
plainClientTotal.put(plainClient);
|
||||||
|
plainClientTotal.put(plainClient2);
|
||||||
|
plainClient.clear();
|
||||||
|
plainClient2.clear();
|
||||||
|
plainClientTotal.flip();
|
||||||
|
|
||||||
|
ByteBuffer encClientToServer = allocateBuffer(client.getSession().getPacketBufferSize());
|
||||||
|
ByteBuffer plainServer = allocateBuffer(server.getSession().getApplicationBufferSize());
|
||||||
|
|
||||||
|
handshake(client, server);
|
||||||
|
|
||||||
|
SSLEngineResult result = client.wrap(plainClient, encClientToServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(result.bytesConsumed(), plainClient.capacity());
|
||||||
|
final int encClientLen = result.bytesProduced();
|
||||||
|
|
||||||
|
result = client.wrap(plainClient2, encClientToServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(result.bytesConsumed(), plainClient2.capacity());
|
||||||
|
final int encClientLen2 = result.bytesProduced();
|
||||||
|
|
||||||
|
// Flip so we can read it.
|
||||||
|
encClientToServer.flip();
|
||||||
|
|
||||||
|
// Consume a partial TLS packet.
|
||||||
|
ByteBuffer encClientFirstHalf = encClientToServer.duplicate();
|
||||||
|
encClientFirstHalf.limit(encClientLen / 2);
|
||||||
|
result = server.unwrap(encClientFirstHalf, plainServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(result.bytesConsumed(), encClientLen / 2);
|
||||||
|
encClientToServer.position(result.bytesConsumed());
|
||||||
|
|
||||||
|
// We now have half of the first packet and the whole second packet, so lets decode all but the last byte.
|
||||||
|
ByteBuffer encClientAllButLastByte = encClientToServer.duplicate();
|
||||||
|
final int encClientAllButLastByteLen = encClientAllButLastByte.remaining() - 1;
|
||||||
|
encClientAllButLastByte.limit(encClientAllButLastByte.limit() - 1);
|
||||||
|
result = server.unwrap(encClientAllButLastByte, plainServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(result.bytesConsumed(), encClientAllButLastByteLen);
|
||||||
|
encClientToServer.position(encClientToServer.position() + result.bytesConsumed());
|
||||||
|
|
||||||
|
// Read the last byte and verify the original content has been decrypted.
|
||||||
|
result = server.unwrap(encClientToServer, plainServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(result.bytesConsumed(), 1);
|
||||||
|
plainServer.flip();
|
||||||
|
assertEquals(plainClientTotal, plainServer);
|
||||||
|
} finally {
|
||||||
|
cert.delete();
|
||||||
|
cleanupClientSslEngine(client);
|
||||||
|
cleanupServerSslEngine(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBufferUnderFlowAvoidedIfJDKCompatabilityModeOff() throws Exception {
|
||||||
|
SelfSignedCertificate cert = new SelfSignedCertificate();
|
||||||
|
|
||||||
|
clientSslCtx = SslContextBuilder
|
||||||
|
.forClient()
|
||||||
|
.trustManager(cert.cert())
|
||||||
|
.sslProvider(sslClientProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine client = clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine();
|
||||||
|
|
||||||
|
serverSslCtx = SslContextBuilder
|
||||||
|
.forServer(cert.certificate(), cert.privateKey())
|
||||||
|
.sslProvider(sslServerProvider())
|
||||||
|
.build();
|
||||||
|
SSLEngine server = serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine();
|
||||||
|
|
||||||
|
try {
|
||||||
|
ByteBuffer plainClient = allocateBuffer(1024);
|
||||||
|
plainClient.limit(plainClient.capacity());
|
||||||
|
|
||||||
|
ByteBuffer encClientToServer = allocateBuffer(client.getSession().getPacketBufferSize());
|
||||||
|
ByteBuffer plainServer = allocateBuffer(server.getSession().getApplicationBufferSize());
|
||||||
|
|
||||||
|
handshake(client, server);
|
||||||
|
|
||||||
|
SSLEngineResult result = client.wrap(plainClient, encClientToServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(result.bytesConsumed(), plainClient.capacity());
|
||||||
|
|
||||||
|
// Flip so we can read it.
|
||||||
|
encClientToServer.flip();
|
||||||
|
int remaining = encClientToServer.remaining();
|
||||||
|
|
||||||
|
// We limit the buffer so we have less then the header to read, this should result in an BUFFER_UNDERFLOW.
|
||||||
|
encClientToServer.limit(SslUtils.SSL_RECORD_HEADER_LENGTH - 1);
|
||||||
|
result = server.unwrap(encClientToServer, plainServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(SslUtils.SSL_RECORD_HEADER_LENGTH - 1, result.bytesConsumed());
|
||||||
|
assertEquals(0, result.bytesProduced());
|
||||||
|
remaining -= result.bytesConsumed();
|
||||||
|
|
||||||
|
// We limit the buffer so we can read the header but not the rest, this should result in an
|
||||||
|
// BUFFER_UNDERFLOW.
|
||||||
|
encClientToServer.limit(SslUtils.SSL_RECORD_HEADER_LENGTH);
|
||||||
|
result = server.unwrap(encClientToServer, plainServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(1, result.bytesConsumed());
|
||||||
|
assertEquals(0, result.bytesProduced());
|
||||||
|
remaining -= result.bytesConsumed();
|
||||||
|
|
||||||
|
// We limit the buffer so we can read the header and partly the rest, this should result in an
|
||||||
|
// BUFFER_UNDERFLOW.
|
||||||
|
encClientToServer.limit(
|
||||||
|
SslUtils.SSL_RECORD_HEADER_LENGTH + remaining - 1 - SslUtils.SSL_RECORD_HEADER_LENGTH);
|
||||||
|
result = server.unwrap(encClientToServer, plainServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(encClientToServer.limit() - SslUtils.SSL_RECORD_HEADER_LENGTH, result.bytesConsumed());
|
||||||
|
assertEquals(0, result.bytesProduced());
|
||||||
|
remaining -= result.bytesConsumed();
|
||||||
|
|
||||||
|
// Reset limit so we can read the full record.
|
||||||
|
encClientToServer.limit(remaining);
|
||||||
|
assertEquals(0, encClientToServer.remaining());
|
||||||
|
result = server.unwrap(encClientToServer, plainServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.BUFFER_UNDERFLOW, result.getStatus());
|
||||||
|
assertEquals(0, result.bytesConsumed());
|
||||||
|
assertEquals(0, result.bytesProduced());
|
||||||
|
|
||||||
|
encClientToServer.position(0);
|
||||||
|
result = server.unwrap(encClientToServer, plainServer);
|
||||||
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
assertEquals(remaining, result.bytesConsumed());
|
||||||
|
assertEquals(0, result.bytesProduced());
|
||||||
|
} finally {
|
||||||
|
cert.delete();
|
||||||
|
cleanupClientSslEngine(client);
|
||||||
|
cleanupServerSslEngine(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void testWrapWithDifferentSizes(String protocol, String cipher) throws Exception {
|
private void testWrapWithDifferentSizes(String protocol, String cipher) throws Exception {
|
||||||
assumeTrue(OpenSsl.SUPPORTED_PROTOCOLS_SET.contains(protocol));
|
assumeTrue(OpenSsl.SUPPORTED_PROTOCOLS_SET.contains(protocol));
|
||||||
if (!OpenSsl.isCipherSuiteAvailable(cipher)) {
|
if (!OpenSsl.isCipherSuiteAvailable(cipher)) {
|
||||||
@ -573,7 +901,7 @@ public class OpenSslEngineTest extends SSLEngineTest {
|
|||||||
|
|
||||||
private void testWrapDstBigEnough(SSLEngine engine, int srcLen) throws SSLException {
|
private void testWrapDstBigEnough(SSLEngine engine, int srcLen) throws SSLException {
|
||||||
ByteBuffer src = allocateBuffer(srcLen);
|
ByteBuffer src = allocateBuffer(srcLen);
|
||||||
ByteBuffer dst = allocateBuffer(srcLen + MAX_TLS_RECORD_OVERHEAD_LENGTH);
|
ByteBuffer dst = allocateBuffer(srcLen + ((ReferenceCountedOpenSslEngine) engine).maxWrapOverhead());
|
||||||
|
|
||||||
SSLEngineResult result = engine.wrap(src, dst);
|
SSLEngineResult result = engine.wrap(src, dst);
|
||||||
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
||||||
|
@ -16,26 +16,9 @@
|
|||||||
|
|
||||||
package io.netty.handler.ssl;
|
package io.netty.handler.ssl;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
|
||||||
import static org.hamcrest.CoreMatchers.nullValue;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assume.assumeTrue;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
@ -63,9 +46,27 @@ import io.netty.util.ReferenceCounted;
|
|||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.ObjectUtil;
|
import io.netty.util.internal.ObjectUtil;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
@RunWith(Parameterized.class)
|
@RunWith(Parameterized.class)
|
||||||
public class SniHandlerTest {
|
public class SniHandlerTest {
|
||||||
|
|
||||||
@ -251,14 +252,27 @@ public class SniHandlerTest {
|
|||||||
|
|
||||||
// invalid
|
// invalid
|
||||||
byte[] message = {22, 3, 1, 0, 0};
|
byte[] message = {22, 3, 1, 0, 0};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Push the handshake message.
|
// Push the handshake message.
|
||||||
ch.writeInbound(Unpooled.wrappedBuffer(message));
|
ch.writeInbound(Unpooled.wrappedBuffer(message));
|
||||||
|
// TODO(scott): This should fail becasue the engine should reject zero length records during handshake.
|
||||||
|
// See https://github.com/netty/netty/issues/6348.
|
||||||
|
// fail();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// expected
|
// expected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ch.close();
|
||||||
|
|
||||||
|
// When the channel is closed the SslHandler will write an empty buffer to the channel.
|
||||||
|
ByteBuf buf = ch.readOutbound();
|
||||||
|
// TODO(scott): if the engine is shutdown correctly then this buffer shouldn't be null!
|
||||||
|
// See https://github.com/netty/netty/issues/6348.
|
||||||
|
if (buf != null) {
|
||||||
|
assertFalse(buf.isReadable());
|
||||||
|
buf.release();
|
||||||
|
}
|
||||||
|
|
||||||
assertThat(ch.finish(), is(false));
|
assertThat(ch.finish(), is(false));
|
||||||
assertThat(handler.hostname(), nullValue());
|
assertThat(handler.hostname(), nullValue());
|
||||||
assertThat(handler.sslContext(), is(nettyContext));
|
assertThat(handler.sslContext(), is(nettyContext));
|
||||||
|
@ -388,8 +388,14 @@ public class SslHandlerTest {
|
|||||||
SslHandler handler = new SslHandler(SSLContext.getDefault().createSSLEngine());
|
SslHandler handler = new SslHandler(SSLContext.getDefault().createSSLEngine());
|
||||||
EmbeddedChannel ch = new EmbeddedChannel(handler);
|
EmbeddedChannel ch = new EmbeddedChannel(handler);
|
||||||
|
|
||||||
// Closing the Channel will also produce a close_notify so it is expected to return true.
|
ch.close();
|
||||||
assertTrue(ch.finishAndReleaseAll());
|
|
||||||
|
// When the channel is closed the SslHandler will write an empty buffer to the channel.
|
||||||
|
ByteBuf buf = ch.readOutbound();
|
||||||
|
assertFalse(buf.isReadable());
|
||||||
|
buf.release();
|
||||||
|
|
||||||
|
assertFalse(ch.finishAndReleaseAll());
|
||||||
|
|
||||||
assertTrue(handler.handshakeFuture().cause() instanceof ClosedChannelException);
|
assertTrue(handler.handshakeFuture().cause() instanceof ClosedChannelException);
|
||||||
assertTrue(handler.sslCloseFuture().cause() instanceof ClosedChannelException);
|
assertTrue(handler.sslCloseFuture().cause() instanceof ClosedChannelException);
|
||||||
|
Loading…
Reference in New Issue
Block a user