/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.xnet.provider.jsse; import java.io.FileDescriptor; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLException; import javax.net.ssl.SSLProtocolException; import javax.security.auth.x500.X500Principal; import junit.framework.TestCase; import libcore.java.security.StandardNames; import libcore.java.security.TestKeyStore; import org.apache.harmony.xnet.provider.jsse.CipherSuite; import org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSLHandshakeCallbacks; public class NativeCryptoTest extends TestCase { private static final int NULL = 0; private static final FileDescriptor INVALID_FD = new FileDescriptor(); private static final SSLHandshakeCallbacks DUMMY_CB = new TestSSLHandshakeCallbacks(-1, null); private static final long TIMEOUT_SECONDS = 5; private static byte[] SERVER_PRIVATE_KEY; private static byte[][] SERVER_CERTIFICATES; private static byte[] CLIENT_PRIVATE_KEY; private static byte[][] CLIENT_CERTIFICATES; private static byte[][] CA_PRINCIPALS; private static byte[] getServerPrivateKey() { initCerts(); return SERVER_PRIVATE_KEY; } private static byte[][] getServerCertificates() { initCerts(); return SERVER_CERTIFICATES; } private static byte[] getClientPrivateKey() { initCerts(); return CLIENT_PRIVATE_KEY; } private static byte[][] getClientCertificates() { initCerts(); return CLIENT_CERTIFICATES; } private static byte[][] getCaPrincipals() { initCerts(); return CA_PRINCIPALS; } /** * Lazily create shared test certificates. */ private static synchronized void initCerts() { if (SERVER_PRIVATE_KEY != null) { return; } try { PrivateKeyEntry serverPrivateKeyEntry = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); SERVER_PRIVATE_KEY = serverPrivateKeyEntry.getPrivateKey().getEncoded(); SERVER_CERTIFICATES = NativeCrypto.encodeCertificates( serverPrivateKeyEntry.getCertificateChain()); PrivateKeyEntry clientPrivateKeyEntry = TestKeyStore.getClientCertificate().getPrivateKey("RSA", "RSA"); CLIENT_PRIVATE_KEY = clientPrivateKeyEntry.getPrivateKey().getEncoded(); CLIENT_CERTIFICATES = NativeCrypto.encodeCertificates( clientPrivateKeyEntry.getCertificateChain()); KeyStore ks = TestKeyStore.getClient().keyStore; String caCertAlias = ks.aliases().nextElement(); X509Certificate certificate = (X509Certificate) ks.getCertificate(caCertAlias); X500Principal principal = certificate.getIssuerX500Principal(); CA_PRINCIPALS = new byte[][] { principal.getEncoded() }; } catch (Exception e) { throw new RuntimeException(e); } } public static void assertEqualSessions(int expected, int actual) { assertEqualByteArrays(NativeCrypto.SSL_SESSION_session_id(expected), NativeCrypto.SSL_SESSION_session_id(actual)); } public static void assertEqualByteArrays(byte[] expected, byte[] actual) { assertEquals(Arrays.toString(expected), Arrays.toString(actual)); } public static void assertEqualPrincipals(byte[][] expected, byte[][] actual) { assertEqualByteArrays(expected, actual); } public static void assertEqualCertificateChains(byte[][] expected, byte[][] actual) { assertEqualByteArrays(expected, actual); } public static void assertEqualByteArrays(byte[][] expected, byte[][] actual) { assertEquals(Arrays.deepToString(expected), Arrays.deepToString(actual)); } public void test_SSL_CTX_new() throws Exception { int c = NativeCrypto.SSL_CTX_new(); assertTrue(c != NULL); int c2 = NativeCrypto.SSL_CTX_new(); assertTrue(c != c2); NativeCrypto.SSL_CTX_free(c); NativeCrypto.SSL_CTX_free(c2); } public void test_SSL_CTX_free() throws Exception { try { NativeCrypto.SSL_CTX_free(NULL); fail(); } catch (NullPointerException expected) { } NativeCrypto.SSL_CTX_free(NativeCrypto.SSL_CTX_new()); } public void test_SSL_new() throws Exception { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); assertTrue(s != NULL); assertTrue((NativeCrypto.SSL_get_options(s) & 0x01000000L) != 0); // SSL_OP_NO_SSLv2 assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_SSLv3) == 0); assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_TLSv1) == 0); int s2 = NativeCrypto.SSL_new(c); assertTrue(s != s2); NativeCrypto.SSL_free(s2); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_use_certificate() throws Exception { try { NativeCrypto.SSL_use_certificate(NULL, null); fail(); } catch (NullPointerException expected) { } int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_use_certificate(s, null); fail(); } catch (NullPointerException expected) { } NativeCrypto.SSL_use_certificate(s, getServerCertificates()); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_use_PrivateKey() throws Exception { try { NativeCrypto.SSL_use_PrivateKey(NULL, null); fail(); } catch (NullPointerException expected) { } int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_use_PrivateKey(s, null); fail(); } catch (NullPointerException expected) { } NativeCrypto.SSL_use_PrivateKey(s, getServerPrivateKey()); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_check_private_key_null() throws Exception { try { NativeCrypto.SSL_check_private_key(NULL); fail(); } catch (NullPointerException expected) { } } public void test_SSL_check_private_key_no_key_no_cert() throws Exception { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); // neither private or certificate set try { NativeCrypto.SSL_check_private_key(s); fail(); } catch (SSLException expected) { } NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_check_private_key_cert_then_key() throws Exception { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); // first certificate, then private NativeCrypto.SSL_use_certificate(s, getServerCertificates()); try { NativeCrypto.SSL_check_private_key(s); fail(); } catch (SSLException expected) { } NativeCrypto.SSL_use_PrivateKey(s, getServerPrivateKey()); NativeCrypto.SSL_check_private_key(s); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_check_private_key_key_then_cert() throws Exception { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); // first private, then certificate NativeCrypto.SSL_use_PrivateKey(s, getServerPrivateKey()); try { NativeCrypto.SSL_check_private_key(s); fail(); } catch (SSLException expected) { } NativeCrypto.SSL_use_certificate(s, getServerCertificates()); NativeCrypto.SSL_check_private_key(s); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_get_mode() throws Exception { try { NativeCrypto.SSL_get_mode(NULL); fail(); } catch (NullPointerException expected) { } int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); assertTrue(NativeCrypto.SSL_get_mode(s) != 0); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_set_mode() throws Exception { try { NativeCrypto.SSL_set_mode(NULL, 0); fail(); } catch (NullPointerException expected) { } int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); // check SSL_MODE_HANDSHAKE_CUTTHROUGH on assertTrue((NativeCrypto.SSL_get_mode(s) & NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH) != 0); // clear SSL_MODE_HANDSHAKE_CUTTHROUGH off NativeCrypto.SSL_clear_mode(s, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH); assertTrue((NativeCrypto.SSL_get_mode(s) & NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH) == 0); // set SSL_MODE_HANDSHAKE_CUTTHROUGH on NativeCrypto.SSL_set_mode(s, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH); assertTrue((NativeCrypto.SSL_get_mode(s) & NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH) != 0); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_clear_mode() throws Exception { try { NativeCrypto.SSL_clear_mode(NULL, 0); fail(); } catch (NullPointerException expected) { } int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); // check SSL_MODE_HANDSHAKE_CUTTHROUGH on assertTrue((NativeCrypto.SSL_get_mode(s) & NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH) != 0); // clear SSL_MODE_HANDSHAKE_CUTTHROUGH off NativeCrypto.SSL_clear_mode(s, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH); assertTrue((NativeCrypto.SSL_get_mode(s) & NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH) == 0); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_get_options() throws Exception { try { NativeCrypto.SSL_get_options(NULL); fail(); } catch (NullPointerException expected) { } int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); assertTrue(NativeCrypto.SSL_get_options(s) != 0); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_set_options() throws Exception { try { NativeCrypto.SSL_set_options(NULL, 0); fail(); } catch (NullPointerException expected) { } int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_SSLv3) == 0); NativeCrypto.SSL_set_options(s, NativeCrypto.SSL_OP_NO_SSLv3); assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_SSLv3) != 0); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_clear_options() throws Exception { try { NativeCrypto.SSL_clear_options(NULL, 0); fail(); } catch (NullPointerException expected) { } int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_SSLv3) == 0); NativeCrypto.SSL_set_options(s, NativeCrypto.SSL_OP_NO_SSLv3); assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_SSLv3) != 0); NativeCrypto.SSL_clear_options(s, NativeCrypto.SSL_OP_NO_SSLv3); assertTrue((NativeCrypto.SSL_get_options(s) & NativeCrypto.SSL_OP_NO_SSLv3) == 0); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_set_cipher_lists() throws Exception { try { NativeCrypto.SSL_set_cipher_lists(NULL, null); fail(); } catch (NullPointerException expected) { } int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_set_cipher_lists(s, null); fail(); } catch (NullPointerException expected) { } NativeCrypto.SSL_set_cipher_lists(s, new String[] {}); try { NativeCrypto.SSL_set_cipher_lists(s, new String[] { null }); fail(); } catch (NullPointerException expected) { } // see OpenSSL ciphers man page String[] illegals = new String[] { // empty "", // never standardized "EXP1024-DES-CBC-SHA", "EXP1024-RC4-SHA", "DHE-DSS-RC4-SHA", // IDEA "IDEA-CBC-SHA", "IDEA-CBC-MD5" }; for (String illegal : illegals) { try { NativeCrypto.SSL_set_cipher_lists(s, new String[] { illegal }); fail(illegal); } catch (IllegalArgumentException expected) { } } List<String> ciphers = new ArrayList<String>(NativeCrypto.OPENSSL_TO_STANDARD_CIPHER_SUITES.keySet()); NativeCrypto.SSL_set_cipher_lists(s, ciphers.toArray(new String[ciphers.size()])); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_set_verify() throws Exception { try { NativeCrypto.SSL_set_verify(NULL, 0); fail(); } catch (NullPointerException expected) { } int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_NONE); NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_PEER); NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT); NativeCrypto.SSL_set_verify(s, (NativeCrypto.SSL_VERIFY_PEER | NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT)); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } private static final boolean DEBUG = false; public static class Hooks { public int getContext() throws SSLException { return NativeCrypto.SSL_CTX_new(); } public int beforeHandshake(int context) throws SSLException { int s = NativeCrypto.SSL_new(context); // without this SSL_set_cipher_lists call the tests were // negotiating DHE-RSA-AES256-SHA by default which had // very slow ephemeral RSA key generation NativeCrypto.SSL_set_cipher_lists(s, new String[] { "RC4-MD5" }); return s; } public void clientCertificateRequested(int s) {} public void afterHandshake(int session, int ssl, int context, Socket socket, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { if (session != NULL) { NativeCrypto.SSL_SESSION_free(session); } if (ssl != NULL) { try { NativeCrypto.SSL_shutdown(ssl, fd, callback); } catch (IOException e) { } NativeCrypto.SSL_free(ssl); } if (context != NULL) { NativeCrypto.SSL_CTX_free(context); } if (socket != null) { socket.close(); } } } public static class TestSSLHandshakeCallbacks implements SSLHandshakeCallbacks { private final int sslNativePointer; private final Hooks hooks; public TestSSLHandshakeCallbacks(int sslNativePointer, Hooks hooks) { this.sslNativePointer = sslNativePointer; this.hooks = hooks; } public byte[][] asn1DerEncodedCertificateChain; public String authMethod; public boolean verifyCertificateChainCalled; public void verifyCertificateChain(byte[][] asn1DerEncodedCertificateChain, String authMethod) throws CertificateException { if (DEBUG) { System.out.println("ssl=0x" + Integer.toString(sslNativePointer, 16) + " verifyCertificateChain" + " asn1DerEncodedCertificateChain=" + asn1DerEncodedCertificateChain + " authMethod=" + authMethod); } this.asn1DerEncodedCertificateChain = asn1DerEncodedCertificateChain; this.authMethod = authMethod; this.verifyCertificateChainCalled = true; return; } public byte[] keyTypes; public byte[][] asn1DerEncodedX500Principals; public boolean clientCertificateRequestedCalled; public void clientCertificateRequested(byte[] keyTypes, byte[][] asn1DerEncodedX500Principals) { if (DEBUG) { System.out.println("ssl=0x" + Integer.toString(sslNativePointer, 16) + " clientCertificateRequested" + " keyTypes=" + keyTypes + " asn1DerEncodedX500Principals=" + asn1DerEncodedX500Principals); } this.keyTypes = keyTypes; this.asn1DerEncodedX500Principals = asn1DerEncodedX500Principals; this.clientCertificateRequestedCalled = true; if (hooks != null ) { hooks.clientCertificateRequested(sslNativePointer); } } public boolean handshakeCompletedCalled; public void handshakeCompleted() { if (DEBUG) { System.out.println("ssl=0x" + Integer.toString(sslNativePointer, 16) + " handshakeCompleted"); } this.handshakeCompletedCalled = true; return; } } public static class ServerHooks extends Hooks { private final byte[] privateKey; private final byte[][] certificates; public ServerHooks(byte[] privateKey, byte[][] certificates) { this.privateKey = privateKey; this.certificates = certificates; } @Override public int beforeHandshake(int c) throws SSLException { int s = super.beforeHandshake(c); if (privateKey != null) { NativeCrypto.SSL_use_PrivateKey(s, privateKey); } if (certificates != null) { NativeCrypto.SSL_use_certificate(s, certificates); } return s; } public void clientCertificateRequested(int s) { fail("Server asked for client certificates"); } } public static Future<TestSSLHandshakeCallbacks> handshake(final ServerSocket listener, final int timeout, final boolean client, final Hooks hooks) { ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new Callable<TestSSLHandshakeCallbacks>() { public TestSSLHandshakeCallbacks call() throws Exception { Socket socket = (client ? new Socket(listener.getInetAddress(), listener.getLocalPort()) : listener.accept()); if (timeout == -1) { return null; } FileDescriptor fd = socket.getFileDescriptor$(); int c = hooks.getContext(); int s = hooks.beforeHandshake(c); TestSSLHandshakeCallbacks callback = new TestSSLHandshakeCallbacks(s, hooks); if (DEBUG) { System.out.println("ssl=0x" + Integer.toString(s, 16) + " handshake" + " context=0x" + Integer.toString(c, 16) + " socket=" + socket + " fd=" + fd + " timeout=" + timeout + " client=" + client); } int session = NativeCrypto.SSL_do_handshake(s, fd, callback, timeout, client); if (DEBUG) { System.out.println("ssl=0x" + Integer.toString(s, 16) + " handshake" + " session=0x" + Integer.toString(session, 16)); } hooks.afterHandshake(session, s, c, socket, fd, callback); return callback; } }); executor.shutdown(); return future; } public void test_SSL_do_handshake_NULL_SSL() throws Exception { try { NativeCrypto.SSL_do_handshake(NULL, null, null, 0, false); fail(); } catch (NullPointerException expected) { } } public void test_SSL_do_handshake_null_args() throws Exception { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_do_handshake(s, null, null, 0, true); fail(); } catch (NullPointerException e) { } try { NativeCrypto.SSL_do_handshake(s, INVALID_FD, null, 0, true); fail(); } catch (NullPointerException e) { } NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } public void test_SSL_do_handshake_normal() throws Exception { // normal client and server case final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public int beforeHandshake(int context) throws SSLException { int s = super.beforeHandshake(context); NativeCrypto.SSL_clear_mode(s, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH); return s; } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue(clientCallback.verifyCertificateChainCalled); assertEqualCertificateChains(getServerCertificates(), clientCallback.asn1DerEncodedCertificateChain); assertEquals("RSA", clientCallback.authMethod); assertFalse(serverCallback.verifyCertificateChainCalled); assertFalse(clientCallback.clientCertificateRequestedCalled); assertFalse(serverCallback.clientCertificateRequestedCalled); assertTrue(clientCallback.handshakeCompletedCalled); assertTrue(serverCallback.handshakeCompletedCalled); } public void test_SSL_do_handshake_optional_client_certificate() throws Exception { // optional client certificate case final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public int beforeHandshake(int context) throws SSLException { int s = super.beforeHandshake(context); NativeCrypto.SSL_clear_mode(s, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH); return s; } @Override public void clientCertificateRequested(int s) { super.clientCertificateRequested(s); NativeCrypto.SSL_use_PrivateKey(s, getClientPrivateKey()); NativeCrypto.SSL_use_certificate(s, getClientCertificates()); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public int beforeHandshake(int c) throws SSLException { int s = super.beforeHandshake(c); NativeCrypto.SSL_set_client_CA_list(s, getCaPrincipals()); NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_PEER); return s; } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue(clientCallback.verifyCertificateChainCalled); assertEqualCertificateChains(getServerCertificates(), clientCallback.asn1DerEncodedCertificateChain); assertEquals("RSA", clientCallback.authMethod); assertTrue(serverCallback.verifyCertificateChainCalled); assertEqualCertificateChains(getClientCertificates(), serverCallback.asn1DerEncodedCertificateChain); assertEquals("RSA", serverCallback.authMethod); assertTrue(clientCallback.clientCertificateRequestedCalled); assertNotNull(clientCallback.keyTypes); // this depends on the SSL_set_cipher_lists call in beforeHandshake // the three returned are the non-ephemeral cases. assertEquals(3, clientCallback.keyTypes.length); assertEquals("RSA", CipherSuite.getClientKeyType(clientCallback.keyTypes[0])); assertEquals("DSA", CipherSuite.getClientKeyType(clientCallback.keyTypes[1])); assertEquals("EC", CipherSuite.getClientKeyType(clientCallback.keyTypes[2])); assertEqualPrincipals(getCaPrincipals(), clientCallback.asn1DerEncodedX500Principals); assertFalse(serverCallback.clientCertificateRequestedCalled); assertTrue(clientCallback.handshakeCompletedCalled); assertTrue(serverCallback.handshakeCompletedCalled); } public void test_SSL_do_handshake_missing_required_certificate() throws Exception { // required client certificate negative case final ServerSocket listener = new ServerSocket(0); try { Hooks cHooks = new Hooks(); Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public int beforeHandshake(int c) throws SSLException { int s = super.beforeHandshake(c); NativeCrypto.SSL_set_client_CA_list(s, getCaPrincipals()); NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_PEER | NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT); return s; } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); fail(); } catch (ExecutionException expected) { assertEquals(SSLProtocolException.class, expected.getCause().getClass()); } } /** * Usually if a RuntimeException is thrown by the * clientCertificateRequestedCalled callback, the caller sees it * during the call to NativeCrypto_SSL_do_handshake. However, IIS * does not request client certs until after the initial * handshake. It does an SSL renegotiation, which means we need to * be able to deliver the callback's exception in cases like * SSL_read, SSL_write, and SSL_shutdown. */ public void test_SSL_do_handshake_clientCertificateRequested_throws_after_renegotiate() throws Exception { final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public int beforeHandshake(int context) throws SSLException { int s = super.beforeHandshake(context); NativeCrypto.SSL_clear_mode(s, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH); return s; } @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { NativeCrypto.SSL_read(s, fd, callback, new byte[1], 0, 1, 0); fail(); super.afterHandshake(session, s, c, sock, fd, callback); } @Override public void clientCertificateRequested(int s) { super.clientCertificateRequested(s); throw new RuntimeException("expected"); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_PEER); NativeCrypto.SSL_renegotiate(s); NativeCrypto.SSL_write(s, fd, callback, new byte[] { 42 }, 0, 1); super.afterHandshake(session, s, c, sock, fd, callback); } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); try { client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } catch (ExecutionException e) { if (!"expected".equals(e.getCause().getMessage())) { throw e; } } } public void test_SSL_do_handshake_client_timeout() throws Exception { // client timeout final ServerSocket listener = new ServerSocket(0); try { Hooks cHooks = new Hooks(); Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, 1, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, -1, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); fail(); } catch (ExecutionException expected) { assertEquals(SocketTimeoutException.class, expected.getCause().getClass()); } } public void test_SSL_do_handshake_server_timeout() throws Exception { // server timeout final ServerSocket listener = new ServerSocket(0); try { Hooks cHooks = new Hooks(); Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, -1, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 1, false, sHooks); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); fail(); } catch (ExecutionException expected) { assertEquals(SocketTimeoutException.class, expected.getCause().getClass()); } } public void test_SSL_set_session() throws Exception { try { NativeCrypto.SSL_set_session(NULL, NULL); fail(); } catch (NullPointerException expected) { } { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); NativeCrypto.SSL_set_session(s, NULL); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } { final int clientContext = NativeCrypto.SSL_CTX_new(); final int serverContext = NativeCrypto.SSL_CTX_new(); final ServerSocket listener = new ServerSocket(0); final int[] clientSession = new int[] { NULL }; final int[] serverSession = new int[] { NULL }; { Hooks cHooks = new Hooks() { @Override public int getContext() throws SSLException { return clientContext; } @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { super.afterHandshake(NULL, s, NULL, sock, fd, callback); clientSession[0] = session; } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public int getContext() throws SSLException { return serverContext; } @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { super.afterHandshake(NULL, s, NULL, sock, fd, callback); serverSession[0] = session; } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } assertEqualSessions(clientSession[0], serverSession[0]); { Hooks cHooks = new Hooks() { @Override public int getContext() throws SSLException { return clientContext; } @Override public int beforeHandshake(int c) throws SSLException { int s = NativeCrypto.SSL_new(clientContext); NativeCrypto.SSL_set_session(s, clientSession[0]); return s; } @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { assertEqualSessions(clientSession[0], session); super.afterHandshake(NULL, s, NULL, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public int getContext() throws SSLException { return serverContext; } @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { assertEqualSessions(serverSession[0], session); super.afterHandshake(NULL, s, NULL, sock, fd, callback); } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } NativeCrypto.SSL_SESSION_free(clientSession[0]); NativeCrypto.SSL_SESSION_free(serverSession[0]); NativeCrypto.SSL_CTX_free(serverContext); NativeCrypto.SSL_CTX_free(clientContext); } } public void test_SSL_set_session_creation_enabled() throws Exception { try { NativeCrypto.SSL_set_session_creation_enabled(NULL, false); fail(); } catch (NullPointerException expected) { } { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); NativeCrypto.SSL_set_session_creation_enabled(s, false); NativeCrypto.SSL_set_session_creation_enabled(s, true); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } final ServerSocket listener = new ServerSocket(0); // negative test case for SSL_set_session_creation_enabled(false) on client try { Hooks cHooks = new Hooks() { @Override public int beforeHandshake(int c) throws SSLException { int s = super.beforeHandshake(c); NativeCrypto.SSL_set_session_creation_enabled(s, false); return s; } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); fail(); } catch (ExecutionException expected) { assertEquals(SSLProtocolException.class, expected.getCause().getClass()); } // negative test case for SSL_set_session_creation_enabled(false) on server try { Hooks cHooks = new Hooks(); Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public int beforeHandshake(int c) throws SSLException { int s = super.beforeHandshake(c); NativeCrypto.SSL_set_session_creation_enabled(s, false); return s; } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); fail(); } catch (ExecutionException expected) { assertEquals(SSLProtocolException.class, expected.getCause().getClass()); } } public void test_SSL_set_tlsext_host_name() throws Exception { // NULL SSL try { NativeCrypto.SSL_set_tlsext_host_name(NULL, null); fail(); } catch (NullPointerException expected) { } final String hostname = "www.android.com"; { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); // null hostname try { NativeCrypto.SSL_set_tlsext_host_name(s, null); fail(); } catch (NullPointerException expected) { } // too long hostname try { char[] longHostname = new char[256]; Arrays.fill(longHostname, 'w'); NativeCrypto.SSL_set_tlsext_host_name(s, new String(longHostname)); fail(); } catch (SSLException expected) { } assertNull(NativeCrypto.SSL_get_servername(s)); NativeCrypto.SSL_set_tlsext_host_name(s, new String(hostname)); assertEquals(hostname, NativeCrypto.SSL_get_servername(s)); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } final ServerSocket listener = new ServerSocket(0); // normal Hooks cHooks = new Hooks() { @Override public int beforeHandshake(int c) throws SSLException { int s = super.beforeHandshake(c); NativeCrypto.SSL_set_tlsext_host_name(s, hostname); return s; } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { assertEquals(hostname, NativeCrypto.SSL_get_servername(s)); super.afterHandshake(session, s, c, sock, fd, callback); } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } public void test_SSL_get_servername_null() throws Exception { // NULL SSL try { NativeCrypto.SSL_get_servername(NULL); fail(); } catch (NullPointerException expected) { } int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); assertNull(NativeCrypto.SSL_get_servername(s)); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); // additional positive testing by test_SSL_set_tlsext_host_name } public void test_SSL_renegotiate() throws Exception { try { NativeCrypto.SSL_renegotiate(NULL); fail(); } catch (NullPointerException expected) { } final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { byte[] buffer = new byte[1]; NativeCrypto.SSL_read(s, fd, callback, buffer, 0, 1, 0); assertEquals(42, buffer[0]); super.afterHandshake(session, s, c, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { NativeCrypto.SSL_renegotiate(s); NativeCrypto.SSL_write(s, fd, callback, new byte[] { 42 }, 0, 1); super.afterHandshake(session, s, c, sock, fd, callback); } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } public void test_SSL_get_certificate() throws Exception { try { NativeCrypto.SSL_get_certificate(NULL); fail(); } catch (NullPointerException expected) { } final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { assertNull(NativeCrypto.SSL_get_certificate(s)); super.afterHandshake(session, s, c, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { assertEqualCertificateChains( getServerCertificates(), NativeCrypto.SSL_get_certificate(s)); super.afterHandshake(session, s, c, sock, fd, callback); } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } public void test_SSL_get_peer_cert_chain() throws Exception { try { NativeCrypto.SSL_get_peer_cert_chain(NULL); fail(); } catch (NullPointerException expected) { } final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { byte[][] cc = NativeCrypto.SSL_get_peer_cert_chain(s); assertEqualCertificateChains(getServerCertificates(), cc); super.afterHandshake(session, s, c, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } final byte[] BYTES = new byte[] { 2, -3, 5, 127, 0, -128 }; public void test_SSL_read() throws Exception { // NULL ssl try { NativeCrypto.SSL_read(NULL, null, null, null, 0, 0, 0); fail(); } catch (NullPointerException expected) { } // null FileDescriptor { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_read(s, null, DUMMY_CB, null, 0, 0, 0); fail(); } catch (NullPointerException expected) { } NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } // null SSLHandshakeCallbacks { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_read(s, INVALID_FD, null, null, 0, 0, 0); fail(); } catch (NullPointerException expected) { } NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } // null byte array { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_read(s, INVALID_FD, DUMMY_CB, null, 0, 0, 0); fail(); } catch (NullPointerException expected) { } NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } // handshaking not yet performed { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_read(s, INVALID_FD, DUMMY_CB, new byte[1], 0, 1, 0); fail(); } catch (SSLException expected) { } NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } final ServerSocket listener = new ServerSocket(0); // normal case { Hooks cHooks = new Hooks() { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { byte[] in = new byte[256]; assertEquals(BYTES.length, NativeCrypto.SSL_read(s, fd, callback, in, 0, BYTES.length, 0)); for (int i = 0; i < BYTES.length; i++) { assertEquals(BYTES[i], in[i]); } super.afterHandshake(session, s, c, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { NativeCrypto.SSL_write(s, fd, callback, BYTES, 0, BYTES.length); super.afterHandshake(session, s, c, sock, fd, callback); } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } // timeout case try { Hooks cHooks = new Hooks() { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { NativeCrypto.SSL_read(s, fd, callback, new byte[1], 0, 1, 1); fail(); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { NativeCrypto.SSL_read(s, fd, callback, new byte[1], 0, 1, 0); super.afterHandshake(session, s, c, sock, fd, callback); } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); fail(); } catch (ExecutionException expected) { assertEquals(SocketTimeoutException.class, expected.getCause().getClass()); } } public void test_SSL_write() throws Exception { try { NativeCrypto.SSL_write(NULL, null, null, null, 0, 0); fail(); } catch (NullPointerException expected) { } // null FileDescriptor { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_write(s, null, DUMMY_CB, null, 0, 1); fail(); } catch (NullPointerException expected) { } NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } // null SSLHandshakeCallbacks { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_write(s, INVALID_FD, null, null, 0, 1); fail(); } catch (NullPointerException expected) { } NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } // null byte array { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_write(s, INVALID_FD, DUMMY_CB, null, 0, 1); fail(); } catch (NullPointerException expected) { } NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } // handshaking not yet performed { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_write(s, INVALID_FD, DUMMY_CB, new byte[1], 0, 1); fail(); } catch (SSLException expected) { } NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } // positively tested by test_SSL_read } public void test_SSL_interrupt() throws Exception { // SSL_interrupt is a rare case that tolerates a null SSL argument NativeCrypto.SSL_interrupt(NULL); // also works without handshaking { int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); NativeCrypto.SSL_interrupt(s); NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); } final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { NativeCrypto.SSL_read(s, fd, callback, new byte[1], 0, 1, 0); super.afterHandshake(session, s, c, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public void afterHandshake(int session, final int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { new Thread() { public void run() { try { Thread.sleep(1*1000); NativeCrypto.SSL_interrupt(s); } catch (Exception e) { } } }.start(); assertEquals(-1, NativeCrypto.SSL_read(s, fd, callback, new byte[1], 0, 1, 0)); super.afterHandshake(session, s, c, sock, fd, callback); } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } public void test_SSL_shutdown() throws Exception { // null FileDescriptor try { NativeCrypto.SSL_shutdown(NULL, null, DUMMY_CB); } catch (NullPointerException expected) { } // null SSLHandshakeCallbacks try { NativeCrypto.SSL_shutdown(NULL, INVALID_FD, null); } catch (NullPointerException expected) { } // SSL_shutdown is a rare case that tolerates a null SSL argument NativeCrypto.SSL_shutdown(NULL, INVALID_FD, DUMMY_CB); // handshaking not yet performed int c = NativeCrypto.SSL_CTX_new(); int s = NativeCrypto.SSL_new(c); try { NativeCrypto.SSL_shutdown(s, INVALID_FD, DUMMY_CB); } catch (SSLProtocolException expected) { } NativeCrypto.SSL_free(s); NativeCrypto.SSL_CTX_free(c); // positively tested elsewhere because handshake uses use // SSL_shutdown to ensure SSL_SESSIONs are reused. } public void test_SSL_free() throws Exception { try { NativeCrypto.SSL_free(NULL); fail(); } catch (NullPointerException expected) { } int c = NativeCrypto.SSL_CTX_new(); NativeCrypto.SSL_free(NativeCrypto.SSL_new(c)); NativeCrypto.SSL_CTX_free(c); // additional positive testing elsewhere because handshake // uses use SSL_free to cleanup in afterHandshake. } public void test_SSL_SESSION_session_id() throws Exception { try { NativeCrypto.SSL_SESSION_session_id(NULL); fail(); } catch (NullPointerException expected) { } final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { byte[] id = NativeCrypto.SSL_SESSION_session_id(session); assertNotNull(id); assertEquals(32, id.length); super.afterHandshake(session, s, c, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } public void test_SSL_SESSION_get_time() throws Exception { try { NativeCrypto.SSL_SESSION_get_time(NULL); fail(); } catch (NullPointerException expected) { } final ServerSocket listener = new ServerSocket(0); { Hooks cHooks = new Hooks() { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { long time = NativeCrypto.SSL_SESSION_get_time(session); assertTrue(time != 0); assertTrue(time < System.currentTimeMillis()); super.afterHandshake(session, s, c, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } } public void test_SSL_SESSION_get_version() throws Exception { try { NativeCrypto.SSL_SESSION_get_version(NULL); fail(); } catch (NullPointerException expected) { } final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { String v = NativeCrypto.SSL_SESSION_get_version(session); assertTrue(StandardNames.SSL_SOCKET_PROTOCOLS.contains(v)); super.afterHandshake(session, s, c, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } public void test_SSL_SESSION_cipher() throws Exception { try { NativeCrypto.SSL_SESSION_cipher(NULL); fail(); } catch (NullPointerException expected) { } final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { String a = NativeCrypto.SSL_SESSION_cipher(session); assertTrue(NativeCrypto.OPENSSL_TO_STANDARD_CIPHER_SUITES.containsKey(a)); super.afterHandshake(session, s, c, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } public void test_SSL_SESSION_compress_meth_null() throws Exception { try { NativeCrypto.SSL_SESSION_compress_meth(NULL, NULL); fail(); } catch (NullPointerException expected) { } { int c = NativeCrypto.SSL_CTX_new(); try { NativeCrypto.SSL_SESSION_compress_meth(c, NULL); } catch (NullPointerException expected) { } NativeCrypto.SSL_CTX_free(c); } } public void test_SSL_SESSION_compress_meth_NULL() throws Exception { final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { assertEquals("NULL", NativeCrypto.SSL_SESSION_compress_meth(c, session)); super.afterHandshake(session, s, c, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } public void test_SSL_SESSION_compress_meth_ZLIB() throws Exception { final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public int beforeHandshake(int c) throws SSLException { int s = super.beforeHandshake(c); NativeCrypto.SSL_clear_options(s, NativeCrypto.SSL_OP_NO_COMPRESSION); return s; } @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { assertEquals("ZLIB", NativeCrypto.SSL_SESSION_compress_meth(c, session)); super.afterHandshake(session, s, c, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) { @Override public int beforeHandshake(int c) throws SSLException { int s = super.beforeHandshake(c); NativeCrypto.SSL_clear_options(s, NativeCrypto.SSL_OP_NO_COMPRESSION); return s; } }; Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } public void test_SSL_SESSION_free() throws Exception { try { NativeCrypto.SSL_SESSION_free(NULL); fail(); } catch (NullPointerException expected) { } // additional positive testing elsewhere because handshake // uses use SSL_SESSION_free to cleanup in afterHandshake. } public void test_i2d_SSL_SESSION() throws Exception { try { NativeCrypto.i2d_SSL_SESSION(NULL); fail(); } catch (NullPointerException expected) { } final ServerSocket listener = new ServerSocket(0); Hooks cHooks = new Hooks() { @Override public void afterHandshake(int session, int s, int c, Socket sock, FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { byte[] b = NativeCrypto.i2d_SSL_SESSION(session); assertNotNull(b); int session2 = NativeCrypto.d2i_SSL_SESSION(b); assertTrue(session2 != NULL); NativeCrypto.SSL_SESSION_free(session2); super.afterHandshake(session, s, c, sock, fd, callback); } }; Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()); Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks); Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks); client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); } public void test_d2i_SSL_SESSION() throws Exception { try { NativeCrypto.d2i_SSL_SESSION(null); fail(); } catch (NullPointerException expected) { } assertEquals(NULL, NativeCrypto.d2i_SSL_SESSION(new byte[0])); assertEquals(NULL, NativeCrypto.d2i_SSL_SESSION(new byte[1])); // positively testing by test_i2d_SSL_SESSION } public void test_X509_NAME_hashes() { // ensure these hash functions are stable over time since the // /system/etc/security/cacerts CA filenames have to be // consistent with the output. X500Principal name = new X500Principal("CN=localhost"); assertEquals(-1372642656, NativeCrypto.X509_NAME_hash(name)); // SHA1 assertEquals(-1626170662, NativeCrypto.X509_NAME_hash_old(name)); // MD5 } }