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