/* * Copyright 2015 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package io.netty.handler.ssl; import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.internal.tcnative.SSL; import io.netty.util.CharsetUtil; import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.PlatformDependent; import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLParameters; import javax.net.ssl.X509ExtendedKeyManager; import java.net.Socket; import java.nio.ByteBuffer; import java.security.AlgorithmConstraints; import java.security.AlgorithmParameters; import java.security.CryptoPrimitive; import java.security.Key; import java.security.Principal; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Set; import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory; import static io.netty.handler.ssl.ReferenceCountedOpenSslEngine.MAX_PLAINTEXT_LENGTH; import static io.netty.internal.tcnative.SSL.SSL_CVERIFY_IGNORED; import static java.lang.Integer.MAX_VALUE; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; @RunWith(Parameterized.class) public class OpenSslEngineTest extends SSLEngineTest { private static final String PREFERRED_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http2"; private static final String FALLBACK_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http1_1"; @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}, delegate = {2}, useTasks = {3}") public static Collection data() { List params = new ArrayList(); for (BufferType type: BufferType.values()) { params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), false, false }); params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), false, true }); params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), true, false }); params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), true, true }); if (SslProvider.isTlsv13Supported(SslProvider.OPENSSL)) { params.add(new Object[] { type, ProtocolCipherCombo.tlsv13(), false, false }); params.add(new Object[] { type, ProtocolCipherCombo.tlsv13(), false, true }); params.add(new Object[] { type, ProtocolCipherCombo.tlsv13(), true, false }); params.add(new Object[] { type, ProtocolCipherCombo.tlsv13(), true, true }); } } return params; } protected final boolean useTasks; public OpenSslEngineTest(BufferType type, ProtocolCipherCombo cipherCombo, boolean delegate, boolean useTasks) { super(type, cipherCombo, delegate); this.useTasks = useTasks; } @BeforeClass public static void checkOpenSsl() { OpenSsl.ensureAvailability(); } @Override public void tearDown() throws InterruptedException { super.tearDown(); assertEquals("SSL error stack not correctly consumed", 0, SSL.getLastErrorNumber()); } @Override @Test public void testSessionAfterHandshakeKeyManagerFactory() throws Exception { checkShouldUseKeyManagerFactory(); super.testSessionAfterHandshakeKeyManagerFactory(); } @Override @Test public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth() throws Exception { checkShouldUseKeyManagerFactory(); super.testSessionAfterHandshakeKeyManagerFactoryMutualAuth(); } @Override @Test public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth() throws Exception { checkShouldUseKeyManagerFactory(); super.testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(); } @Override @Test public void testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth() throws Exception { checkShouldUseKeyManagerFactory(); super.testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(); } @Override @Test public void testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth() throws Exception { checkShouldUseKeyManagerFactory(); super.testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(); } @Override @Test public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth() throws Exception { checkShouldUseKeyManagerFactory(); super.testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(); } @Override @Test public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth() throws Exception { checkShouldUseKeyManagerFactory(); super.testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(); } @Override public void testHandshakeSession() throws Exception { checkShouldUseKeyManagerFactory(); super.testHandshakeSession(); } @Override @Test public void testSupportedSignatureAlgorithms() throws Exception { checkShouldUseKeyManagerFactory(); super.testSupportedSignatureAlgorithms(); } private static boolean isNpnSupported(String versionString) { String[] versionStringParts = versionString.split(" ", -1); if (versionStringParts.length == 2 && "LibreSSL".equals(versionStringParts[0])) { String[] versionParts = versionStringParts[1].split("\\.", -1); if (versionParts.length == 3) { int major = Integer.parseInt(versionParts[0]); if (major < 2) { return true; } if (major > 2) { return false; } int minor = Integer.parseInt(versionParts[1]); if (minor < 6) { return true; } if (minor > 6) { return false; } int bugfix = Integer.parseInt(versionParts[2]); if (bugfix > 0) { return false; } } } return true; } @Test public void testNpn() throws Exception { String versionString = OpenSsl.versionString(); assumeTrue("LibreSSL 2.6.1 removed NPN support, detected " + versionString, isNpnSupported(versionString)); ApplicationProtocolConfig apn = acceptingNegotiator(Protocol.NPN, PREFERRED_APPLICATION_LEVEL_PROTOCOL); setupHandlers(apn); runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL); } @Test public void testAlpn() throws Exception { assumeTrue(OpenSsl.isAlpnSupported()); ApplicationProtocolConfig apn = acceptingNegotiator(Protocol.ALPN, PREFERRED_APPLICATION_LEVEL_PROTOCOL); setupHandlers(apn); runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL); } @Test public void testAlpnCompatibleProtocolsDifferentClientOrder() throws Exception { assumeTrue(OpenSsl.isAlpnSupported()); ApplicationProtocolConfig clientApn = acceptingNegotiator(Protocol.ALPN, FALLBACK_APPLICATION_LEVEL_PROTOCOL, PREFERRED_APPLICATION_LEVEL_PROTOCOL); ApplicationProtocolConfig serverApn = acceptingNegotiator(Protocol.ALPN, PREFERRED_APPLICATION_LEVEL_PROTOCOL, FALLBACK_APPLICATION_LEVEL_PROTOCOL); setupHandlers(serverApn, clientApn); assertNull(serverException); runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL); } @Test public void testEnablingAnAlreadyDisabledSslProtocol() throws Exception { testEnablingAnAlreadyDisabledSslProtocol(new String[]{SslProtocols.SSL_v2_HELLO}, new String[]{SslProtocols.SSL_v2_HELLO, SslProtocols.TLS_v1_2}); } @Test public void testWrapBuffersNoWritePendingError() throws Exception { clientSslCtx = wrapContext(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); handshake(clientEngine, serverEngine); ByteBuffer src = allocateBuffer(1024 * 10); byte[] data = new byte[src.capacity()]; PlatformDependent.threadLocalRandom().nextBytes(data); src.put(data).flip(); ByteBuffer dst = allocateBuffer(1); // Try to wrap multiple times so we are more likely to hit the issue. for (int i = 0; i < 100; i++) { src.position(0); dst.position(0); assertSame(SSLEngineResult.Status.BUFFER_OVERFLOW, clientEngine.wrap(src, dst).getStatus()); } } finally { cleanupClientSslEngine(clientEngine); cleanupServerSslEngine(serverEngine); } } @Test public void testOnlySmallBufferNeededForWrap() throws Exception { clientSslCtx = wrapContext(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); handshake(clientEngine, serverEngine); // Allocate a buffer which is small enough and set the limit to the capacity to mark its whole content // as readable. int srcLen = 1024; ByteBuffer src = allocateBuffer(srcLen); ByteBuffer dstTooSmall = allocateBuffer( src.capacity() + unwrapEngine(clientEngine).maxWrapOverhead() - 1); ByteBuffer dst = allocateBuffer( src.capacity() + unwrapEngine(clientEngine).maxWrapOverhead()); // Check that we fail to wrap if the dst buffers capacity is not at least // src.capacity() + ReferenceCountedOpenSslEngine.MAX_TLS_RECORD_OVERHEAD_LENGTH SSLEngineResult result = clientEngine.wrap(src, dstTooSmall); assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); assertEquals(0, result.bytesConsumed()); assertEquals(0, result.bytesProduced()); assertEquals(src.remaining(), src.capacity()); assertEquals(dst.remaining(), dst.capacity()); // Check that we can wrap with a dst buffer that has the capacity of // src.capacity() + ReferenceCountedOpenSslEngine.MAX_TLS_RECORD_OVERHEAD_LENGTH result = clientEngine.wrap(src, dst); assertEquals(SSLEngineResult.Status.OK, result.getStatus()); assertEquals(srcLen, result.bytesConsumed()); assertEquals(0, src.remaining()); assertTrue(result.bytesProduced() > srcLen); assertEquals(src.capacity() - result.bytesConsumed(), src.remaining()); assertEquals(dst.capacity() - result.bytesProduced(), dst.remaining()); } finally { cleanupClientSslEngine(clientEngine); cleanupServerSslEngine(serverEngine); } } @Test public void testNeededDstCapacityIsCorrectlyCalculated() throws Exception { clientSslCtx = wrapContext(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); handshake(clientEngine, serverEngine); ByteBuffer src = allocateBuffer(1024); ByteBuffer src2 = src.duplicate(); ByteBuffer dst = allocateBuffer(src.capacity() + unwrapEngine(clientEngine).maxWrapOverhead()); SSLEngineResult result = clientEngine.wrap(new ByteBuffer[] { src, src2 }, dst); assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); assertEquals(0, src.position()); assertEquals(0, src2.position()); assertEquals(0, dst.position()); assertEquals(0, result.bytesConsumed()); assertEquals(0, result.bytesProduced()); } finally { cleanupClientSslEngine(clientEngine); cleanupServerSslEngine(serverEngine); } } @Test public void testSrcsLenOverFlowCorrectlyHandled() throws Exception { clientSslCtx = wrapContext(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); handshake(clientEngine, serverEngine); ByteBuffer src = allocateBuffer(1024); List srcList = new ArrayList(); long srcsLen = 0; long maxLen = ((long) MAX_VALUE) * 2; while (srcsLen < maxLen) { ByteBuffer dup = src.duplicate(); srcList.add(dup); srcsLen += dup.capacity(); } ByteBuffer[] srcs = srcList.toArray(new ByteBuffer[0]); ByteBuffer dst = allocateBuffer( unwrapEngine(clientEngine).maxEncryptedPacketLength() - 1); SSLEngineResult result = clientEngine.wrap(srcs, dst); assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); for (ByteBuffer buffer : srcs) { assertEquals(0, buffer.position()); } assertEquals(0, dst.position()); assertEquals(0, result.bytesConsumed()); assertEquals(0, result.bytesProduced()); } finally { cleanupClientSslEngine(clientEngine); cleanupServerSslEngine(serverEngine); } } @Test public void testCalculateOutNetBufSizeOverflow() throws SSLException { clientSslCtx = wrapContext(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .protocols(protocols()) .ciphers(ciphers()) .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 public void testCalculateOutNetBufSize0() throws SSLException { clientSslCtx = wrapContext(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine clientEngine = null; try { clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); assertTrue(((ReferenceCountedOpenSslEngine) clientEngine).calculateMaxLengthForWrap(0, 1) > 0); } finally { cleanupClientSslEngine(clientEngine); } } @Test public void testCorrectlyCalculateSpaceForAlert() throws Exception { testCorrectlyCalculateSpaceForAlert(true); } @Test public void testCorrectlyCalculateSpaceForAlertJDKCompatabilityModeOff() throws Exception { testCorrectlyCalculateSpaceForAlert(false); } private void testCorrectlyCalculateSpaceForAlert(boolean jdkCompatabilityMode) throws Exception { SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); clientSslCtx = wrapContext(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { if (jdkCompatabilityMode) { clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); } else { clientEngine = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverEngine = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); } handshake(clientEngine, serverEngine); // This should produce an alert clientEngine.closeOutbound(); ByteBuffer empty = allocateBuffer(0); ByteBuffer dst = allocateBuffer(clientEngine.getSession().getPacketBufferSize()); // Limit to something that is guaranteed to be too small to hold an SSL Record. dst.limit(1); // As we called closeOutbound() before this should produce a BUFFER_OVERFLOW. SSLEngineResult result = clientEngine.wrap(empty, dst); assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); // This must calculate a length that can hold an alert at least (or more). dst.limit(dst.capacity()); result = clientEngine.wrap(empty, dst); assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); // flip the buffer so we can verify we produced a full length buffer. dst.flip(); int length = SslUtils.getEncryptedPacketLength(new ByteBuffer[] { dst }, 0); assertEquals(length, dst.remaining()); } finally { cleanupClientSslEngine(clientEngine); cleanupServerSslEngine(serverEngine); ssc.delete(); } } @Override protected void mySetupMutualAuthServerInitSslHandler(SslHandler handler) { ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) handler.engine(); engine.setVerify(SSL_CVERIFY_IGNORED, 1); } @Test public void testWrapWithDifferentSizesTLSv1() throws Exception { clientSslCtx = wrapContext(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .build()); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .build()); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "AES128-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "ECDHE-RSA-AES128-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "AECDH-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "CAMELLIA128-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "SEED-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "RC4-MD5"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "AES256-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "ADH-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "EDH-RSA-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "ADH-RC4-MD5"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "IDEA-CBC-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "RC4-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "CAMELLIA256-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "AECDH-RC4-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "ECDHE-RSA-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "ECDHE-RSA-AES256-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1, "ECDHE-RSA-RC4-SHA"); } @Test public void testWrapWithDifferentSizesTLSv1_1() throws Exception { clientSslCtx = wrapContext(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .build()); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .build()); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "ECDHE-RSA-AES256-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "AES256-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "CAMELLIA256-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "ECDHE-RSA-AES256-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "SEED-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "CAMELLIA128-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "IDEA-CBC-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "AECDH-RC4-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "ADH-RC4-MD5"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "RC4-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "ECDHE-RSA-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "EDH-RSA-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "AECDH-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "ADH-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_1, "DES-CBC3-SHA"); } @Test public void testWrapWithDifferentSizesTLSv1_2() throws Exception { clientSslCtx = wrapContext(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .build()); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .build()); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "AES128-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "ECDHE-RSA-AES128-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "AES128-GCM-SHA256"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "ECDHE-RSA-AES256-SHA384"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "AECDH-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "AES256-GCM-SHA384"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "AES256-SHA256"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "ECDHE-RSA-AES128-GCM-SHA256"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "ECDHE-RSA-AES128-SHA256"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "CAMELLIA128-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "SEED-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "RC4-MD5"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "AES256-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "ADH-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "EDH-RSA-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "ADH-RC4-MD5"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "RC4-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "CAMELLIA256-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "AES128-SHA256"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "AECDH-RC4-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "ECDHE-RSA-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "ECDHE-RSA-AES256-GCM-SHA384"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "ECDHE-RSA-AES256-SHA"); testWrapWithDifferentSizes(SslProtocols.TLS_v1_2, "ECDHE-RSA-RC4-SHA"); } @Test public void testWrapWithDifferentSizesSSLv3() throws Exception { clientSslCtx = wrapContext(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .build()); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .build()); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "ADH-AES128-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "ADH-CAMELLIA128-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "AECDH-AES128-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "AECDH-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "CAMELLIA128-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "DHE-RSA-AES256-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "SEED-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "RC4-MD5"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "ADH-AES256-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "ADH-SEED-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "ADH-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "EDH-RSA-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "ADH-RC4-MD5"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "IDEA-CBC-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "DHE-RSA-AES128-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "RC4-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "CAMELLIA256-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "AECDH-RC4-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "DHE-RSA-SEED-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "AECDH-AES256-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "ECDHE-RSA-DES-CBC3-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "ADH-CAMELLIA256-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "DHE-RSA-CAMELLIA256-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "DHE-RSA-CAMELLIA128-SHA"); testWrapWithDifferentSizes(SslProtocols.SSL_v3, "ECDHE-RSA-RC4-SHA"); } @Test public void testMultipleRecordsInOneBufferWithNonZeroPositionJDKCompatabilityModeOff() throws Exception { SelfSignedCertificate cert = new SelfSignedCertificate(); clientSslCtx = wrapContext(SslContextBuilder .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverSslCtx = wrapContext(SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine server = wrapEngine(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 = wrapContext(SslContextBuilder .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverSslCtx = wrapContext(SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine server = wrapEngine(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 = wrapContext(SslContextBuilder .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverSslCtx = wrapContext(SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine server = wrapEngine(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 = wrapContext(SslContextBuilder .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverSslCtx = wrapContext(SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine server = wrapEngine(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 { assumeTrue(OpenSsl.SUPPORTED_PROTOCOLS_SET.contains(protocol)); if (!OpenSsl.isCipherSuiteAvailable(cipher)) { return; } SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); clientEngine.setEnabledCipherSuites(new String[] { cipher }); clientEngine.setEnabledProtocols(new String[] { protocol }); serverEngine.setEnabledCipherSuites(new String[] { cipher }); serverEngine.setEnabledProtocols(new String[] { protocol }); try { handshake(clientEngine, serverEngine); } catch (SSLException e) { if (e.getMessage().contains("unsupported protocol") || e.getMessage().contains("no protocols available")) { Assume.assumeNoException(protocol + " not supported with cipher " + cipher, e); } throw e; } int srcLen = 64; do { testWrapDstBigEnough(clientEngine, srcLen); srcLen += 64; } while (srcLen < MAX_PLAINTEXT_LENGTH); testWrapDstBigEnough(clientEngine, MAX_PLAINTEXT_LENGTH); } finally { cleanupClientSslEngine(clientEngine); cleanupServerSslEngine(serverEngine); } } private void testWrapDstBigEnough(SSLEngine engine, int srcLen) throws SSLException { ByteBuffer src = allocateBuffer(srcLen); ByteBuffer dst = allocateBuffer(srcLen + unwrapEngine(engine).maxWrapOverhead()); SSLEngineResult result = engine.wrap(src, dst); assertEquals(SSLEngineResult.Status.OK, result.getStatus()); int consumed = result.bytesConsumed(); int produced = result.bytesProduced(); assertEquals(srcLen, consumed); assertTrue(produced > consumed); dst.flip(); assertEquals(produced, dst.remaining()); assertFalse(src.hasRemaining()); } @Test public void testSNIMatchersDoesNotThrow() throws Exception { assumeTrue(PlatformDependent.javaVersion() >= 8); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { SSLParameters parameters = new SSLParameters(); Java8SslTestUtils.setSNIMatcher(parameters, EmptyArrays.EMPTY_BYTES); engine.setSSLParameters(parameters); } finally { cleanupServerSslEngine(engine); ssc.delete(); } } @Test public void testSNIMatchersWithSNINameWithUnderscore() throws Exception { assumeTrue(PlatformDependent.javaVersion() >= 8); byte[] name = "rb8hx3pww30y3tvw0mwy.v1_1".getBytes(CharsetUtil.UTF_8); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { SSLParameters parameters = new SSLParameters(); Java8SslTestUtils.setSNIMatcher(parameters, name); engine.setSSLParameters(parameters); assertTrue(unwrapEngine(engine).checkSniHostnameMatch(name)); assertFalse(unwrapEngine(engine).checkSniHostnameMatch("other".getBytes(CharsetUtil.UTF_8))); } finally { cleanupServerSslEngine(engine); ssc.delete(); } } @Test(expected = IllegalArgumentException.class) public void testAlgorithmConstraintsThrows() throws Exception { SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { SSLParameters parameters = new SSLParameters(); parameters.setAlgorithmConstraints(new AlgorithmConstraints() { @Override public boolean permits( Set primitives, String algorithm, AlgorithmParameters parameters) { return false; } @Override public boolean permits(Set primitives, Key key) { return false; } @Override public boolean permits( Set primitives, String algorithm, Key key, AlgorithmParameters parameters) { return false; } }); engine.setSSLParameters(parameters); } finally { cleanupServerSslEngine(engine); ssc.delete(); } } private static void runTasksIfNeeded(SSLEngine engine) { if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { for (;;) { Runnable task = engine.getDelegatedTask(); if (task == null) { assertNotEquals(HandshakeStatus.NEED_TASK, engine.getHandshakeStatus()); break; } task.run(); } } } @Test public void testExtractMasterkeyWorksCorrectly() throws Exception { if (protocolCipherCombo != ProtocolCipherCombo.tlsv12()) { return; } SelfSignedCertificate cert = new SelfSignedCertificate(); serverSslCtx = wrapContext(SslContextBuilder.forServer(cert.key(), cert.cert()) .protocols(protocols()) .ciphers(ciphers()) .sslProvider(SslProvider.OPENSSL).build()); final SSLEngine serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); clientSslCtx = wrapContext(SslContextBuilder.forClient() .trustManager(cert.certificate()) .protocols(protocols()) .ciphers(ciphers()) .sslProvider(SslProvider.OPENSSL).build()); final SSLEngine clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); final String enabledCipher = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"; try { //lets set the cipher suite to a specific one with DHE assumeTrue("The diffie hellman cipher is not supported on your runtime.", Arrays.asList(clientEngine.getSupportedCipherSuites()) .contains(enabledCipher)); //https://www.ietf.org/rfc/rfc5289.txt //For cipher suites ending with _SHA256, the PRF is the TLS PRF //[RFC5246] with SHA-256 as the hash function. The MAC is HMAC //[RFC2104] with SHA-256 as the hash function. clientEngine.setEnabledCipherSuites(new String[] { enabledCipher }); serverEngine.setEnabledCipherSuites(new String[] { enabledCipher }); int appBufferMax = clientEngine.getSession().getApplicationBufferSize(); int netBufferMax = clientEngine.getSession().getPacketBufferSize(); /* * We'll make the input buffers a bit bigger than the max needed * size, so that unwrap()s following a successful data transfer * won't generate BUFFER_OVERFLOWS. */ ByteBuffer clientIn = ByteBuffer.allocate(appBufferMax + 50); ByteBuffer serverIn = ByteBuffer.allocate(appBufferMax + 50); ByteBuffer cTOs = ByteBuffer.allocate(netBufferMax); ByteBuffer sTOc = ByteBuffer.allocate(netBufferMax); ByteBuffer clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes(CharsetUtil.US_ASCII)); ByteBuffer serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes(CharsetUtil.US_ASCII)); // This implementation is largely imitated from // https://docs.oracle.com/javase/8/docs/technotes/ // guides/security/jsse/samples/sslengine/SSLEngineSimpleDemo.java // It has been simplified however without the need for running delegation tasks // Do handshake for SSL // A typical handshake will usually contain the following steps: // 1. wrap: ClientHello // 2. unwrap: ServerHello/Cert/ServerHelloDone // 3. wrap: ClientKeyExchange // 4. wrap: ChangeCipherSpec // 5. wrap: Finished // 6. unwrap: ChangeCipherSpec // 7. unwrap: Finished //set a for loop; instead of a while loop to guarantee we quit out eventually boolean asserted = false; for (int i = 0; i < 1000; i++) { clientEngine.wrap(clientOut, cTOs); serverEngine.wrap(serverOut, sTOc); cTOs.flip(); sTOc.flip(); runTasksIfNeeded(clientEngine); runTasksIfNeeded(serverEngine); clientEngine.unwrap(sTOc, clientIn); serverEngine.unwrap(cTOs, serverIn); runTasksIfNeeded(clientEngine); runTasksIfNeeded(serverEngine); // check when the application data has fully been consumed and sent // for both the client and server if ((clientOut.limit() == serverIn.position()) && (serverOut.limit() == clientIn.position())) { byte[] serverRandom = SSL.getServerRandom(unwrapEngine(serverEngine).sslPointer()); byte[] clientRandom = SSL.getClientRandom(unwrapEngine(clientEngine).sslPointer()); byte[] serverMasterKey = SSL.getMasterKey(unwrapEngine(serverEngine).sslPointer()); byte[] clientMasterKey = SSL.getMasterKey(unwrapEngine(clientEngine).sslPointer()); asserted = true; assertArrayEquals(serverMasterKey, clientMasterKey); // let us re-read the encrypted data and decrypt it ourselves! cTOs.flip(); sTOc.flip(); // See https://tools.ietf.org/html/rfc5246#section-6.3: // key_block = PRF(SecurityParameters.master_secret, "key expansion", // SecurityParameters.server_random + SecurityParameters.client_random); // // partitioned: // client_write_MAC_secret[SecurityParameters.hash_size] // server_write_MAC_secret[SecurityParameters.hash_size] // client_write_key[SecurityParameters.key_material_length] // server_write_key[SecurityParameters.key_material_length] int keySize = 16; // AES is 16 bytes or 128 bits int macSize = 32; // SHA256 is 32 bytes or 256 bits int keyBlockSize = (2 * keySize) + (2 * macSize); byte[] seed = new byte[serverRandom.length + clientRandom.length]; System.arraycopy(serverRandom, 0, seed, 0, serverRandom.length); System.arraycopy(clientRandom, 0, seed, serverRandom.length, clientRandom.length); byte[] keyBlock = PseudoRandomFunction.hash(serverMasterKey, "key expansion".getBytes(CharsetUtil.US_ASCII), seed, keyBlockSize, "HmacSha256"); int offset = 0; byte[] clientWriteMac = Arrays.copyOfRange(keyBlock, offset, offset + macSize); offset += macSize; byte[] serverWriteMac = Arrays.copyOfRange(keyBlock, offset, offset + macSize); offset += macSize; byte[] clientWriteKey = Arrays.copyOfRange(keyBlock, offset, offset + keySize); offset += keySize; byte[] serverWriteKey = Arrays.copyOfRange(keyBlock, offset, offset + keySize); offset += keySize; //advance the cipher text by 5 //to take into account the TLS Record Header cTOs.position(cTOs.position() + 5); byte[] ciphertext = new byte[cTOs.remaining()]; cTOs.get(ciphertext); //the initialization vector is the first 16 bytes (128 bits) of the payload byte[] clientWriteIV = Arrays.copyOfRange(ciphertext, 0, 16); ciphertext = Arrays.copyOfRange(ciphertext, 16, ciphertext.length); SecretKeySpec secretKey = new SecretKeySpec(clientWriteKey, "AES"); final IvParameterSpec ivForCBC = new IvParameterSpec(clientWriteIV); Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, secretKey, ivForCBC); byte[] plaintext = cipher.doFinal(ciphertext); assertTrue(new String(plaintext).startsWith("Hi Server, I'm Client")); break; } else { cTOs.compact(); sTOc.compact(); } } assertTrue("The assertions were never executed.", asserted); } finally { cleanupClientSslEngine(clientEngine); cleanupServerSslEngine(serverEngine); cert.delete(); } } @Test(expected = SSLException.class) public void testNoKeyFound() throws Exception { checkShouldUseKeyManagerFactory(); clientSslCtx = wrapContext(SslContextBuilder .forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = wrapContext(SslContextBuilder .forServer(new X509ExtendedKeyManager() { @Override public String[] getClientAliases(String keyType, Principal[] issuers) { return new String[0]; } @Override public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { return null; } @Override public String[] getServerAliases(String keyType, Principal[] issuers) { return new String[0]; } @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { return null; } @Override public X509Certificate[] getCertificateChain(String alias) { return new X509Certificate[0]; } @Override public PrivateKey getPrivateKey(String alias) { return null; } }) .sslProvider(sslServerProvider()) .protocols(protocols()) .ciphers(ciphers()) .build()); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { handshake(client, server); } finally { cleanupClientSslEngine(client); cleanupServerSslEngine(server); } } @Override @Test public void testSessionLocalWhenNonMutualWithKeyManager() throws Exception { checkShouldUseKeyManagerFactory(); super.testSessionLocalWhenNonMutualWithKeyManager(); } @Override public void testSessionLocalWhenNonMutualWithoutKeyManager() throws Exception { // This only really works when the KeyManagerFactory is supported as otherwise we not really know when // we need to provide a cert. assumeTrue(OpenSsl.supportsKeyManagerFactory()); super.testSessionLocalWhenNonMutualWithoutKeyManager(); } @Test public void testDefaultTLS1NotAcceptedByDefaultServer() throws Exception { testDefaultTLS1NotAcceptedByDefault(null, SslProtocols.TLS_v1); } @Test public void testDefaultTLS11NotAcceptedByDefaultServer() throws Exception { testDefaultTLS1NotAcceptedByDefault(null, SslProtocols.TLS_v1_1); } @Test public void testDefaultTLS1NotAcceptedByDefaultClient() throws Exception { testDefaultTLS1NotAcceptedByDefault(SslProtocols.TLS_v1, null); } @Test public void testDefaultTLS11NotAcceptedByDefaultClient() throws Exception { testDefaultTLS1NotAcceptedByDefault(SslProtocols.TLS_v1_1, null); } private void testDefaultTLS1NotAcceptedByDefault(String clientProtocol, String serverProtocol) throws Exception { SslContextBuilder clientCtxBuilder = SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .sslContextProvider(clientSslContextProvider()); if (clientProtocol != null) { clientCtxBuilder.protocols(clientProtocol); } clientSslCtx = wrapContext(clientCtxBuilder.build()); SelfSignedCertificate ssc = new SelfSignedCertificate(); SslContextBuilder serverCtxBuilder = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .sslContextProvider(serverSslContextProvider()); if (serverProtocol != null) { serverCtxBuilder.protocols(serverProtocol); } serverSslCtx = wrapContext(serverCtxBuilder.build()); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { handshake(client, server); fail(); } catch (SSLHandshakeException expected) { // expected } finally { cleanupClientSslEngine(client); cleanupServerSslEngine(server); ssc.delete(); } } @Override protected SslProvider sslClientProvider() { return SslProvider.OPENSSL; } @Override protected SslProvider sslServerProvider() { return SslProvider.OPENSSL; } private static ApplicationProtocolConfig acceptingNegotiator(Protocol protocol, String... supportedProtocols) { return new ApplicationProtocolConfig(protocol, SelectorFailureBehavior.NO_ADVERTISE, SelectedListenerFailureBehavior.ACCEPT, supportedProtocols); } @Override protected SSLEngine wrapEngine(SSLEngine engine) { if (PlatformDependent.javaVersion() >= 8) { return Java8SslTestUtils.wrapSSLEngineForTesting(engine); } return engine; } ReferenceCountedOpenSslEngine unwrapEngine(SSLEngine engine) { if (engine instanceof JdkSslEngine) { return (ReferenceCountedOpenSslEngine) ((JdkSslEngine) engine).getWrappedEngine(); } return (ReferenceCountedOpenSslEngine) engine; } @SuppressWarnings("deprecation") @Override protected SslContext wrapContext(SslContext context) { if (context instanceof OpenSslContext) { ((OpenSslContext) context).setUseTasks(useTasks); // Explicit enable the session cache as its disabled by default on the client side. ((OpenSslContext) context).sessionContext().setSessionCacheEnabled(true); } return context; } @Override @Test public void testSessionCache() throws Exception { assumeTrue(OpenSsl.isSessionCacheSupported()); super.testSessionCache(); assertSessionContext(clientSslCtx); assertSessionContext(serverSslCtx); } @Override @Test public void testSessionCacheTimeout() throws Exception { assumeTrue(OpenSsl.isSessionCacheSupported()); super.testSessionCacheTimeout(); } @Override @Test public void testSessionCacheSize() throws Exception { assumeTrue(OpenSsl.isSessionCacheSupported()); super.testSessionCacheSize(); } private static void assertSessionContext(SslContext context) { if (context == null) { return; } OpenSslSessionContext serverSessionCtx = (OpenSslSessionContext) context.sessionContext(); assertTrue(serverSessionCtx.isSessionCacheEnabled()); if (serverSessionCtx.getIds().hasMoreElements()) { serverSessionCtx.setSessionCacheEnabled(false); assertFalse(serverSessionCtx.getIds().hasMoreElements()); assertFalse(serverSessionCtx.isSessionCacheEnabled()); } } @Override protected void assertSessionReusedForEngine(SSLEngine clientEngine, SSLEngine serverEngine, boolean reuse) { assertEquals(reuse, unwrapEngine(clientEngine).isSessionReused()); assertEquals(reuse, unwrapEngine(serverEngine).isSessionReused()); } @Override protected boolean isSessionMaybeReused(SSLEngine engine) { return unwrapEngine(engine).isSessionReused(); } }