Home | History | Annotate | Download | only in conscrypt
      1 /* GENERATED SOURCE. DO NOT MODIFY. */
      2 /*
      3  * Copyright (C) 2017 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License
     16  */
     17 
     18 package com.android.org.conscrypt;
     19 
     20 import static com.android.org.conscrypt.TestUtils.getConscryptProvider;
     21 import static com.android.org.conscrypt.TestUtils.getJdkProvider;
     22 import static com.android.org.conscrypt.TestUtils.getProtocols;
     23 import static com.android.org.conscrypt.TestUtils.initSslContext;
     24 import static com.android.org.conscrypt.TestUtils.newTextMessage;
     25 import static org.junit.Assert.assertArrayEquals;
     26 import static org.junit.Assert.assertEquals;
     27 import static org.junit.Assert.assertFalse;
     28 import static org.junit.Assert.assertNull;
     29 import static org.junit.Assert.assertTrue;
     30 import static org.mockito.Matchers.same;
     31 import static org.mockito.Mockito.when;
     32 
     33 import java.io.ByteArrayOutputStream;
     34 import java.io.IOException;
     35 import java.nio.ByteBuffer;
     36 import java.security.NoSuchAlgorithmException;
     37 import java.security.Provider;
     38 import java.util.ArrayList;
     39 import java.util.Arrays;
     40 import java.util.List;
     41 import javax.net.ssl.SSLContext;
     42 import javax.net.ssl.SSLEngine;
     43 import javax.net.ssl.SSLEngineResult;
     44 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
     45 import javax.net.ssl.SSLEngineResult.Status;
     46 import javax.net.ssl.SSLException;
     47 import javax.net.ssl.SSLHandshakeException;
     48 import javax.net.ssl.SSLSession;
     49 import com.android.org.conscrypt.java.security.TestKeyStore;
     50 import org.junit.Test;
     51 import org.junit.runner.RunWith;
     52 import org.junit.runners.Parameterized;
     53 import org.junit.runners.Parameterized.Parameter;
     54 import org.junit.runners.Parameterized.Parameters;
     55 import org.mockito.Matchers;
     56 import org.mockito.Mockito;
     57 
     58 /**
     59  * @hide This class is not part of the Android public SDK API
     60  */
     61 @RunWith(Parameterized.class)
     62 public class ConscryptEngineTest {
     63     private static final int MESSAGE_SIZE = 4096;
     64     private static final int LARGE_MESSAGE_SIZE = 16413;
     65     private static final String[] CIPHERS = TestUtils.getCommonCipherSuites();
     66     private static final String RENEGOTIATION_CIPHER = CIPHERS[CIPHERS.length - 1];
     67 
     68     /**
     69      * @hide This class is not part of the Android public SDK API
     70      */
     71     @SuppressWarnings("ImmutableEnumChecker")
     72     public enum BufferType {
     73         HEAP_ALLOCATOR(BufferAllocator.unpooled()) {
     74             @Override
     75             ByteBuffer newBuffer(int size) {
     76                 return ByteBuffer.allocate(size);
     77             }
     78         },
     79         HEAP_NO_ALLOCATOR(null) {
     80             @Override
     81             ByteBuffer newBuffer(int size) {
     82                 return ByteBuffer.allocate(size);
     83             }
     84         },
     85         DIRECT(null) {
     86             @Override
     87             ByteBuffer newBuffer(int size) {
     88                 return ByteBuffer.allocateDirect(size);
     89             }
     90         };
     91 
     92         abstract ByteBuffer newBuffer(int size);
     93 
     94         BufferType(BufferAllocator allocator) {
     95             this.allocator = allocator;
     96         }
     97 
     98         private final BufferAllocator allocator;
     99     }
    100 
    101     private enum ClientAuth {
    102         NONE {
    103             @Override
    104             void apply(SSLEngine engine) {
    105                 engine.setWantClientAuth(false);
    106                 engine.setNeedClientAuth(false);
    107             }
    108         },
    109         OPTIONAL {
    110             @Override
    111             void apply(SSLEngine engine) {
    112                 engine.setWantClientAuth(true);
    113                 engine.setNeedClientAuth(false);
    114             }
    115         },
    116         REQUIRED {
    117             @Override
    118             void apply(SSLEngine engine) {
    119                 engine.setWantClientAuth(false);
    120                 engine.setNeedClientAuth(true);
    121             }
    122         };
    123 
    124         abstract void apply(SSLEngine engine);
    125     }
    126 
    127     @Parameters(name = "{0}")
    128     public static Iterable<BufferType> data() {
    129         return Arrays.asList(
    130                 BufferType.HEAP_ALLOCATOR, BufferType.HEAP_NO_ALLOCATOR, BufferType.DIRECT);
    131     }
    132 
    133     @Parameter public BufferType bufferType;
    134 
    135     private SSLEngine clientEngine;
    136     private SSLEngine serverEngine;
    137 
    138     @Test
    139     public void closingOutboundBeforeHandshakeShouldCloseAll() throws Exception {
    140         setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
    141         assertFalse(clientEngine.isInboundDone());
    142         assertFalse(clientEngine.isOutboundDone());
    143         assertFalse(serverEngine.isInboundDone());
    144         assertFalse(serverEngine.isOutboundDone());
    145 
    146         clientEngine.closeOutbound();
    147         serverEngine.closeOutbound();
    148 
    149         assertTrue(clientEngine.isInboundDone());
    150         assertTrue(clientEngine.isOutboundDone());
    151         assertTrue(serverEngine.isInboundDone());
    152         assertTrue(serverEngine.isOutboundDone());
    153     }
    154 
    155     @Test
    156     public void closingOutboundAfterHandshakeShouldOnlyCloseOutbound() throws Exception {
    157         setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
    158         doHandshake(true);
    159 
    160         assertFalse(clientEngine.isInboundDone());
    161         assertFalse(clientEngine.isOutboundDone());
    162         assertFalse(serverEngine.isInboundDone());
    163         assertFalse(serverEngine.isOutboundDone());
    164 
    165         clientEngine.closeOutbound();
    166         serverEngine.closeOutbound();
    167 
    168         assertFalse(clientEngine.isInboundDone());
    169         assertTrue(clientEngine.isOutboundDone());
    170         assertFalse(serverEngine.isInboundDone());
    171         assertTrue(serverEngine.isOutboundDone());
    172     }
    173 
    174     @Test
    175     public void closingInboundShouldOnlyCloseInbound() throws Exception {
    176         setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
    177         doHandshake(true);
    178 
    179         assertFalse(clientEngine.isInboundDone());
    180         assertFalse(clientEngine.isOutboundDone());
    181         assertFalse(serverEngine.isInboundDone());
    182         assertFalse(serverEngine.isOutboundDone());
    183 
    184         clientEngine.closeInbound();
    185         serverEngine.closeInbound();
    186 
    187         assertTrue(clientEngine.isInboundDone());
    188         assertFalse(clientEngine.isOutboundDone());
    189         assertTrue(serverEngine.isInboundDone());
    190         assertFalse(serverEngine.isOutboundDone());
    191     }
    192 
    193     @Test
    194     public void mutualAuthWithSameCertsShouldSucceed() throws Exception {
    195         doMutualAuthHandshake(TestKeyStore.getServer(), TestKeyStore.getServer(), ClientAuth.NONE);
    196     }
    197 
    198     @Test
    199     public void mutualAuthWithDifferentCertsShouldSucceed() throws Exception {
    200         doMutualAuthHandshake(TestKeyStore.getClient(), TestKeyStore.getServer(), ClientAuth.NONE);
    201     }
    202 
    203     @Test(expected = SSLHandshakeException.class)
    204     public void mutualAuthWithUntrustedServerShouldFail() throws Exception {
    205         doMutualAuthHandshake(
    206                 TestKeyStore.getClientCA2(), TestKeyStore.getServer(), ClientAuth.NONE);
    207     }
    208 
    209     @Test(expected = SSLHandshakeException.class)
    210     public void mutualAuthWithUntrustedClientShouldFail() throws Exception {
    211         doMutualAuthHandshake(TestKeyStore.getClient(), TestKeyStore.getClient(), ClientAuth.NONE);
    212     }
    213 
    214     @Test
    215     public void optionalClientAuthShouldSucceed() throws Exception {
    216         doMutualAuthHandshake(
    217                 TestKeyStore.getClient(), TestKeyStore.getServer(), ClientAuth.OPTIONAL);
    218     }
    219 
    220     @Test(expected = SSLHandshakeException.class)
    221     public void optionalClientAuthShouldFail() throws Exception {
    222         doMutualAuthHandshake(
    223                 TestKeyStore.getClient(), TestKeyStore.getClient(), ClientAuth.OPTIONAL);
    224     }
    225 
    226     @Test
    227     public void requiredClientAuthShouldSucceed() throws Exception {
    228         doMutualAuthHandshake(
    229                 TestKeyStore.getServer(), TestKeyStore.getServer(), ClientAuth.REQUIRED);
    230     }
    231 
    232     @Test(expected = SSLHandshakeException.class)
    233     public void requiredClientAuthShouldFail() throws Exception {
    234         doMutualAuthHandshake(
    235                 TestKeyStore.getClient(), TestKeyStore.getClient(), ClientAuth.REQUIRED);
    236     }
    237 
    238     @Test
    239     public void exchangeMessages() throws Exception {
    240         setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
    241         doHandshake(true);
    242 
    243         ByteBuffer message = newMessage(MESSAGE_SIZE);
    244         byte[] messageBytes = toArray(message);
    245 
    246         // Wrap the original message and create the encrypted data.
    247         final int numMessages = 100;
    248         ByteBuffer[] encryptedBuffers = new ByteBuffer[numMessages];
    249         for (int i = 0; i < numMessages; ++i) {
    250             List<ByteBuffer> wrapped = wrap(message.duplicate(), clientEngine);
    251             // Small message, we should only have 1 buffer created.
    252             assertEquals(1, wrapped.size());
    253             encryptedBuffers[i] = wrapped.get(0);
    254         }
    255 
    256         // Unwrap the all of the encrypted messages.
    257         byte[] actualBytes = unwrap(encryptedBuffers, serverEngine);
    258         assertEquals(MESSAGE_SIZE * numMessages, actualBytes.length);
    259         for (int i = 0; i < numMessages; ++i) {
    260             int offset = i * MESSAGE_SIZE;
    261             byte[] actualMessageBytes =
    262                     Arrays.copyOfRange(actualBytes, offset, offset + MESSAGE_SIZE);
    263             assertArrayEquals(messageBytes, actualMessageBytes);
    264         }
    265     }
    266 
    267     @Test
    268     public void exchangeLargeMessage() throws Exception {
    269         setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
    270         doHandshake(true);
    271 
    272         ByteBuffer inputBuffer = newMessage(LARGE_MESSAGE_SIZE);
    273         exchangeMessage(inputBuffer, clientEngine, serverEngine);
    274     }
    275 
    276     @Test
    277     public void alpnWithProtocolListShouldSucceed() throws Exception {
    278         setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
    279 
    280         // Configure ALPN protocols
    281         String[] clientAlpnProtocols = new String[]{"http/1.1", "foo", "spdy/2"};
    282         String[] serverAlpnProtocols = new String[]{"spdy/2", "foo", "bar"};
    283 
    284         Conscrypt.setApplicationProtocols(clientEngine, clientAlpnProtocols);
    285         Conscrypt.setApplicationProtocols(serverEngine, serverAlpnProtocols);
    286 
    287         doHandshake(true);
    288         assertEquals("spdy/2", Conscrypt.getApplicationProtocol(clientEngine));
    289         assertEquals("spdy/2", Conscrypt.getApplicationProtocol(serverEngine));
    290     }
    291 
    292     @Test
    293     public void alpnWithProtocolListShouldFail() throws Exception {
    294         setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
    295 
    296         // Configure ALPN protocols
    297         String[] clientAlpnProtocols = new String[]{"http/1.1", "foo", "spdy/2"};
    298         String[] serverAlpnProtocols = new String[]{"h2", "bar", "baz"};
    299 
    300         Conscrypt.setApplicationProtocols(clientEngine, clientAlpnProtocols);
    301         Conscrypt.setApplicationProtocols(serverEngine, serverAlpnProtocols);
    302 
    303         doHandshake(true);
    304         assertNull(Conscrypt.getApplicationProtocol(clientEngine));
    305         assertNull(Conscrypt.getApplicationProtocol(serverEngine));
    306     }
    307 
    308     @Test
    309     public void alpnWithServerProtocolSelectorShouldSucceed() throws Exception {
    310         setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
    311 
    312         // Configure client protocols.
    313         String[] clientAlpnProtocols = new String[]{"http/1.1", "foo", "spdy/2"};
    314         Conscrypt.setApplicationProtocols(clientEngine, clientAlpnProtocols);
    315 
    316         // Configure server selector
    317         ApplicationProtocolSelector selector = Mockito.mock(ApplicationProtocolSelector.class);
    318         when(selector.selectApplicationProtocol(same(serverEngine), Matchers.anyListOf(String.class)))
    319                 .thenReturn("spdy/2");
    320         Conscrypt.setApplicationProtocolSelector(serverEngine, selector);
    321 
    322         doHandshake(true);
    323         assertEquals("spdy/2", Conscrypt.getApplicationProtocol(clientEngine));
    324         assertEquals("spdy/2", Conscrypt.getApplicationProtocol(serverEngine));
    325     }
    326 
    327     @Test
    328     public void alpnWithServerProtocolSelectorShouldFail() throws Exception {
    329         setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
    330 
    331         // Configure client protocols.
    332         String[] clientAlpnProtocols = new String[]{"http/1.1", "foo", "spdy/2"};
    333         Conscrypt.setApplicationProtocols(clientEngine, clientAlpnProtocols);
    334 
    335         // Configure server selector
    336         ApplicationProtocolSelector selector = Mockito.mock(ApplicationProtocolSelector.class);
    337         when(selector.selectApplicationProtocol(same(serverEngine), Matchers.anyListOf(String.class)))
    338                 .thenReturn("h2");
    339         Conscrypt.setApplicationProtocolSelector(serverEngine, selector);
    340 
    341         doHandshake(true);
    342         assertNull(Conscrypt.getApplicationProtocol(clientEngine));
    343         assertNull(Conscrypt.getApplicationProtocol(serverEngine));
    344     }
    345 
    346     /**
    347      * BoringSSL server doesn't support renegotiation. BoringSSL clients do not support
    348      * initiating a renegotiation, only processing a renegotiation initiated by the
    349      * (non-BoringSSL) server. For this reason we test a server-initiated renegotiation with
    350      * a Conscrypt client and a JDK server.
    351      */
    352     @Test
    353     public void serverInitiatedRenegotiationShouldSucceed() throws Exception {
    354         setupClientEngine(getConscryptProvider(), TestKeyStore.getClient());
    355         setupServerEngine(getJdkProvider(), TestKeyStore.getServer());
    356 
    357         // Perform the initial handshake.
    358         doHandshake(true);
    359 
    360         // Send a message from client->server.
    361         exchangeMessage(newMessage(MESSAGE_SIZE), clientEngine, serverEngine);
    362 
    363         // Trigger a renegotiation from the server and send a message back from Server->Client
    364         serverEngine.setEnabledCipherSuites(new String[] {RENEGOTIATION_CIPHER});
    365         serverEngine.beginHandshake();
    366         doHandshake(false);
    367 
    368         exchangeMessage(newMessage(MESSAGE_SIZE), serverEngine, clientEngine);
    369     }
    370 
    371     @Test
    372     public void savedSessionWorksAfterClose() throws Exception {
    373         setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
    374         doHandshake(true);
    375 
    376         SSLSession session = clientEngine.getSession();
    377         String cipherSuite = session.getCipherSuite();
    378 
    379         clientEngine.closeOutbound();
    380         clientEngine.closeInbound();
    381 
    382         assertEquals(cipherSuite, session.getCipherSuite());
    383     }
    384 
    385     private void doMutualAuthHandshake(
    386             TestKeyStore clientKs, TestKeyStore serverKs, ClientAuth clientAuth) throws Exception {
    387         setupEngines(clientKs, serverKs);
    388         clientAuth.apply(serverEngine);
    389         doHandshake(true);
    390         assertEquals(HandshakeStatus.NOT_HANDSHAKING, clientEngine.getHandshakeStatus());
    391         assertEquals(HandshakeStatus.NOT_HANDSHAKING, serverEngine.getHandshakeStatus());
    392     }
    393 
    394     private void doHandshake(boolean beginHandshake) throws SSLException {
    395         ByteBuffer clientApplicationBuffer =
    396                 bufferType.newBuffer(clientEngine.getSession().getApplicationBufferSize());
    397         ByteBuffer clientPacketBuffer =
    398                 bufferType.newBuffer(clientEngine.getSession().getPacketBufferSize());
    399         ByteBuffer serverApplicationBuffer =
    400                 bufferType.newBuffer(serverEngine.getSession().getApplicationBufferSize());
    401         ByteBuffer serverPacketBuffer =
    402                 bufferType.newBuffer(serverEngine.getSession().getPacketBufferSize());
    403         TestUtils.doEngineHandshake(clientEngine, serverEngine, clientApplicationBuffer,
    404                 clientPacketBuffer, serverApplicationBuffer, serverPacketBuffer, beginHandshake);
    405     }
    406 
    407     private void setupEngines(TestKeyStore clientKeyStore, TestKeyStore serverKeyStore) throws SSLException {
    408         setupClientEngine(getConscryptProvider(), clientKeyStore);
    409         setupServerEngine(getConscryptProvider(), serverKeyStore);
    410     }
    411 
    412     private void setupClientEngine(Provider provider, TestKeyStore clientKeyStore)
    413             throws SSLException {
    414         clientEngine = newEngine(provider, clientKeyStore, true);
    415     }
    416 
    417     private void setupServerEngine(Provider provider, TestKeyStore serverKeyStore)
    418             throws SSLException {
    419         serverEngine = newEngine(provider, serverKeyStore, false);
    420     }
    421 
    422     private SSLEngine newEngine(
    423             Provider provider, TestKeyStore keyStore, boolean client) {
    424         SSLContext serverContext = newContext(provider, keyStore);
    425         SSLEngine engine = serverContext.createSSLEngine();
    426         engine.setEnabledCipherSuites(CIPHERS);
    427         engine.setUseClientMode(client);
    428         if (Conscrypt.isConscrypt(engine)) {
    429             Conscrypt.setBufferAllocator(engine, bufferType.allocator);
    430         }
    431         return engine;
    432     }
    433 
    434     private void exchangeMessage(ByteBuffer inputBuffer, SSLEngine src, SSLEngine dest)
    435             throws IOException {
    436         byte[] messageBytes = toArray(inputBuffer);
    437 
    438         // Encrypt the input message.
    439         List<ByteBuffer> encryptedBufferList = wrap(inputBuffer, src);
    440 
    441         // Unwrap the all of the encrypted messages.
    442         ByteBuffer[] encryptedBuffers =
    443                 encryptedBufferList.toArray(new ByteBuffer[encryptedBufferList.size()]);
    444         byte[] actualBytes = unwrap(encryptedBuffers, dest);
    445         assertArrayEquals(messageBytes, actualBytes);
    446     }
    447 
    448     private List<ByteBuffer> wrap(ByteBuffer input, SSLEngine engine) throws SSLException {
    449         // Encrypt the input message.
    450         List<ByteBuffer> wrapped = new ArrayList<ByteBuffer>();
    451         while (input.hasRemaining()) {
    452             ByteBuffer encryptedBuffer =
    453                     bufferType.newBuffer(engine.getSession().getPacketBufferSize());
    454             SSLEngineResult wrapResult = engine.wrap(input, encryptedBuffer);
    455             assertEquals(SSLEngineResult.Status.OK, wrapResult.getStatus());
    456             encryptedBuffer.flip();
    457             wrapped.add(encryptedBuffer);
    458         }
    459         return wrapped;
    460     }
    461 
    462     private byte[] unwrap(ByteBuffer[] encryptedBuffers, SSLEngine engine) throws IOException {
    463         ByteArrayOutputStream cleartextStream = new ByteArrayOutputStream();
    464         int decryptedBufferSize = 8192;
    465         final ByteBuffer encryptedBuffer = combine(encryptedBuffers);
    466         final ByteBuffer decryptedBuffer = bufferType.newBuffer(decryptedBufferSize);
    467         while (encryptedBuffer.hasRemaining()) {
    468             if (!decryptedBuffer.hasRemaining()) {
    469                 decryptedBuffer.clear();
    470             }
    471             int prevPos = decryptedBuffer.position();
    472             SSLEngineResult unwrapResult = engine.unwrap(encryptedBuffer, decryptedBuffer);
    473             SSLEngineResult.Status status = unwrapResult.getStatus();
    474             switch (status) {
    475                 case BUFFER_OVERFLOW:
    476                 case OK: {
    477                     break;
    478                 }
    479                 default: { throw new RuntimeException("Unexpected SSLEngine status: " + status); }
    480             }
    481             int newPos = decryptedBuffer.position();
    482             int bytesProduced = unwrapResult.bytesProduced();
    483             assertEquals(bytesProduced, newPos - prevPos);
    484 
    485             // Add any generated bytes to the output stream.
    486             if (bytesProduced > 0 || status == Status.BUFFER_OVERFLOW) {
    487                 byte[] decryptedBytes = new byte[unwrapResult.bytesProduced()];
    488 
    489                 // Read the chunk that was just written to the output array.
    490                 int limit = decryptedBuffer.limit();
    491                 decryptedBuffer.limit(newPos);
    492                 decryptedBuffer.position(prevPos);
    493                 decryptedBuffer.get(decryptedBytes);
    494 
    495                 // Restore the position and limit.
    496                 decryptedBuffer.limit(limit);
    497 
    498                 // Write the decrypted bytes to the stream.
    499                 cleartextStream.write(decryptedBytes);
    500             }
    501         }
    502 
    503         return cleartextStream.toByteArray();
    504     }
    505 
    506     private ByteBuffer combine(ByteBuffer[] buffers) {
    507         int size = 0;
    508         for (ByteBuffer buffer : buffers) {
    509             size += buffer.remaining();
    510         }
    511         ByteBuffer combined = bufferType.newBuffer(size);
    512         for (ByteBuffer buffer : buffers) {
    513             combined.put(buffer);
    514         }
    515         combined.flip();
    516         return combined;
    517     }
    518 
    519     private ByteBuffer newMessage(int size) {
    520         ByteBuffer buffer = bufferType.newBuffer(size);
    521         buffer.put(newTextMessage(size));
    522         buffer.flip();
    523         return buffer;
    524     }
    525 
    526     private static byte[] toArray(ByteBuffer buffer) {
    527         int pos = buffer.position();
    528         byte[] bytes = new byte[buffer.remaining()];
    529         buffer.get(bytes);
    530         buffer.position(pos);
    531         return bytes;
    532     }
    533 
    534     private static SSLContext newContext(Provider provider, TestKeyStore keyStore) {
    535         try {
    536             SSLContext ctx = SSLContext.getInstance(getProtocols()[0], provider);
    537             return initSslContext(ctx, keyStore);
    538         } catch (NoSuchAlgorithmException e) {
    539             throw new RuntimeException(e);
    540         }
    541     }
    542 }
    543