Home | History | Annotate | Download | only in ssl
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.conscrypt.javax.net.ssl;
     18 
     19 import static org.conscrypt.TestUtils.UTF_8;
     20 import static org.junit.Assert.assertArrayEquals;
     21 import static org.junit.Assert.assertEquals;
     22 import static org.junit.Assert.assertFalse;
     23 import static org.junit.Assert.assertNotNull;
     24 import static org.junit.Assert.assertNotSame;
     25 import static org.junit.Assert.assertNull;
     26 import static org.junit.Assert.assertTrue;
     27 import static org.junit.Assert.fail;
     28 
     29 import java.io.IOException;
     30 import java.nio.ByteBuffer;
     31 import java.util.Arrays;
     32 import java.util.concurrent.Callable;
     33 import java.util.concurrent.CountDownLatch;
     34 import java.util.concurrent.ExecutorService;
     35 import java.util.concurrent.Executors;
     36 import java.util.concurrent.Future;
     37 import javax.crypto.SecretKey;
     38 import javax.crypto.spec.SecretKeySpec;
     39 import javax.net.ssl.KeyManager;
     40 import javax.net.ssl.SSLContext;
     41 import javax.net.ssl.SSLEngine;
     42 import javax.net.ssl.SSLEngineResult;
     43 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
     44 import javax.net.ssl.SSLEngineResult.Status;
     45 import javax.net.ssl.SSLException;
     46 import javax.net.ssl.SSLHandshakeException;
     47 import javax.net.ssl.SSLParameters;
     48 import javax.net.ssl.SSLSession;
     49 import javax.net.ssl.X509ExtendedKeyManager;
     50 import libcore.java.security.StandardNames;
     51 import org.conscrypt.Conscrypt;
     52 import org.conscrypt.TestUtils;
     53 import org.conscrypt.java.security.TestKeyStore;
     54 import org.junit.Test;
     55 import org.junit.runner.RunWith;
     56 import org.junit.runners.JUnit4;
     57 
     58 @RunWith(JUnit4.class)
     59 public class SSLEngineTest {
     60     @Test
     61     public void test_SSLEngine_defaultConfiguration() throws Exception {
     62         SSLConfigurationAsserts.assertSSLEngineDefaultConfiguration(
     63                 TestSSLContext.create().clientContext.createSSLEngine());
     64     }
     65 
     66     @Test
     67     public void test_SSLEngine_getSupportedCipherSuites_returnsCopies() throws Exception {
     68         TestSSLContext c = TestSSLContext.create();
     69         SSLEngine e = c.clientContext.createSSLEngine();
     70         assertNotSame(e.getSupportedCipherSuites(), e.getSupportedCipherSuites());
     71         c.close();
     72     }
     73 
     74     @Test
     75     public void test_SSLEngine_getSupportedCipherSuites_connect() throws Exception {
     76         // note the rare usage of non-RSA keys
     77         TestKeyStore testKeyStore = new TestKeyStore.Builder()
     78                                             .keyAlgorithms("RSA", "DSA", "EC", "EC_RSA")
     79                                             .aliasPrefix("rsa-dsa-ec")
     80                                             .ca(true)
     81                                             .build();
     82         test_SSLEngine_getSupportedCipherSuites_connect(testKeyStore, false);
     83         test_SSLEngine_getSupportedCipherSuites_connect(testKeyStore, true);
     84     }
     85 
     86     // http://b/18554122
     87     @Test
     88     public void test_SSLEngine_underflowsOnEmptyBuffersDuringHandshake() throws Exception {
     89         final SSLEngine sslEngine = SSLContext.getDefault().createSSLEngine();
     90         sslEngine.setUseClientMode(false);
     91         ByteBuffer input = ByteBuffer.allocate(1024);
     92         input.flip();
     93         ByteBuffer output = ByteBuffer.allocate(1024);
     94         sslEngine.beginHandshake();
     95         assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, sslEngine.getHandshakeStatus());
     96         SSLEngineResult result = sslEngine.unwrap(input, output);
     97         assertEquals(SSLEngineResult.Status.BUFFER_UNDERFLOW, result.getStatus());
     98         assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus());
     99     }
    100 
    101     // http://b/18554122
    102     @Test
    103     public void test_SSLEngine_underflowsOnEmptyBuffersAfterHandshake() throws Exception {
    104         // Note that create performs the handshake.
    105         final TestSSLEnginePair engines = TestSSLEnginePair.create(null /* hooks */);
    106         ByteBuffer input = ByteBuffer.allocate(1024);
    107         input.flip();
    108         ByteBuffer output = ByteBuffer.allocate(1024);
    109         assertEquals(SSLEngineResult.Status.BUFFER_UNDERFLOW,
    110                 engines.client.unwrap(input, output).getStatus());
    111     }
    112 
    113     private void test_SSLEngine_getSupportedCipherSuites_connect(
    114             TestKeyStore testKeyStore, boolean secureRenegotiation) throws Exception {
    115         KeyManager pskKeyManager =
    116                 PSKKeyManagerProxy.getConscryptPSKKeyManager(new PSKKeyManagerProxy() {
    117                     @Override
    118                     protected SecretKey getKey(
    119                             String identityHint, String identity, SSLEngine engine) {
    120                         return new SecretKeySpec("Just an arbitrary key".getBytes(UTF_8), "RAW");
    121                     }
    122                 });
    123         TestSSLContext c = TestSSLContext.newBuilder()
    124                                    .client(testKeyStore)
    125                                    .server(testKeyStore)
    126                                    .additionalClientKeyManagers(new KeyManager[] {pskKeyManager})
    127                                    .additionalServerKeyManagers(new KeyManager[] {pskKeyManager})
    128                                    .build();
    129 
    130         // Create a TestSSLContext where the KeyManager returns wrong (randomly generated) private
    131         // keys, matching the algorithm and parameters of the correct keys.
    132         // I couldn't find a more elegant way to achieve this other than temporarily replacing the
    133         // first X509ExtendedKeyManager element of TestKeyStore.keyManagers while invoking
    134         // TestSSLContext.create.
    135         TestSSLContext cWithWrongPrivateKeys;
    136         {
    137             // Create a RandomPrivateKeyX509ExtendedKeyManager based on the first
    138             // X509ExtendedKeyManager in c.serverKeyManagers.
    139             KeyManager randomPrivateKeyX509ExtendedKeyManager = null;
    140             for (KeyManager keyManager : c.serverKeyManagers) {
    141                 if (keyManager instanceof X509ExtendedKeyManager) {
    142                     randomPrivateKeyX509ExtendedKeyManager =
    143                             new RandomPrivateKeyX509ExtendedKeyManager(
    144                                     (X509ExtendedKeyManager) keyManager);
    145                     break;
    146                 }
    147             }
    148             if (randomPrivateKeyX509ExtendedKeyManager == null) {
    149                 fail("No X509ExtendedKeyManager in c.serverKeyManagers");
    150             }
    151 
    152             // Find the first X509ExtendedKeyManager in testKeyStore.keyManagers
    153             int replaceIndex = -1;
    154             for (int i = 0; i < testKeyStore.keyManagers.length; i++) {
    155                 KeyManager keyManager = testKeyStore.keyManagers[i];
    156                 if (keyManager instanceof X509ExtendedKeyManager) {
    157                     replaceIndex = i;
    158                     break;
    159                 }
    160             }
    161             if (replaceIndex == -1) {
    162                 fail("No X509ExtendedKeyManager in testKeyStore.keyManagers");
    163             }
    164 
    165             // Temporarily substitute the RandomPrivateKeyX509ExtendedKeyManager in place of the
    166             // original X509ExtendedKeyManager.
    167             KeyManager originalKeyManager = testKeyStore.keyManagers[replaceIndex];
    168             testKeyStore.keyManagers[replaceIndex] = randomPrivateKeyX509ExtendedKeyManager;
    169             cWithWrongPrivateKeys = TestSSLContext.create(testKeyStore, testKeyStore);
    170             testKeyStore.keyManagers[replaceIndex] = originalKeyManager;
    171         }
    172 
    173         // To catch all the errors.
    174         StringBuilder error = new StringBuilder();
    175 
    176         String[] cipherSuites = c.clientContext.createSSLEngine().getSupportedCipherSuites();
    177         for (String cipherSuite : cipherSuites) {
    178             try {
    179                 // Skip cipher suites that are obsoleted.
    180                 if (StandardNames.IS_RI && "TLSv1.2".equals(c.clientContext.getProtocol())
    181                         && StandardNames.CIPHER_SUITES_OBSOLETE_TLS12.contains(cipherSuite)) {
    182                     continue;
    183                 }
    184                 /*
    185                  * Signaling Cipher Suite Values (SCSV) cannot be used on their own, but instead in
    186                  * conjunction with other cipher suites.
    187                  */
    188                 if (cipherSuite.equals(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION)
    189                         || cipherSuite.equals(StandardNames.CIPHER_SUITE_FALLBACK)) {
    190                     continue;
    191                 }
    192                 /*
    193                  * Kerberos cipher suites require external setup. See "Kerberos Requirements" in
    194                  * https://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html
    195                  * #KRBRequire
    196                  */
    197                 if (cipherSuite.startsWith("TLS_KRB5_")) {
    198                     continue;
    199                 }
    200 
    201                 final String[] cipherSuiteArray = (secureRenegotiation
    202                                 ? new String[] {cipherSuite,
    203                                           StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION}
    204                                 : new String[] {cipherSuite});
    205 
    206                 // Check that handshake succeeds.
    207                 TestSSLEnginePair pair = null;
    208                 try {
    209                     pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
    210                         @Override
    211                         void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
    212                             client.setEnabledCipherSuites(cipherSuiteArray);
    213                             server.setEnabledCipherSuites(cipherSuiteArray);
    214                         }
    215                     });
    216                     assertConnected(pair);
    217 
    218                     boolean needsRecordSplit = "TLS".equalsIgnoreCase(c.clientContext.getProtocol())
    219                             && cipherSuite.contains("_CBC_");
    220 
    221                     assertSendsCorrectly("This is the client. Hello!".getBytes(UTF_8), pair.client,
    222                             pair.server, needsRecordSplit);
    223                     assertSendsCorrectly("This is the server. Hi!".getBytes(UTF_8), pair.server,
    224                             pair.client, needsRecordSplit);
    225                 } finally {
    226                     if (pair != null) {
    227                         pair.close();
    228                     }
    229                 }
    230 
    231                 // Check that handshake fails when the server does not possess the private key
    232                 // corresponding to the server's certificate. This is achieved by using SSLContext
    233                 // cWithWrongPrivateKeys whose KeyManager returns wrong private keys that match
    234                 // the algorithm (and parameters) of the correct keys.
    235                 boolean serverAuthenticatedUsingPublicKey = true;
    236                 if (cipherSuite.contains("_anon_")) {
    237                     serverAuthenticatedUsingPublicKey = false;
    238                 } else if ((cipherSuite.startsWith("TLS_PSK_"))
    239                         || (cipherSuite.startsWith("TLS_ECDHE_PSK_"))) {
    240                     serverAuthenticatedUsingPublicKey = false;
    241                 }
    242                 if (serverAuthenticatedUsingPublicKey) {
    243                     TestSSLEnginePair p = null;
    244                     try {
    245                         p = TestSSLEnginePair.create(
    246                                 cWithWrongPrivateKeys, new TestSSLEnginePair.Hooks() {
    247                                     @Override
    248                                     void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
    249                                         client.setEnabledCipherSuites(cipherSuiteArray);
    250                                         server.setEnabledCipherSuites(cipherSuiteArray);
    251                                     }
    252                                 });
    253                         assertNotConnected(p);
    254                     } catch (IOException expected) {
    255                         // Ignored.
    256                     } finally {
    257                         if (p != null) {
    258                             p.close();
    259                         }
    260                     }
    261                 }
    262             } catch (Exception e) {
    263                 String message = ("Problem trying to connect cipher suite " + cipherSuite);
    264                 System.out.println(message);
    265                 e.printStackTrace();
    266                 error.append(message);
    267                 error.append('\n');
    268             }
    269         }
    270         c.close();
    271 
    272         if (error.length() > 0) {
    273             throw new Exception("One or more problems in "
    274                     + "test_SSLEngine_getSupportedCipherSuites_connect:\n" + error);
    275         }
    276     }
    277 
    278     private static void assertSendsCorrectly(final byte[] sourceBytes, SSLEngine source,
    279             SSLEngine dest, boolean needsRecordSplit) throws SSLException {
    280         ByteBuffer sourceOut = ByteBuffer.wrap(sourceBytes);
    281         SSLSession sourceSession = source.getSession();
    282         ByteBuffer sourceToDest = ByteBuffer.allocate(sourceSession.getPacketBufferSize());
    283         SSLEngineResult sourceOutRes = source.wrap(sourceOut, sourceToDest);
    284         sourceToDest.flip();
    285 
    286         String sourceCipherSuite = source.getSession().getCipherSuite();
    287         assertEquals(sourceCipherSuite, sourceBytes.length, sourceOutRes.bytesConsumed());
    288         assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING,
    289                 sourceOutRes.getHandshakeStatus());
    290 
    291         SSLSession destSession = dest.getSession();
    292         ByteBuffer destIn = ByteBuffer.allocate(destSession.getApplicationBufferSize());
    293 
    294         int numUnwrapCalls = 0;
    295         while (destIn.position() != sourceOut.limit()) {
    296             SSLEngineResult destRes = dest.unwrap(sourceToDest, destIn);
    297             assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING,
    298                     destRes.getHandshakeStatus());
    299             if (needsRecordSplit && numUnwrapCalls == 0) {
    300                 assertEquals(sourceCipherSuite, 1, destRes.bytesProduced());
    301             }
    302             numUnwrapCalls++;
    303         }
    304 
    305         destIn.flip();
    306         byte[] actual = new byte[destIn.remaining()];
    307         destIn.get(actual);
    308         assertEquals(sourceCipherSuite, Arrays.toString(sourceBytes), Arrays.toString(actual));
    309 
    310         if (needsRecordSplit) {
    311             assertEquals(sourceCipherSuite, 2, numUnwrapCalls);
    312         } else {
    313             assertEquals(sourceCipherSuite, 1, numUnwrapCalls);
    314             assertSendsCorrectlyWhenSplit(sourceBytes, source, dest);
    315         }
    316     }
    317 
    318     private static void assertSendsCorrectlyWhenSplit(final byte[] sourceBytes, SSLEngine source,
    319             SSLEngine dest) throws SSLException {
    320         // Split the input into three to test the version that accepts ByteBuffer[].  Three
    321         // is chosen somewhat arbitrarily as a number larger than the minimum of 2 but small
    322         // enough that it's not unwieldy.
    323         ByteBuffer[] sourceBufs = new ByteBuffer[3];
    324         int sourceLen = sourceBytes.length;
    325         sourceBufs[0] = ByteBuffer.wrap(sourceBytes, 0, sourceLen / 3);
    326         sourceBufs[1] = ByteBuffer.wrap(sourceBytes, sourceLen / 3, sourceLen / 3);
    327         sourceBufs[2] = ByteBuffer.wrap(
    328             sourceBytes, 2 * (sourceLen / 3), sourceLen - 2 * (sourceLen / 3));
    329         SSLSession sourceSession = source.getSession();
    330         ByteBuffer sourceToDest = ByteBuffer.allocate(sourceSession.getPacketBufferSize());
    331         SSLEngineResult sourceOutRes = source.wrap(sourceBufs, sourceToDest);
    332         sourceToDest.flip();
    333         String sourceCipherSuite = source.getSession().getCipherSuite();
    334         assertEquals(sourceCipherSuite, sourceBytes.length, sourceOutRes.bytesConsumed());
    335         assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING,
    336                 sourceOutRes.getHandshakeStatus());
    337         SSLSession destSession = dest.getSession();
    338         ByteBuffer destIn = ByteBuffer.allocate(destSession.getApplicationBufferSize());
    339         int numUnwrapCalls = 0;
    340         while (destIn.position() != sourceBytes.length) {
    341             SSLEngineResult destRes = dest.unwrap(sourceToDest, destIn);
    342             assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING,
    343                     destRes.getHandshakeStatus());
    344             numUnwrapCalls++;
    345         }
    346         destIn.flip();
    347         byte[] actual = new byte[destIn.remaining()];
    348         destIn.get(actual);
    349         assertEquals(sourceCipherSuite, Arrays.toString(sourceBytes), Arrays.toString(actual));
    350         assertEquals(sourceCipherSuite, 3, numUnwrapCalls);
    351     }
    352 
    353     @Test
    354     public void test_SSLEngine_getEnabledCipherSuites_returnsCopies() throws Exception {
    355         TestSSLContext c = TestSSLContext.create();
    356         SSLEngine e = c.clientContext.createSSLEngine();
    357         assertNotSame(e.getEnabledCipherSuites(), e.getEnabledCipherSuites());
    358         c.close();
    359     }
    360 
    361     @Test
    362     public void test_SSLEngine_setEnabledCipherSuites_storesCopy() throws Exception {
    363         TestSSLContext c = TestSSLContext.create();
    364         SSLEngine e = c.clientContext.createSSLEngine();
    365         String[] array = new String[] {e.getEnabledCipherSuites()[0]};
    366         String originalFirstElement = array[0];
    367         e.setEnabledCipherSuites(array);
    368         array[0] = "Modified after having been set";
    369         assertEquals(originalFirstElement, e.getEnabledCipherSuites()[0]);
    370     }
    371 
    372     @Test
    373     public void test_SSLEngine_setEnabledCipherSuites() throws Exception {
    374         TestSSLContext c = TestSSLContext.create();
    375         SSLEngine e = c.clientContext.createSSLEngine();
    376 
    377         try {
    378             e.setEnabledCipherSuites(null);
    379             fail();
    380         } catch (IllegalArgumentException expected) {
    381             // Ignored.
    382         }
    383         try {
    384             e.setEnabledCipherSuites(new String[1]);
    385             fail();
    386         } catch (IllegalArgumentException expected) {
    387             // Ignored.
    388         }
    389         try {
    390             e.setEnabledCipherSuites(new String[] {"Bogus"});
    391             fail();
    392         } catch (IllegalArgumentException expected) {
    393             // Ignored.
    394         }
    395 
    396         e.setEnabledCipherSuites(new String[0]);
    397         e.setEnabledCipherSuites(e.getEnabledCipherSuites());
    398         e.setEnabledCipherSuites(e.getSupportedCipherSuites());
    399 
    400         // Check that setEnabledCipherSuites affects getEnabledCipherSuites
    401         String[] cipherSuites = new String[] {e.getSupportedCipherSuites()[0]};
    402         e.setEnabledCipherSuites(cipherSuites);
    403         assertEquals(Arrays.asList(cipherSuites), Arrays.asList(e.getEnabledCipherSuites()));
    404 
    405         c.close();
    406     }
    407 
    408     @Test
    409     public void test_SSLEngine_getSupportedProtocols_returnsCopies() throws Exception {
    410         TestSSLContext c = TestSSLContext.create();
    411         SSLEngine e = c.clientContext.createSSLEngine();
    412         assertNotSame(e.getSupportedProtocols(), e.getSupportedProtocols());
    413         c.close();
    414     }
    415 
    416     @Test
    417     public void test_SSLEngine_getEnabledProtocols_returnsCopies() throws Exception {
    418         TestSSLContext c = TestSSLContext.create();
    419         SSLEngine e = c.clientContext.createSSLEngine();
    420         assertNotSame(e.getEnabledProtocols(), e.getEnabledProtocols());
    421         c.close();
    422     }
    423 
    424     @Test
    425     public void test_SSLEngine_setEnabledProtocols_storesCopy() throws Exception {
    426         TestSSLContext c = TestSSLContext.create();
    427         SSLEngine e = c.clientContext.createSSLEngine();
    428         String[] array = new String[] {e.getEnabledProtocols()[0]};
    429         String originalFirstElement = array[0];
    430         e.setEnabledProtocols(array);
    431         array[0] = "Modified after having been set";
    432         assertEquals(originalFirstElement, e.getEnabledProtocols()[0]);
    433     }
    434 
    435     @Test
    436     public void test_SSLEngine_setEnabledProtocols() throws Exception {
    437         TestSSLContext c = TestSSLContext.create();
    438         SSLEngine e = c.clientContext.createSSLEngine();
    439 
    440         try {
    441             e.setEnabledProtocols(null);
    442             fail();
    443         } catch (IllegalArgumentException expected) {
    444             // Ignored.
    445         }
    446         try {
    447             e.setEnabledProtocols(new String[1]);
    448             fail();
    449         } catch (IllegalArgumentException expected) {
    450             // Ignored.
    451         }
    452         try {
    453             e.setEnabledProtocols(new String[] {"Bogus"});
    454             fail();
    455         } catch (IllegalArgumentException expected) {
    456             // Ignored.
    457         }
    458         e.setEnabledProtocols(new String[0]);
    459         e.setEnabledProtocols(e.getEnabledProtocols());
    460         e.setEnabledProtocols(e.getSupportedProtocols());
    461 
    462         // Check that setEnabledProtocols affects getEnabledProtocols
    463         for (String protocol : e.getSupportedProtocols()) {
    464             if ("SSLv2Hello".equals(protocol)) {
    465                 try {
    466                     e.setEnabledProtocols(new String[] {protocol});
    467                     fail("Should fail when SSLv2Hello is set by itself");
    468                 } catch (IllegalArgumentException expected) {
    469                     // Ignored.
    470                 }
    471             } else {
    472                 String[] protocols = new String[] {protocol};
    473                 e.setEnabledProtocols(protocols);
    474                 assertEquals(Arrays.deepToString(protocols),
    475                         Arrays.deepToString(e.getEnabledProtocols()));
    476             }
    477         }
    478 
    479         c.close();
    480     }
    481 
    482     @Test
    483     public void test_SSLEngine_getSession() throws Exception {
    484         TestSSLContext c = TestSSLContext.create();
    485         SSLEngine e = c.clientContext.createSSLEngine();
    486         SSLSession session = e.getSession();
    487         assertNotNull(session);
    488         assertFalse(session.isValid());
    489         c.close();
    490     }
    491 
    492     @Test
    493     public void test_SSLEngine_beginHandshake() throws Exception {
    494         TestSSLContext c = TestSSLContext.create();
    495 
    496         try {
    497             c.clientContext.createSSLEngine().beginHandshake();
    498             fail();
    499         } catch (IllegalStateException expected) {
    500             // Ignored.
    501         }
    502         c.close();
    503 
    504         TestSSLEnginePair p = TestSSLEnginePair.create(null);
    505         assertConnected(p);
    506         p.close();
    507     }
    508 
    509     @Test
    510     public void test_SSLEngine_beginHandshake_noKeyStore() throws Exception {
    511         TestSSLContext c = TestSSLContext.newBuilder()
    512                                    .useDefaults(false)
    513                                    .clientContext(SSLContext.getDefault())
    514                                    .serverContext(SSLContext.getDefault())
    515                                    .build();
    516         SSLEngine[] p = null;
    517         try {
    518             // TODO Fix KnownFailure AlertException "NO SERVER CERTIFICATE FOUND"
    519             // ServerHandshakeImpl.selectSuite should not select a suite without a required cert
    520             p = TestSSLEnginePair.connect(c, null);
    521             fail();
    522         } catch (SSLHandshakeException expected) {
    523             // Ignored.
    524         } finally {
    525             if (p != null) {
    526                 TestSSLEnginePair.close(p);
    527             }
    528         }
    529         c.close();
    530     }
    531 
    532     @Test
    533     public void test_SSLEngine_beginHandshake_noClientCertificate() throws Exception {
    534         TestSSLContext c = TestSSLContext.create();
    535         SSLEngine[] engines = TestSSLEnginePair.connect(c, null);
    536         assertConnected(engines[0], engines[1]);
    537         c.close();
    538         TestSSLEnginePair.close(engines);
    539     }
    540 
    541     // http://b/37078438
    542     @Test
    543     public void test_SSLEngine_beginHandshake_redundantCalls() throws Exception {
    544         TestSSLContext c = TestSSLContext.create();
    545         SSLEngine client = c.clientContext.createSSLEngine(c.host.getHostName(), c.port);
    546         client.setUseClientMode(true);
    547         client.beginHandshake();
    548         client.beginHandshake(); // This call should be ignored
    549         c.close();
    550     }
    551 
    552     @Test
    553     public void test_SSLEngine_getUseClientMode() throws Exception {
    554         TestSSLContext c = TestSSLContext.create();
    555         assertFalse(c.clientContext.createSSLEngine().getUseClientMode());
    556         assertFalse(c.clientContext.createSSLEngine(null, -1).getUseClientMode());
    557         c.close();
    558     }
    559 
    560     @Test
    561     public void test_SSLEngine_setUseClientMode() throws Exception {
    562         boolean[] finished;
    563         TestSSLEnginePair p;
    564 
    565         // client is client, server is server
    566         finished = new boolean[2];
    567         p = test_SSLEngine_setUseClientMode(true, false, finished);
    568         assertConnected(p);
    569         assertTrue(finished[0]);
    570         assertTrue(finished[1]);
    571         p.close();
    572 
    573         // client is server, server is client
    574         finished = new boolean[2];
    575         p = test_SSLEngine_setUseClientMode(false, true, finished);
    576         assertConnected(p);
    577         assertTrue(finished[0]);
    578         assertTrue(finished[1]);
    579         p.close();
    580 
    581         // both are client
    582         /*
    583          * Our implementation throws an SSLHandshakeException, but RI just
    584          * stalls forever
    585          */
    586         p = null;
    587         try {
    588             p = test_SSLEngine_setUseClientMode(true, true, null);
    589             assertNotConnected(p);
    590         } catch (SSLHandshakeException maybeExpected) {
    591             // Ignored.
    592         } finally {
    593             if (p != null) {
    594                 p.close();
    595             }
    596         }
    597 
    598         p = test_SSLEngine_setUseClientMode(false, false, null);
    599         // both are server
    600         assertNotConnected(p);
    601         p.close();
    602     }
    603 
    604     @Test
    605     public void test_SSLEngine_setUseClientMode_afterHandshake() throws Exception {
    606         // can't set after handshake
    607         TestSSLEnginePair pair = TestSSLEnginePair.create(null);
    608         try {
    609             pair.server.setUseClientMode(false);
    610             fail();
    611         } catch (IllegalArgumentException expected) {
    612             // Ignored.
    613         }
    614         try {
    615             pair.client.setUseClientMode(false);
    616             fail();
    617         } catch (IllegalArgumentException expected) {
    618             // Ignored.
    619         }
    620         pair.close();
    621     }
    622 
    623     private TestSSLEnginePair test_SSLEngine_setUseClientMode(final boolean clientClientMode,
    624             final boolean serverClientMode, final boolean[] finished) throws Exception {
    625         TestSSLContext c;
    626         if (!clientClientMode && serverClientMode) {
    627             c = TestSSLContext.create(TestKeyStore.getServer(), TestKeyStore.getClient());
    628         } else {
    629             c = TestSSLContext.create();
    630         }
    631 
    632         return TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
    633             @Override
    634             void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
    635                 client.setUseClientMode(clientClientMode);
    636                 server.setUseClientMode(serverClientMode);
    637             }
    638         }, finished);
    639     }
    640 
    641     @Test
    642     public void test_SSLEngine_clientAuth() throws Exception {
    643         TestSSLContext c = TestSSLContext.create();
    644         SSLEngine e = c.clientContext.createSSLEngine();
    645 
    646         assertFalse(e.getWantClientAuth());
    647         assertFalse(e.getNeedClientAuth());
    648 
    649         // confirm turning one on by itself
    650         e.setWantClientAuth(true);
    651         assertTrue(e.getWantClientAuth());
    652         assertFalse(e.getNeedClientAuth());
    653 
    654         // confirm turning setting on toggles the other
    655         e.setNeedClientAuth(true);
    656         assertFalse(e.getWantClientAuth());
    657         assertTrue(e.getNeedClientAuth());
    658 
    659         // confirm toggling back
    660         e.setWantClientAuth(true);
    661         assertTrue(e.getWantClientAuth());
    662         assertFalse(e.getNeedClientAuth());
    663 
    664         // TODO Fix KnownFailure "init - invalid private key"
    665         TestSSLContext clientAuthContext = TestSSLContext.create(
    666                 TestKeyStore.getClientCertificate(), TestKeyStore.getServer());
    667         TestSSLEnginePair p =
    668                 TestSSLEnginePair.create(clientAuthContext, new TestSSLEnginePair.Hooks() {
    669                     @Override
    670                     void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
    671                         server.setWantClientAuth(true);
    672                     }
    673                 });
    674         assertConnected(p);
    675         assertNotNull(p.client.getSession().getLocalCertificates());
    676         TestKeyStore.assertChainLength(p.client.getSession().getLocalCertificates());
    677         TestSSLContext.assertClientCertificateChain(
    678                 clientAuthContext.clientTrustManager, p.client.getSession().getLocalCertificates());
    679         clientAuthContext.close();
    680         c.close();
    681         p.close();
    682     }
    683 
    684     /**
    685      * http://code.google.com/p/android/issues/detail?id=31903
    686      * This test case directly tests the fix for the issue.
    687      */
    688     @Test
    689     public void test_SSLEngine_clientAuthWantedNoClientCert() throws Exception {
    690         TestSSLContext clientAuthContext =
    691                 TestSSLContext.create(TestKeyStore.getClient(), TestKeyStore.getServer());
    692         TestSSLEnginePair p =
    693                 TestSSLEnginePair.create(clientAuthContext, new TestSSLEnginePair.Hooks() {
    694                     @Override
    695                     void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
    696                         server.setWantClientAuth(true);
    697                     }
    698                 });
    699         assertConnected(p);
    700         clientAuthContext.close();
    701         p.close();
    702     }
    703 
    704     /**
    705      * http://code.google.com/p/android/issues/detail?id=31903
    706      * This test case verifies that if the server requires a client cert
    707      * (setNeedClientAuth) but the client does not provide one SSL connection
    708      * establishment will fail
    709      */
    710     @Test
    711     public void test_SSLEngine_clientAuthNeededNoClientCert() throws Exception {
    712         TestSSLContext clientAuthContext =
    713                 TestSSLContext.create(TestKeyStore.getClient(), TestKeyStore.getServer());
    714         TestSSLEnginePair p = null;
    715         try {
    716             p = TestSSLEnginePair.create(clientAuthContext, new TestSSLEnginePair.Hooks() {
    717                 @Override
    718                 void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
    719                     server.setNeedClientAuth(true);
    720                 }
    721             });
    722             fail();
    723         } catch (SSLHandshakeException expected) {
    724             // Ignored.
    725         } finally {
    726             clientAuthContext.close();
    727             if (p != null) {
    728                 p.close();
    729             }
    730         }
    731     }
    732 
    733     @Test
    734     public void test_SSLEngine_endpointVerification_Success() throws Exception {
    735         TestUtils.assumeSetEndpointIdentificationAlgorithmAvailable();
    736         TestSSLContext c = TestSSLContext.create();
    737         TestSSLEnginePair p = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
    738             @Override
    739             void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
    740                 SSLParameters p = client.getSSLParameters();
    741                 p.setEndpointIdentificationAlgorithm("HTTPS");
    742                 client.setSSLParameters(p);
    743             }
    744         });
    745         assertConnected(p);
    746         c.close();
    747     }
    748 
    749     @Test
    750     public void test_SSLEngine_getEnableSessionCreation() throws Exception {
    751         TestSSLContext c = TestSSLContext.create();
    752         SSLEngine e = c.clientContext.createSSLEngine();
    753         assertTrue(e.getEnableSessionCreation());
    754         c.close();
    755         TestSSLEnginePair.close(new SSLEngine[] {e});
    756     }
    757 
    758     @Test
    759     public void test_SSLEngine_setEnableSessionCreation_server() throws Exception {
    760         TestSSLEnginePair p = null;
    761         try {
    762             p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
    763                 @Override
    764                 void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
    765                     server.setEnableSessionCreation(false);
    766                 }
    767             });
    768             assertNotConnected(p);
    769         } catch (SSLException maybeExpected) {
    770             // Ignored.
    771         } finally {
    772             if (p != null) {
    773                 p.close();
    774             }
    775         }
    776     }
    777 
    778     @Test
    779     public void test_SSLEngine_setEnableSessionCreation_client() throws Exception {
    780         TestSSLEnginePair p = null;
    781         try {
    782             p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
    783                 @Override
    784                 void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
    785                     client.setEnableSessionCreation(false);
    786                 }
    787             });
    788             fail();
    789         } catch (SSLException expected) {
    790             // Ignored.
    791         } finally {
    792             if (p != null) {
    793                 p.close();
    794             }
    795         }
    796     }
    797 
    798     @Test
    799     public void test_SSLEngine_getSSLParameters() throws Exception {
    800         TestSSLContext c = TestSSLContext.create();
    801         SSLEngine e = c.clientContext.createSSLEngine();
    802 
    803         SSLParameters p = e.getSSLParameters();
    804         assertNotNull(p);
    805 
    806         String[] cipherSuites = p.getCipherSuites();
    807         assertNotSame(cipherSuites, e.getEnabledCipherSuites());
    808         assertEquals(Arrays.asList(cipherSuites), Arrays.asList(e.getEnabledCipherSuites()));
    809 
    810         String[] protocols = p.getProtocols();
    811         assertNotSame(protocols, e.getEnabledProtocols());
    812         assertEquals(Arrays.asList(protocols), Arrays.asList(e.getEnabledProtocols()));
    813 
    814         assertEquals(p.getWantClientAuth(), e.getWantClientAuth());
    815         assertEquals(p.getNeedClientAuth(), e.getNeedClientAuth());
    816 
    817         c.close();
    818     }
    819 
    820     @Test
    821     public void test_SSLEngine_setSSLParameters() throws Exception {
    822         TestSSLContext c = TestSSLContext.create();
    823         SSLEngine e = c.clientContext.createSSLEngine();
    824         String[] defaultCipherSuites = e.getEnabledCipherSuites();
    825         String[] defaultProtocols = e.getEnabledProtocols();
    826         String[] supportedCipherSuites = e.getSupportedCipherSuites();
    827         String[] supportedProtocols = e.getSupportedProtocols();
    828 
    829         {
    830             SSLParameters p = new SSLParameters();
    831             e.setSSLParameters(p);
    832             assertEquals(
    833                     Arrays.asList(defaultCipherSuites), Arrays.asList(e.getEnabledCipherSuites()));
    834             assertEquals(Arrays.asList(defaultProtocols), Arrays.asList(e.getEnabledProtocols()));
    835         }
    836 
    837         {
    838             SSLParameters p = new SSLParameters(supportedCipherSuites, supportedProtocols);
    839             e.setSSLParameters(p);
    840             assertEquals(Arrays.asList(supportedCipherSuites),
    841                     Arrays.asList(e.getEnabledCipherSuites()));
    842             assertEquals(Arrays.asList(supportedProtocols), Arrays.asList(e.getEnabledProtocols()));
    843         }
    844         {
    845             SSLParameters p = new SSLParameters();
    846 
    847             p.setNeedClientAuth(true);
    848             assertFalse(e.getNeedClientAuth());
    849             assertFalse(e.getWantClientAuth());
    850             e.setSSLParameters(p);
    851             assertTrue(e.getNeedClientAuth());
    852             assertFalse(e.getWantClientAuth());
    853 
    854             p.setWantClientAuth(true);
    855             assertTrue(e.getNeedClientAuth());
    856             assertFalse(e.getWantClientAuth());
    857             e.setSSLParameters(p);
    858             assertFalse(e.getNeedClientAuth());
    859             assertTrue(e.getWantClientAuth());
    860 
    861             p.setWantClientAuth(false);
    862             assertFalse(e.getNeedClientAuth());
    863             assertTrue(e.getWantClientAuth());
    864             e.setSSLParameters(p);
    865             assertFalse(e.getNeedClientAuth());
    866             assertFalse(e.getWantClientAuth());
    867         }
    868         c.close();
    869     }
    870 
    871     @Test
    872     public void test_TestSSLEnginePair_create() throws Exception {
    873         TestSSLEnginePair test = TestSSLEnginePair.create(null);
    874         assertNotNull(test.c);
    875         assertNotNull(test.server);
    876         assertNotNull(test.client);
    877         assertConnected(test);
    878         test.close();
    879     }
    880 
    881     private final int NUM_STRESS_ITERATIONS = 1000;
    882 
    883     @Test
    884     public void test_SSLEngine_Multiple_Thread_Success() throws Exception {
    885         final TestSSLEnginePair pair = TestSSLEnginePair.create();
    886         try {
    887             assertConnected(pair);
    888 
    889             final CountDownLatch startUpSync = new CountDownLatch(2);
    890             ExecutorService executor = Executors.newFixedThreadPool(2);
    891             Future<Void> client = executor.submit(new Callable<Void>() {
    892                 @Override
    893                 public Void call() throws Exception {
    894                     startUpSync.countDown();
    895 
    896                     for (int i = 0; i < NUM_STRESS_ITERATIONS; i++) {
    897                         assertSendsCorrectly("This is the client. Hello!".getBytes(UTF_8),
    898                                 pair.client, pair.server, false);
    899                     }
    900 
    901                     return null;
    902                 }
    903             });
    904             Future<Void> server = executor.submit(new Callable<Void>() {
    905                 @Override
    906                 public Void call() throws Exception {
    907                     startUpSync.countDown();
    908 
    909                     for (int i = 0; i < NUM_STRESS_ITERATIONS; i++) {
    910                         assertSendsCorrectly("This is the server. Hi!".getBytes(UTF_8), pair.server,
    911                                 pair.client, false);
    912                     }
    913 
    914                     return null;
    915                 }
    916             });
    917             executor.shutdown();
    918             client.get();
    919             server.get();
    920         } finally {
    921             pair.close();
    922         }
    923     }
    924 
    925     @Test
    926     public void test_SSLEngine_CloseOutbound() throws Exception {
    927         final TestSSLEnginePair pair = TestSSLEnginePair.create();
    928         try {
    929             assertConnected(pair);
    930 
    931             // Closing the outbound direction should cause a close_notify to be sent
    932             pair.client.closeOutbound();
    933             ByteBuffer clientOut = ByteBuffer
    934                     .allocate(pair.client.getSession().getPacketBufferSize());
    935             SSLEngineResult res = pair.client.wrap(ByteBuffer.wrap(new byte[0]), clientOut);
    936             assertEquals(Status.CLOSED, res.getStatus());
    937             assertEquals(HandshakeStatus.NOT_HANDSHAKING, res.getHandshakeStatus());
    938             assertTrue(res.bytesProduced() > 0);
    939 
    940             // Read the close_notify in the server
    941             clientOut.flip();
    942             ByteBuffer serverIn = ByteBuffer
    943                     .allocate(pair.server.getSession().getApplicationBufferSize());
    944             res = pair.server.unwrap(clientOut, serverIn);
    945             assertEquals(Status.CLOSED, res.getStatus());
    946             assertEquals(HandshakeStatus.NEED_WRAP, res.getHandshakeStatus());
    947 
    948             // Reading the close_notify should cause a close_notify to be sent back
    949             ByteBuffer serverOut = ByteBuffer
    950                     .allocate(pair.server.getSession().getPacketBufferSize());
    951             res = pair.server.wrap(ByteBuffer.wrap(new byte[0]), serverOut);
    952             assertEquals(Status.CLOSED, res.getStatus());
    953             assertEquals(HandshakeStatus.NOT_HANDSHAKING, res.getHandshakeStatus());
    954             assertTrue(res.bytesProduced() > 0);
    955 
    956             // Read the close_notify in the client
    957             serverOut.flip();
    958             ByteBuffer clientIn = ByteBuffer
    959                     .allocate(pair.client.getSession().getApplicationBufferSize());
    960             res = pair.client.unwrap(serverOut, clientIn);
    961             assertEquals(Status.CLOSED, res.getStatus());
    962             assertEquals(HandshakeStatus.NOT_HANDSHAKING, res.getHandshakeStatus());
    963 
    964             // Both sides have received close_notify messages, so both peers should have
    965             // registered that they're finished
    966             assertTrue(pair.client.isInboundDone() && pair.client.isOutboundDone());
    967             assertTrue(pair.server.isInboundDone() && pair.server.isOutboundDone());
    968         } finally {
    969             pair.close();
    970         }
    971     }
    972 
    973     @Test
    974     public void test_SSLEngine_TlsUnique() throws Exception {
    975         TestSSLEnginePair pair = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
    976             @Override
    977             void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
    978                 assertNull(Conscrypt.getTlsUnique(client));
    979                 assertNull(Conscrypt.getTlsUnique(server));
    980             }
    981         });
    982         try {
    983             assertConnected(pair);
    984 
    985             byte[] clientTlsUnique = Conscrypt.getTlsUnique(pair.client);
    986             byte[] serverTlsUnique = Conscrypt.getTlsUnique(pair.server);
    987             assertNotNull(clientTlsUnique);
    988             assertNotNull(serverTlsUnique);
    989             assertArrayEquals(clientTlsUnique, serverTlsUnique);
    990         } finally {
    991             pair.close();
    992         }
    993     }
    994 
    995     private void assertConnected(TestSSLEnginePair e) {
    996         assertConnected(e.client, e.server);
    997     }
    998 
    999     private void assertNotConnected(TestSSLEnginePair e) {
   1000         assertNotConnected(e.client, e.server);
   1001     }
   1002 
   1003     private void assertConnected(SSLEngine a, SSLEngine b) {
   1004         assertTrue(connected(a, b));
   1005     }
   1006 
   1007     private void assertNotConnected(SSLEngine a, SSLEngine b) {
   1008         assertFalse(connected(a, b));
   1009     }
   1010 
   1011     private boolean connected(SSLEngine a, SSLEngine b) {
   1012         return (a.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING
   1013                 && b.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING
   1014                 && a.getSession() != null && b.getSession() != null && !a.isInboundDone()
   1015                 && !b.isInboundDone() && !a.isOutboundDone() && !b.isOutboundDone());
   1016     }
   1017 }
   1018