blob: e6c43f461b79135a68992e38c4d4305052e43751 [file] [log] [blame]
/* GENERATED SOURCE. DO NOT MODIFY. */
/*
* 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 com.android.org.conscrypt;
import static com.android.org.conscrypt.NativeConstants.SSL_MODE_CBC_RECORD_SPLITTING;
import static com.android.org.conscrypt.NativeConstants.SSL_MODE_ENABLE_FALSE_START;
import static com.android.org.conscrypt.NativeConstants.SSL_OP_CIPHER_SERVER_PREFERENCE;
import static com.android.org.conscrypt.NativeConstants.SSL_OP_NO_TICKET;
import static com.android.org.conscrypt.NativeConstants.SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
import static com.android.org.conscrypt.NativeConstants.SSL_VERIFY_NONE;
import static com.android.org.conscrypt.NativeConstants.SSL_VERIFY_PEER;
import static com.android.org.conscrypt.NativeConstants.TLS1_1_VERSION;
import static com.android.org.conscrypt.NativeConstants.TLS1_2_VERSION;
import static com.android.org.conscrypt.NativeConstants.TLS1_VERSION;
import static com.android.org.conscrypt.TestUtils.openTestFile;
import static com.android.org.conscrypt.TestUtils.readTestFile;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.ECPrivateKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
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.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLProtocolException;
import javax.security.auth.x500.X500Principal;
import com.android.org.conscrypt.NativeCrypto.SSLHandshakeCallbacks;
import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
import com.android.org.conscrypt.io.IoUtils;
import com.android.org.conscrypt.java.security.StandardNames;
import com.android.org.conscrypt.java.security.TestKeyStore;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Matchers;
import org.mockito.Mockito;
/**
* @hide This class is not part of the Android public SDK API
*/
@RunWith(JUnit4.class)
public class NativeCryptoTest {
private static final long NULL = 0;
private static final FileDescriptor INVALID_FD = new FileDescriptor();
private static final SSLHandshakeCallbacks DUMMY_CB =
new TestSSLHandshakeCallbacks(null, 0, null);
private static final long TIMEOUT_SECONDS = 5;
private static OpenSSLKey SERVER_PRIVATE_KEY;
private static OpenSSLX509Certificate[] SERVER_CERTIFICATES_HOLDER;
private static long[] SERVER_CERTIFICATE_REFS;
private static byte[][] ENCODED_SERVER_CERTIFICATES;
private static OpenSSLKey CLIENT_PRIVATE_KEY;
private static OpenSSLX509Certificate[] CLIENT_CERTIFICATES_HOLDER;
private static long[] CLIENT_CERTIFICATE_REFS;
private static byte[][] ENCODED_CLIENT_CERTIFICATES;
private static byte[][] CA_PRINCIPALS;
private static OpenSSLKey CHANNEL_ID_PRIVATE_KEY;
private static byte[] CHANNEL_ID;
private static Method m_Platform_getFileDescriptor;
@BeforeClass
public static void getPlatformMethods() throws Exception {
Class<?> c_Platform = TestUtils.conscryptClass("Platform");
m_Platform_getFileDescriptor =
c_Platform.getDeclaredMethod("getFileDescriptor", Socket.class);
m_Platform_getFileDescriptor.setAccessible(true);
}
private static OpenSSLKey getServerPrivateKey() {
initCerts();
return SERVER_PRIVATE_KEY;
}
private static long[] getServerCertificateRefs() {
initCerts();
return SERVER_CERTIFICATE_REFS;
}
private static byte[][] getEncodedServerCertificates() {
initCerts();
return ENCODED_SERVER_CERTIFICATES;
}
private static OpenSSLKey getClientPrivateKey() {
initCerts();
return CLIENT_PRIVATE_KEY;
}
private static long[] getClientCertificateRefs() {
initCerts();
return CLIENT_CERTIFICATE_REFS;
}
private static byte[][] getEncodedClientCertificates() {
initCerts();
return ENCODED_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 = OpenSSLKey.fromPrivateKey(serverPrivateKeyEntry.getPrivateKey());
SERVER_CERTIFICATES_HOLDER =
encodeCertificateList(serverPrivateKeyEntry.getCertificateChain());
SERVER_CERTIFICATE_REFS = getCertificateReferences(SERVER_CERTIFICATES_HOLDER);
ENCODED_SERVER_CERTIFICATES = getEncodedCertificates(SERVER_CERTIFICATES_HOLDER);
PrivateKeyEntry clientPrivateKeyEntry =
TestKeyStore.getClientCertificate().getPrivateKey("RSA", "RSA");
CLIENT_PRIVATE_KEY = OpenSSLKey.fromPrivateKey(clientPrivateKeyEntry.getPrivateKey());
CLIENT_CERTIFICATES_HOLDER =
encodeCertificateList(clientPrivateKeyEntry.getCertificateChain());
CLIENT_CERTIFICATE_REFS = getCertificateReferences(CLIENT_CERTIFICATES_HOLDER);
ENCODED_CLIENT_CERTIFICATES = getEncodedCertificates(CLIENT_CERTIFICATES_HOLDER);
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()};
initChannelIdKey();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static long[] getCertificateReferences(OpenSSLX509Certificate[] certs) {
final long[] certRefs = new long[certs.length];
for (int i = 0; i < certs.length; i++) {
certRefs[i] = certs[i].getContext();
}
return certRefs;
}
private static byte[][] getEncodedCertificates(OpenSSLX509Certificate[] certs) {
try {
final byte[][] encoded = new byte[certs.length][];
for (int i = 0; i < certs.length; i++) {
encoded[i] = certs[i].getEncoded();
}
return encoded;
} catch (CertificateEncodingException e) {
throw new RuntimeException(e);
}
}
private static OpenSSLX509Certificate[] encodeCertificateList(Certificate[] chain)
throws CertificateEncodingException {
final OpenSSLX509Certificate[] openSslCerts = new OpenSSLX509Certificate[chain.length];
for (int i = 0; i < chain.length; i++) {
openSslCerts[i] = OpenSSLX509Certificate.fromCertificate(chain[i]);
}
return openSslCerts;
}
private static synchronized void initChannelIdKey() throws Exception {
if (CHANNEL_ID_PRIVATE_KEY != null) {
return;
}
// NIST P-256 aka SECG secp256r1 aka X9.62 prime256v1
OpenSSLECGroupContext openSslSpec = OpenSSLECGroupContext.getCurveByName("prime256v1");
BigInteger s = new BigInteger(
"229cdbbf489aea584828a261a23f9ff8b0f66f7ccac98bf2096ab3aee41497c5", 16);
CHANNEL_ID_PRIVATE_KEY =
new OpenSSLECPrivateKey(new ECPrivateKeySpec(s, openSslSpec.getECParameterSpec()))
.getOpenSSLKey();
// Channel ID is the concatenation of the X and Y coordinates of the public key.
CHANNEL_ID = new BigInteger(
"702b07871fd7955c320b26f15e244e47eed60272124c92b9ebecf0b42f90069b"
+ "ab53592ebfeb4f167dbf3ce61513afb0e354c479b1c1b69874fa471293494f77",
16).toByteArray();
}
private static RSAPrivateCrtKey generateRsaKey() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair keyPair = kpg.generateKeyPair();
return (RSAPrivateCrtKey) keyPair.getPrivate();
}
private static NativeRef.EVP_PKEY getRsaPkey(RSAPrivateCrtKey privKey) throws Exception {
return new NativeRef.EVP_PKEY(NativeCrypto.EVP_PKEY_new_RSA(
privKey.getModulus().toByteArray(), privKey.getPublicExponent().toByteArray(),
privKey.getPrivateExponent().toByteArray(), privKey.getPrimeP().toByteArray(),
privKey.getPrimeQ().toByteArray(), privKey.getPrimeExponentP().toByteArray(),
privKey.getPrimeExponentQ().toByteArray(),
privKey.getCrtCoefficient().toByteArray()));
}
public static void assertEqualSessions(long expected, long 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(long[] expected, long[] actual) {
assertEquals(expected.length, actual.length);
for (int i = 0; i < expected.length; i++) {
NativeCrypto.X509_cmp(expected[i], null, actual[i], null);
}
}
public static void assertEqualByteArrays(byte[][] expected, byte[][] actual) {
assertEquals(Arrays.deepToString(expected), Arrays.deepToString(actual));
}
@Test(expected = NullPointerException.class)
public void EVP_PKEY_cmp_BothNullParameters() throws Exception {
NativeCrypto.EVP_PKEY_cmp(null, null);
}
@Test(expected = NullPointerException.class)
public void EVP_PKEY_cmp_withNullShouldThrow() throws Exception {
RSAPrivateCrtKey privKey1 = generateRsaKey();
NativeRef.EVP_PKEY pkey1 = getRsaPkey(privKey1);
assertNotSame(NULL, pkey1);
NativeCrypto.EVP_PKEY_cmp(pkey1, null);
}
@Test
public void test_EVP_PKEY_cmp() throws Exception {
RSAPrivateCrtKey privKey1 = generateRsaKey();
NativeRef.EVP_PKEY pkey1 = getRsaPkey(privKey1);
assertNotSame(NULL, pkey1);
NativeRef.EVP_PKEY pkey1_copy = getRsaPkey(privKey1);
assertNotSame(NULL, pkey1_copy);
NativeRef.EVP_PKEY pkey2 = getRsaPkey(generateRsaKey());
assertNotSame(NULL, pkey2);
assertEquals("Same keys should be the equal", 1, NativeCrypto.EVP_PKEY_cmp(pkey1, pkey1));
assertEquals(
"Same keys should be the equal", 1, NativeCrypto.EVP_PKEY_cmp(pkey1, pkey1_copy));
assertEquals(
"Different keys should not be equal", 0, NativeCrypto.EVP_PKEY_cmp(pkey1, pkey2));
}
@Test
public void test_SSL_CTX_new() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
assertTrue(c != NULL);
long c2 = NativeCrypto.SSL_CTX_new();
assertTrue(c != c2);
NativeCrypto.SSL_CTX_free(c, null);
NativeCrypto.SSL_CTX_free(c2, null);
}
@Test(expected = NullPointerException.class)
public void test_SSL_CTX_free_NullArgument() throws Exception {
NativeCrypto.SSL_CTX_free(NULL, null);
}
@Test
public void test_SSL_CTX_free() throws Exception {
NativeCrypto.SSL_CTX_free(NativeCrypto.SSL_CTX_new(), null);
}
@Test(expected = NullPointerException.class)
public void SSL_CTX_set_session_id_context_NullContextArgument() throws Exception {
NativeCrypto.SSL_CTX_set_session_id_context(NULL, null, new byte[0]);
}
@Test(expected = NullPointerException.class)
public void SSL_CTX_set_session_id_context_withNullShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
try {
NativeCrypto.SSL_CTX_set_session_id_context(c, null, null);
} finally {
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test(expected = IllegalArgumentException.class)
public void test_SSL_CTX_set_session_id_context_withInvalidIdShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
try {
NativeCrypto.SSL_CTX_set_session_id_context(c, null, new byte[33]);
} finally {
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test
public void test_SSL_CTX_set_session_id_context() throws Exception {
byte[] empty = new byte[0];
long c = NativeCrypto.SSL_CTX_new();
try {
NativeCrypto.SSL_CTX_set_session_id_context(c, null, empty);
NativeCrypto.SSL_CTX_set_session_id_context(c, null, new byte[32]);
} finally {
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test
public void test_SSL_new() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
assertTrue(s != NULL);
assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_NO_TICKET) != 0);
long s2 = NativeCrypto.SSL_new(c, null);
assertTrue(s != s2);
NativeCrypto.SSL_free(s2, null);
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test(expected = NullPointerException.class)
public void setLocalCertsAndPrivateKey_withNullSSLShouldThrow() throws Exception {
NativeCrypto.setLocalCertsAndPrivateKey(
NULL, null, getEncodedServerCertificates(), getServerPrivateKey().getNativeRef());
}
@Test(expected = NullPointerException.class)
public void setLocalCertsAndPrivateKey_withNullCertificatesShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.setLocalCertsAndPrivateKey(s, null, null, getServerPrivateKey().getNativeRef());
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test(expected = NullPointerException.class)
public void setLocalCertsAndPrivateKey_withNullKeyShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.setLocalCertsAndPrivateKey(s, null, getEncodedServerCertificates(), null);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test
public void setLocalCertsAndPrivateKey() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
NativeCrypto.setLocalCertsAndPrivateKey(
s, null, getEncodedServerCertificates(), getServerPrivateKey().getNativeRef());
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test(expected = NullPointerException.class)
public void SSL_set1_tls_channel_id_withNullChannelShouldThrow() throws Exception {
NativeCrypto.SSL_set1_tls_channel_id(NULL, null, null);
}
@Test(expected = NullPointerException.class)
public void SSL_set1_tls_channel_id_withNullKeyShouldThrow() throws Exception {
initChannelIdKey();
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_set1_tls_channel_id(s, null, null);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test
public void test_SSL_use_PrivateKey_for_tls_channel_id() throws Exception {
initChannelIdKey();
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
// Use the key natively. This works because the initChannelIdKey method ensures that the
// key is backed by OpenSSL.
NativeCrypto.SSL_set1_tls_channel_id(s, null, CHANNEL_ID_PRIVATE_KEY.getNativeRef());
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test(expected = NullPointerException.class)
public void SSL_get_mode_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_get_mode(NULL, null);
}
@Test
public void test_SSL_get_mode() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
assertTrue(NativeCrypto.SSL_get_mode(s, null) != 0);
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test(expected = NullPointerException.class)
public void SSL_set_mode_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_set_mode(NULL, null, 0);
}
@Test
public void test_SSL_set_mode_and_clear_mode() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
// check SSL_MODE_ENABLE_FALSE_START on by default for BoringSSL
assertEquals(SSL_MODE_ENABLE_FALSE_START,
NativeCrypto.SSL_get_mode(s, null) & SSL_MODE_ENABLE_FALSE_START);
// check SSL_MODE_CBC_RECORD_SPLITTING off by default
assertEquals(0, NativeCrypto.SSL_get_mode(s, null) & SSL_MODE_CBC_RECORD_SPLITTING);
// set SSL_MODE_ENABLE_FALSE_START on
NativeCrypto.SSL_set_mode(s, null, SSL_MODE_ENABLE_FALSE_START);
assertTrue((NativeCrypto.SSL_get_mode(s, null) & SSL_MODE_ENABLE_FALSE_START) != 0);
// clear SSL_MODE_ENABLE_FALSE_START off
NativeCrypto.SSL_clear_mode(s, null, SSL_MODE_ENABLE_FALSE_START);
assertTrue((NativeCrypto.SSL_get_mode(s, null) & SSL_MODE_ENABLE_FALSE_START) == 0);
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test(expected = NullPointerException.class)
public void SSL_get_options_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_get_options(NULL, null);
}
@Test
public void test_SSL_get_options() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
assertTrue(NativeCrypto.SSL_get_options(s, null) != 0);
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test(expected = NullPointerException.class)
public void SSL_set_options_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_set_options(NULL, null, 0);
}
@Test
public void test_SSL_set_options() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0);
NativeCrypto.SSL_set_options(s, null, SSL_OP_CIPHER_SERVER_PREFERENCE);
assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) != 0);
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test(expected = NullPointerException.class)
public void SSL_clear_options_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_clear_options(NULL, null, 0);
}
@Test
public void test_SSL_clear_options() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0);
NativeCrypto.SSL_set_options(s, null, SSL_OP_CIPHER_SERVER_PREFERENCE);
assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) != 0);
NativeCrypto.SSL_clear_options(s, null, SSL_OP_CIPHER_SERVER_PREFERENCE);
assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0);
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test(expected = NullPointerException.class)
public void SSL_set_protocol_versions_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_set_protocol_versions(NULL, null, 0, 0);
}
@Test
public void SSL_set_protocol_versions() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
assertEquals(1, NativeCrypto.SSL_set_protocol_versions(s, null, TLS1_VERSION, TLS1_1_VERSION));
assertEquals(1, NativeCrypto.SSL_set_protocol_versions(s, null, TLS1_2_VERSION, TLS1_2_VERSION));
assertEquals(0, NativeCrypto.SSL_set_protocol_versions(s, null, TLS1_2_VERSION + 413, TLS1_1_VERSION));
assertEquals(0, NativeCrypto.SSL_set_protocol_versions(s, null, TLS1_1_VERSION, TLS1_2_VERSION + 413));
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test(expected = NullPointerException.class)
public void SSL_set_cipher_lists_withNullSslShouldThrow() throws Exception {
NativeCrypto.SSL_set_cipher_lists(NULL, null, null);
}
@Test(expected = NullPointerException.class)
public void SSL_set_cipher_lists_withNullCiphersShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_set_cipher_lists(s, null, null);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test(expected = NullPointerException.class)
public void test_SSL_set_cipher_lists_withNullCipherShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_set_cipher_lists(s, null, new String[] {null});
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test
public void SSL_set_cipher_lists_withEmptyCiphersShouldSucceed() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
// Explicitly checking that the empty list is allowed.
// b/21816861
NativeCrypto.SSL_set_cipher_lists(s, null, new String[] {});
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test
public void SSL_set_cipher_lists_withIllegalCipherShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
// see OpenSSL ciphers man page
String[] illegals = new String[] {// empty
"",
// never standardized
"EXP1024-DES-CBC-SHA",
// IDEA
"IDEA-CBC-SHA", "IDEA-CBC-MD5"};
for (String illegal : illegals) {
try {
NativeCrypto.SSL_set_cipher_lists(s, null, new String[] {illegal});
fail("Exception now thrown for illegal cipher: " + illegal);
} catch (IllegalArgumentException expected) {
// Expected.
}
}
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test
public void SSL_set_cipher_lists_withValidCiphersShouldSucceed() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
List<String> ciphers = new ArrayList<String>(NativeCrypto.SUPPORTED_TLS_1_2_CIPHER_SUITES_SET);
NativeCrypto.SSL_set_cipher_lists(s, null, ciphers.toArray(new String[ciphers.size()]));
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test(expected = NullPointerException.class)
public void SSL_set_verify_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_set_verify(NULL, null, 0);
}
@Test
public void test_SSL_set_verify() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
NativeCrypto.SSL_set_verify(s, null, SSL_VERIFY_NONE);
NativeCrypto.SSL_set_verify(s, null, SSL_VERIFY_PEER);
NativeCrypto.SSL_set_verify(s, null, SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
NativeCrypto.SSL_set_verify(s, null, (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT));
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
private static final boolean DEBUG = false;
/**
* @hide This class is not part of the Android public SDK API
*/
public static class Hooks {
String negotiatedCipherSuite;
private OpenSSLKey channelIdPrivateKey;
boolean pskEnabled;
byte[] pskKey;
List<String> enabledCipherSuites;
/**
* @throws SSLException if an error occurs creating the context.
*/
public long getContext() throws SSLException {
return NativeCrypto.SSL_CTX_new();
}
public long beforeHandshake(long context) throws SSLException {
long s = NativeCrypto.SSL_new(context, null);
// Limit cipher suites to a known set so authMethod is known.
List<String> cipherSuites = new ArrayList<String>();
if (enabledCipherSuites == null) {
cipherSuites.add("ECDHE-RSA-AES128-SHA");
if (pskEnabled) {
// In TLS-PSK the client indicates that PSK key exchange is desired by offering
// at least one PSK cipher suite.
cipherSuites.add(0, "PSK-AES128-CBC-SHA");
}
} else {
cipherSuites.addAll(enabledCipherSuites);
}
// Protocol list is included for determining whether to send TLS_FALLBACK_SCSV
NativeCrypto.setEnabledCipherSuites(s, null,
cipherSuites.toArray(new String[cipherSuites.size()]),
new String[] {"TLSv1.2"});
if (channelIdPrivateKey != null) {
NativeCrypto.SSL_set1_tls_channel_id(s, null, channelIdPrivateKey.getNativeRef());
}
return s;
}
public void configureCallbacks(
@SuppressWarnings("unused") TestSSLHandshakeCallbacks callbacks) {}
public void clientCertificateRequested(@SuppressWarnings("unused") long s)
throws CertificateEncodingException, SSLException {}
public void afterHandshake(long session, long ssl, long context, Socket socket,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
if (session != NULL) {
negotiatedCipherSuite = NativeCrypto.SSL_SESSION_cipher(session);
NativeCrypto.SSL_SESSION_free(session);
}
if (ssl != NULL) {
try {
NativeCrypto.SSL_shutdown(ssl, null, fd, callback);
} catch (IOException e) {
// Expected.
}
NativeCrypto.SSL_free(ssl, null);
}
if (context != NULL) {
NativeCrypto.SSL_CTX_free(context, null);
}
if (socket != null) {
socket.close();
}
}
}
static class TestSSLHandshakeCallbacks implements SSLHandshakeCallbacks {
private final Socket socket;
private final long sslNativePointer;
private final Hooks hooks;
TestSSLHandshakeCallbacks(Socket socket, long sslNativePointer, Hooks hooks) {
this.socket = socket;
this.sslNativePointer = sslNativePointer;
this.hooks = hooks;
}
private long[] certificateChainRefs;
private String authMethod;
private boolean verifyCertificateChainCalled;
@Override
public void verifyCertificateChain(byte[][] certs, String authMethod)
throws CertificateException {
certificateChainRefs = new long[certs.length];
for (int i = 0; i < certs.length; ++i) {
byte[] cert = certs[i];
try {
certificateChainRefs[i] = NativeCrypto.d2i_X509(cert);
} catch (ParsingException e) {
throw new RuntimeException(e);
}
}
this.authMethod = authMethod;
this.verifyCertificateChainCalled = true;
}
private byte[] keyTypes;
private int[] signatureAlgs;
private byte[][] asn1DerEncodedX500Principals;
private boolean clientCertificateRequestedCalled;
@Override
public void clientCertificateRequested(
byte[] keyTypes, int[] signatureAlgs, byte[][] asn1DerEncodedX500Principals)
throws CertificateEncodingException, SSLException {
if (DEBUG) {
System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+ " clientCertificateRequested"
+ " keyTypes=" + Arrays.toString(keyTypes)
+ " asn1DerEncodedX500Principals="
+ Arrays.toString(asn1DerEncodedX500Principals));
}
this.keyTypes = keyTypes;
this.signatureAlgs = signatureAlgs;
this.asn1DerEncodedX500Principals = asn1DerEncodedX500Principals;
this.clientCertificateRequestedCalled = true;
if (hooks != null) {
hooks.clientCertificateRequested(sslNativePointer);
}
}
private boolean handshakeCompletedCalled;
@Override
public void onSSLStateChange(int type, int val) {
if (DEBUG) {
System.out.println(
"ssl=0x" + Long.toString(sslNativePointer, 16) + " onSSLStateChange");
}
this.handshakeCompletedCalled = true;
}
Socket getSocket() {
return socket;
}
private boolean clientPSKKeyRequestedInvoked;
private String clientPSKKeyRequestedIdentityHint;
private int clientPSKKeyRequestedResult;
private byte[] clientPSKKeyRequestedResultKey;
private byte[] clientPSKKeyRequestedResultIdentity;
@Override
public int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key) {
if (DEBUG) {
System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+ " clientPSKKeyRequested"
+ " identityHint=" + identityHint + " identity capacity=" + identity.length
+ " key capacity=" + key.length);
}
clientPSKKeyRequestedInvoked = true;
clientPSKKeyRequestedIdentityHint = identityHint;
if (clientPSKKeyRequestedResultKey != null) {
System.arraycopy(clientPSKKeyRequestedResultKey, 0, key, 0,
clientPSKKeyRequestedResultKey.length);
}
if (clientPSKKeyRequestedResultIdentity != null) {
System.arraycopy(clientPSKKeyRequestedResultIdentity, 0, identity, 0,
Math.min(clientPSKKeyRequestedResultIdentity.length, identity.length));
}
return clientPSKKeyRequestedResult;
}
private boolean serverPSKKeyRequestedInvoked;
private int serverPSKKeyRequestedResult;
private byte[] serverPSKKeyRequestedResultKey;
private String serverPSKKeyRequestedIdentityHint;
private String serverPSKKeyRequestedIdentity;
@Override
public int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
if (DEBUG) {
System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+ " serverPSKKeyRequested"
+ " identityHint=" + identityHint + " identity=" + identity
+ " key capacity=" + key.length);
}
serverPSKKeyRequestedInvoked = true;
serverPSKKeyRequestedIdentityHint = identityHint;
serverPSKKeyRequestedIdentity = identity;
if (serverPSKKeyRequestedResultKey != null) {
System.arraycopy(serverPSKKeyRequestedResultKey, 0, key, 0,
serverPSKKeyRequestedResultKey.length);
}
return serverPSKKeyRequestedResult;
}
private boolean onNewSessionEstablishedInvoked;
private boolean onNewSessionEstablishedSaveSession;
private long onNewSessionEstablishedSessionNativePointer;
@Override
public void onNewSessionEstablished(long sslSessionNativePtr) {
if (DEBUG) {
System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+ " onNewSessionCreated"
+ " ssl=0x" + Long.toString(sslSessionNativePtr, 16));
}
onNewSessionEstablishedInvoked = true;
if (onNewSessionEstablishedSaveSession) {
NativeCrypto.SSL_SESSION_up_ref(sslSessionNativePtr);
onNewSessionEstablishedSessionNativePointer = sslSessionNativePtr;
}
}
@Override
public long serverSessionRequested(byte[] id) {
// TODO(nathanmittler): Implement server-side caching for TLS < 1.3
return 0;
}
}
static class ClientHooks extends Hooks {
private String pskIdentity;
@Override
public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
super.configureCallbacks(callbacks);
if (pskEnabled) {
if (pskIdentity != null) {
// Create a NULL-terminated modified UTF-8 representation of pskIdentity.
byte[] b;
try {
b = pskIdentity.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 encoding not supported", e);
}
callbacks.clientPSKKeyRequestedResultIdentity = Arrays.copyOf(b, b.length + 1);
}
callbacks.clientPSKKeyRequestedResultKey = pskKey;
callbacks.clientPSKKeyRequestedResult = (pskKey != null) ? pskKey.length : 0;
}
}
@Override
public long beforeHandshake(long c) throws SSLException {
long s = super.beforeHandshake(c);
if (pskEnabled) {
NativeCrypto.set_SSL_psk_client_callback_enabled(s, null, true);
}
return s;
}
}
static class ServerHooks extends Hooks {
private final OpenSSLKey privateKey;
private final byte[][] certificates;
private boolean channelIdEnabled;
private byte[] channelIdAfterHandshake;
private Throwable channelIdAfterHandshakeException;
private String pskIdentityHint;
public ServerHooks() {
this(null, null);
}
ServerHooks(OpenSSLKey privateKey, byte[][] certificates) {
this.privateKey = privateKey;
this.certificates = certificates;
}
@Override
public long beforeHandshake(long c) throws SSLException {
long s = super.beforeHandshake(c);
if (privateKey != null && certificates != null) {
NativeCrypto.setLocalCertsAndPrivateKey(s, null, certificates, privateKey.getNativeRef());
}
if (channelIdEnabled) {
NativeCrypto.SSL_enable_tls_channel_id(s, null);
}
if (pskEnabled) {
NativeCrypto.set_SSL_psk_server_callback_enabled(s, null, true);
NativeCrypto.SSL_use_psk_identity_hint(s, null, pskIdentityHint);
}
NativeCrypto.SSL_set_verify(s, null, SSL_VERIFY_NONE);
return s;
}
@Override
public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
super.configureCallbacks(callbacks);
if (pskEnabled) {
callbacks.serverPSKKeyRequestedResultKey = pskKey;
callbacks.serverPSKKeyRequestedResult = (pskKey != null) ? pskKey.length : 0;
}
}
@Override
public void afterHandshake(long session, long ssl, long context, Socket socket,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
if (channelIdEnabled) {
try {
channelIdAfterHandshake = NativeCrypto.SSL_get_tls_channel_id(ssl, null);
} catch (Exception e) {
channelIdAfterHandshakeException = e;
}
}
super.afterHandshake(session, ssl, context, socket, fd, callback);
}
@Override
public void clientCertificateRequested(long s) {
fail("Server asked for client certificates");
}
}
public static Future<TestSSLHandshakeCallbacks> handshake(final ServerSocket listener,
final int timeout, final boolean client, final Hooks hooks, final byte[] alpnProtocols,
final ApplicationProtocolSelectorAdapter alpnSelector) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<TestSSLHandshakeCallbacks> future =
executor.submit(new Callable<TestSSLHandshakeCallbacks>() {
@Override
public TestSSLHandshakeCallbacks call() throws Exception {
@SuppressWarnings("resource")
// Socket needs to remain open after the handshake
Socket socket = (client ? new Socket(listener.getInetAddress(),
listener.getLocalPort())
: listener.accept());
if (timeout == -1) {
return new TestSSLHandshakeCallbacks(socket, 0, null);
}
FileDescriptor fd =
(FileDescriptor) m_Platform_getFileDescriptor.invoke(
null, socket);
long c = hooks.getContext();
long s = hooks.beforeHandshake(c);
TestSSLHandshakeCallbacks callback =
new TestSSLHandshakeCallbacks(socket, s, hooks);
hooks.configureCallbacks(callback);
if (DEBUG) {
System.out.println("ssl=0x" + Long.toString(s, 16) + " handshake"
+ " context=0x" + Long.toString(c, 16) + " socket=" + socket
+ " fd=0x" + Long.toString(System.identityHashCode(fd), 16)
+ " timeout=" + timeout + " client=" + client);
}
long session = NULL;
try {
if (client) {
NativeCrypto.SSL_set_connect_state(s, null);
} else {
NativeCrypto.SSL_set_accept_state(s, null);
}
if (alpnProtocols != null) {
NativeCrypto.setApplicationProtocols(s, null, client, alpnProtocols);
}
if (!client && alpnSelector != null) {
NativeCrypto.setApplicationProtocolSelector(s, null, alpnSelector);
}
NativeCrypto.SSL_do_handshake(s, null, fd, callback, timeout);
session = NativeCrypto.SSL_get1_session(s, null);
if (DEBUG) {
System.out.println("ssl=0x" + Long.toString(s, 16)
+ " handshake"
+ " session=0x" + Long.toString(session, 16));
}
} finally {
// Ensure afterHandshake is called to free resources
hooks.afterHandshake(session, s, c, socket, fd, callback);
}
return callback;
}
});
executor.shutdown();
return future;
}
@Test(expected = NullPointerException.class)
public void test_SSL_do_handshake_NULL_SSL() throws Exception {
NativeCrypto.SSL_do_handshake(NULL, null, null, null, 0);
}
@Test(expected = NullPointerException.class)
public void test_SSL_do_handshake_withNullFdShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
NativeCrypto.SSL_set_connect_state(s, null);
try {
NativeCrypto.SSL_do_handshake(s, null, null, null, 0);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test(expected = NullPointerException.class)
public void test_SSL_do_handshake_withNullShcShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
NativeCrypto.SSL_set_connect_state(s, null);
try {
NativeCrypto.SSL_do_handshake(s, null, INVALID_FD, null, 0);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test
public void test_SSL_do_handshake_normal() throws Exception {
// normal client and server case
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks();
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue(clientCallback.verifyCertificateChainCalled);
assertEqualCertificateChains(
getServerCertificateRefs(), clientCallback.certificateChainRefs);
assertEquals("ECDHE_RSA", clientCallback.authMethod);
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
assertTrue(clientCallback.onNewSessionEstablishedInvoked);
assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
}
@Test
public void test_SSL_do_handshake_reusedSession() throws Exception {
// normal client and server case
final ServerSocket listener = newServerSocket();
Future<TestSSLHandshakeCallbacks> client1 = handshake(listener, 0, true, new ClientHooks() {
@Override
public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
callbacks.onNewSessionEstablishedSaveSession = true;
}
}, null, null);
Future<TestSSLHandshakeCallbacks> server1 = handshake(listener, 0,
false, new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
callbacks.onNewSessionEstablishedSaveSession = true;
}
}, null, null);
TestSSLHandshakeCallbacks clientCallback1 = client1.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback1 = server1.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue(clientCallback1.verifyCertificateChainCalled);
assertEqualCertificateChains(
getServerCertificateRefs(), clientCallback1.certificateChainRefs);
assertEquals("ECDHE_RSA", clientCallback1.authMethod);
assertFalse(serverCallback1.verifyCertificateChainCalled);
assertFalse(clientCallback1.clientCertificateRequestedCalled);
assertFalse(serverCallback1.clientCertificateRequestedCalled);
assertFalse(clientCallback1.clientPSKKeyRequestedInvoked);
assertFalse(serverCallback1.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback1.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback1.serverPSKKeyRequestedInvoked);
assertTrue(clientCallback1.onNewSessionEstablishedInvoked);
assertTrue(serverCallback1.onNewSessionEstablishedInvoked);
assertTrue(clientCallback1.handshakeCompletedCalled);
assertTrue(serverCallback1.handshakeCompletedCalled);
final long clientSessionContext =
clientCallback1.onNewSessionEstablishedSessionNativePointer;
final long serverSessionContext =
serverCallback1.onNewSessionEstablishedSessionNativePointer;
Future<TestSSLHandshakeCallbacks> client2 = handshake(listener, 0, true, new ClientHooks() {
@Override
public long beforeHandshake(long c) throws SSLException {
long sslNativePtr = super.beforeHandshake(c);
NativeCrypto.SSL_set_session(sslNativePtr, null, clientSessionContext);
return sslNativePtr;
}
}, null, null);
Future<TestSSLHandshakeCallbacks> server2 = handshake(listener, 0,
false, new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public long beforeHandshake(long c) throws SSLException {
long sslNativePtr = super.beforeHandshake(c);
NativeCrypto.SSL_set_session(sslNativePtr, null, serverSessionContext);
return sslNativePtr;
}
}, null, null);
TestSSLHandshakeCallbacks clientCallback2 = client2.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback2 = server2.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue(clientCallback2.verifyCertificateChainCalled);
assertEqualCertificateChains(
getServerCertificateRefs(), clientCallback2.certificateChainRefs);
assertEquals("ECDHE_RSA", clientCallback2.authMethod);
assertFalse(serverCallback2.verifyCertificateChainCalled);
assertFalse(clientCallback2.clientCertificateRequestedCalled);
assertFalse(serverCallback2.clientCertificateRequestedCalled);
assertFalse(clientCallback2.clientPSKKeyRequestedInvoked);
assertFalse(serverCallback2.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback2.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback2.serverPSKKeyRequestedInvoked);
assertTrue(clientCallback2.onNewSessionEstablishedInvoked);
assertTrue(serverCallback2.onNewSessionEstablishedInvoked);
assertTrue(clientCallback2.handshakeCompletedCalled);
assertTrue(serverCallback2.handshakeCompletedCalled);
NativeCrypto.SSL_SESSION_free(clientSessionContext);
NativeCrypto.SSL_SESSION_free(serverSessionContext);
}
@Test
public void test_SSL_do_handshake_optional_client_certificate() throws Exception {
// optional client certificate case
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public void clientCertificateRequested(long s)
throws CertificateEncodingException, SSLException {
super.clientCertificateRequested(s);
NativeCrypto.setLocalCertsAndPrivateKey(
s, null, getEncodedClientCertificates(), getClientPrivateKey().getNativeRef());
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public long beforeHandshake(long c) throws SSLException {
long s = super.beforeHandshake(c);
NativeCrypto.SSL_set_client_CA_list(s, null, getCaPrincipals());
NativeCrypto.SSL_set_verify(s, null, SSL_VERIFY_PEER);
return s;
}
};
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue(clientCallback.verifyCertificateChainCalled);
assertEqualCertificateChains(
getServerCertificateRefs(), clientCallback.certificateChainRefs);
assertEquals("ECDHE_RSA", clientCallback.authMethod);
assertTrue(serverCallback.verifyCertificateChainCalled);
assertEqualCertificateChains(
getClientCertificateRefs(), serverCallback.certificateChainRefs);
assertEquals("ECDHE_RSA", serverCallback.authMethod);
assertTrue(clientCallback.clientCertificateRequestedCalled);
assertNotNull(clientCallback.keyTypes);
assertNotNull(clientCallback.signatureAlgs);
assertEquals(new HashSet<String>(Arrays.asList("EC", "RSA")),
SSLUtils.getSupportedClientKeyTypes(
clientCallback.keyTypes, clientCallback.signatureAlgs));
assertEqualPrincipals(getCaPrincipals(), clientCallback.asn1DerEncodedX500Principals);
assertFalse(serverCallback.clientCertificateRequestedCalled);
assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
assertTrue(clientCallback.onNewSessionEstablishedInvoked);
assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
}
@Test
public void test_SSL_do_handshake_missing_required_certificate() throws Exception {
// required client certificate negative case
final ServerSocket listener = newServerSocket();
try {
Hooks cHooks = new Hooks();
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public long beforeHandshake(long c) throws SSLException {
long s = super.beforeHandshake(c);
NativeCrypto.SSL_set_client_CA_list(s, null, getCaPrincipals());
NativeCrypto.SSL_set_verify(
s, null, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
return s;
}
};
@SuppressWarnings("unused")
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
fail();
} catch (ExecutionException expected) {
assertEquals(SSLProtocolException.class, expected.getCause().getClass());
}
}
@Test
public void test_SSL_do_handshake_client_timeout() throws Exception {
// client timeout
final ServerSocket listener = newServerSocket();
Socket serverSocket = null;
try {
Hooks cHooks = new Hooks();
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, 1, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, -1, false, sHooks, null, null);
serverSocket = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).getSocket();
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
fail();
} catch (ExecutionException expected) {
assertEquals(SocketTimeoutException.class, expected.getCause().getClass());
} finally {
// Manually close peer socket when testing timeout
IoUtils.closeQuietly(serverSocket);
}
}
@Test
public void test_SSL_do_handshake_server_timeout() throws Exception {
// server timeout
final ServerSocket listener = newServerSocket();
Socket clientSocket = null;
try {
Hooks cHooks = new Hooks();
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, -1, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 1, false, sHooks, null, null);
clientSocket = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).getSocket();
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
fail();
} catch (ExecutionException expected) {
assertEquals(SocketTimeoutException.class, expected.getCause().getClass());
} finally {
// Manually close peer socket when testing timeout
IoUtils.closeQuietly(clientSocket);
}
}
@Test
public void test_SSL_do_handshake_with_channel_id_normal() throws Exception {
initChannelIdKey();
// Normal handshake with TLS Channel ID.
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks();
cHooks.channelIdPrivateKey = CHANNEL_ID_PRIVATE_KEY;
// TLS Channel ID currently requires ECDHE-based key exchanges.
cHooks.enabledCipherSuites = Collections.singletonList("ECDHE-RSA-AES128-SHA");
ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
sHooks.channelIdEnabled = true;
sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue(clientCallback.verifyCertificateChainCalled);
assertEqualCertificateChains(
getServerCertificateRefs(), clientCallback.certificateChainRefs);
assertEquals("ECDHE_RSA", clientCallback.authMethod);
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
assertTrue(clientCallback.onNewSessionEstablishedInvoked);
assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertNull(sHooks.channelIdAfterHandshakeException);
assertEqualByteArrays(CHANNEL_ID, sHooks.channelIdAfterHandshake);
}
@Test
public void test_SSL_do_handshake_with_channel_id_not_supported_by_server() throws Exception {
initChannelIdKey();
// Client tries to use TLS Channel ID but the server does not enable/offer the extension.
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks();
cHooks.channelIdPrivateKey = CHANNEL_ID_PRIVATE_KEY;
// TLS Channel ID currently requires ECDHE-based key exchanges.
cHooks.enabledCipherSuites = Collections.singletonList("ECDHE-RSA-AES128-SHA");
ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
sHooks.channelIdEnabled = false;
sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue(clientCallback.verifyCertificateChainCalled);
assertEqualCertificateChains(
getServerCertificateRefs(), clientCallback.certificateChainRefs);
assertEquals("ECDHE_RSA", clientCallback.authMethod);
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
assertTrue(clientCallback.onNewSessionEstablishedInvoked);
assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertNull(sHooks.channelIdAfterHandshakeException);
assertNull(sHooks.channelIdAfterHandshake);
}
@Test
public void test_SSL_do_handshake_with_channel_id_not_enabled_by_client() throws Exception {
initChannelIdKey();
// Client does not use TLS Channel ID when the server has the extension enabled/offered.
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks();
cHooks.channelIdPrivateKey = null;
// TLS Channel ID currently requires ECDHE-based key exchanges.
cHooks.enabledCipherSuites = Collections.singletonList("ECDHE-RSA-AES128-SHA");
ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
sHooks.channelIdEnabled = true;
sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue(clientCallback.verifyCertificateChainCalled);
assertEqualCertificateChains(
getServerCertificateRefs(), clientCallback.certificateChainRefs);
assertEquals("ECDHE_RSA", clientCallback.authMethod);
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
assertTrue(clientCallback.onNewSessionEstablishedInvoked);
assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertNull(sHooks.channelIdAfterHandshakeException);
assertNull(sHooks.channelIdAfterHandshake);
}
@Test
public void test_SSL_do_handshake_with_psk_normal() throws Exception {
// normal TLS-PSK client and server case
final ServerSocket listener = newServerSocket();
Hooks cHooks = new ClientHooks();
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
sHooks.pskEnabled = true;
cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
sHooks.pskKey = cHooks.pskKey;
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertFalse(clientCallback.verifyCertificateChainCalled);
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
assertTrue(clientCallback.onNewSessionEstablishedInvoked);
assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertTrue(serverCallback.serverPSKKeyRequestedInvoked);
assertContains(cHooks.negotiatedCipherSuite, "PSK");
assertEquals(cHooks.negotiatedCipherSuite, sHooks.negotiatedCipherSuite);
assertNull(clientCallback.clientPSKKeyRequestedIdentityHint);
assertNull(serverCallback.serverPSKKeyRequestedIdentityHint);
assertEquals("", serverCallback.serverPSKKeyRequestedIdentity);
}
@Test
public void test_SSL_do_handshake_with_psk_with_identity_and_hint() throws Exception {
// normal TLS-PSK client and server case where the server provides the client with a PSK
// identity hint, and the client provides the server with a PSK identity.
final ServerSocket listener = newServerSocket();
ClientHooks cHooks = new ClientHooks();
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
sHooks.pskEnabled = true;
cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
sHooks.pskKey = cHooks.pskKey;
sHooks.pskIdentityHint = "Some non-ASCII characters: \u00c4\u0332";
cHooks.pskIdentity = "More non-ASCII characters: \u00f5\u044b";
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertFalse(clientCallback.verifyCertificateChainCalled);
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
assertTrue(clientCallback.onNewSessionEstablishedInvoked);
assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertTrue(serverCallback.serverPSKKeyRequestedInvoked);
assertContains(cHooks.negotiatedCipherSuite, "PSK");
assertEquals(cHooks.negotiatedCipherSuite, sHooks.negotiatedCipherSuite);
assertEquals(sHooks.pskIdentityHint, clientCallback.clientPSKKeyRequestedIdentityHint);
assertEquals(sHooks.pskIdentityHint, serverCallback.serverPSKKeyRequestedIdentityHint);
assertEquals(cHooks.pskIdentity, serverCallback.serverPSKKeyRequestedIdentity);
}
@Test
@SuppressWarnings("deprecation")
public void test_SSL_do_handshake_with_psk_with_identity_and_hint_of_max_length()
throws Exception {
// normal TLS-PSK client and server case where the server provides the client with a PSK
// identity hint, and the client provides the server with a PSK identity.
final ServerSocket listener = newServerSocket();
ClientHooks cHooks = new ClientHooks();
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
sHooks.pskEnabled = true;
cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
sHooks.pskKey = cHooks.pskKey;
sHooks.pskIdentityHint = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx";
cHooks.pskIdentity = "123456789012345678901234567890123456789012345678901234567890"
+ "12345678901234567890123456789012345678901234567890123456789012345678";
assertEquals(PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES, sHooks.pskIdentityHint.length());
assertEquals(PSKKeyManager.MAX_IDENTITY_LENGTH_BYTES, cHooks.pskIdentity.length());
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertFalse(clientCallback.verifyCertificateChainCalled);
assertFalse(serverCallback.verifyCertificateChainCalled);
assertFalse(clientCallback.clientCertificateRequestedCalled);
assertFalse(serverCallback.clientCertificateRequestedCalled);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
assertTrue(serverCallback.serverPSKKeyRequestedInvoked);
assertContains(cHooks.negotiatedCipherSuite, "PSK");
assertEquals(cHooks.negotiatedCipherSuite, sHooks.negotiatedCipherSuite);
assertEquals(sHooks.pskIdentityHint, clientCallback.clientPSKKeyRequestedIdentityHint);
assertEquals(sHooks.pskIdentityHint, serverCallback.serverPSKKeyRequestedIdentityHint);
assertEquals(cHooks.pskIdentity, serverCallback.serverPSKKeyRequestedIdentity);
}
@Test
public void test_SSL_do_handshake_with_psk_key_mismatch() throws Exception {
final ServerSocket listener = newServerSocket();
ClientHooks cHooks = new ClientHooks();
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
sHooks.pskEnabled = true;
cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
sHooks.pskKey = "1, 2, 3, 3, Testing...".getBytes("UTF-8");
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
try {
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
fail();
} catch (ExecutionException expected) {
assertEquals(SSLProtocolException.class, expected.getCause().getClass());
}
}
@Test
public void test_SSL_do_handshake_with_psk_with_no_client_key() throws Exception {
final ServerSocket listener = newServerSocket();
ClientHooks cHooks = new ClientHooks();
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
sHooks.pskEnabled = true;
cHooks.pskKey = null;
sHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
try {
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
fail();
} catch (ExecutionException expected) {
assertEquals(SSLProtocolException.class, expected.getCause().getClass());
}
}
@Test
public void test_SSL_do_handshake_with_psk_with_no_server_key() throws Exception {
final ServerSocket listener = newServerSocket();
ClientHooks cHooks = new ClientHooks();
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
sHooks.pskEnabled = true;
cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
sHooks.pskKey = null;
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
try {
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
fail();
} catch (ExecutionException expected) {
assertEquals(SSLProtocolException.class, expected.getCause().getClass());
}
}
@Test
@SuppressWarnings("deprecation")
public void test_SSL_do_handshake_with_psk_key_too_long() throws Exception {
final ServerSocket listener = newServerSocket();
ClientHooks cHooks = new ClientHooks() {
@Override
public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
super.configureCallbacks(callbacks);
callbacks.clientPSKKeyRequestedResult = PSKKeyManager.MAX_KEY_LENGTH_BYTES + 1;
}
};
ServerHooks sHooks = new ServerHooks();
cHooks.pskEnabled = true;
sHooks.pskEnabled = true;
cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
sHooks.pskKey = cHooks.pskKey;
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
try {
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
fail();
} catch (ExecutionException expected) {
assertEquals(SSLProtocolException.class, expected.getCause().getClass());
}
}
@Test
public void test_SSL_do_handshake_with_ocsp_response() throws Exception {
final byte[] OCSP_TEST_DATA = new byte[] {1, 2, 3, 4};
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public long beforeHandshake(long c) throws SSLException {
long s = super.beforeHandshake(c);
NativeCrypto.SSL_enable_ocsp_stapling(s, null);
return s;
}
@Override
public void afterHandshake(long session, long ssl, long context, Socket socket,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
assertEqualByteArrays(OCSP_TEST_DATA, NativeCrypto.SSL_get_ocsp_response(ssl, null));
super.afterHandshake(session, ssl, context, socket, fd, callback);
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public long beforeHandshake(long c) throws SSLException {
long s = super.beforeHandshake(c);
NativeCrypto.SSL_set_ocsp_response(s, null, OCSP_TEST_DATA);
return s;
}
};
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
}
@Test
public void test_SSL_do_handshake_with_sct_extension() throws Exception {
// Fake SCT extension has a length of overall extension (unsigned 16-bit).
// Each SCT entry has a length (unsigned 16-bit) and data.
final byte[] SCT_TEST_DATA = new byte[] {0, 6, 0, 4, 1, 2, 3, 4};
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public long beforeHandshake(long c) throws SSLException {
long s = super.beforeHandshake(c);
NativeCrypto.SSL_enable_signed_cert_timestamps(s, null);
return s;
}
@Override
public void afterHandshake(long session, long ssl, long context, Socket socket,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
assertEqualByteArrays(
SCT_TEST_DATA, NativeCrypto.SSL_get_signed_cert_timestamp_list(ssl, null));
super.afterHandshake(session, ssl, context, socket, fd, callback);
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public long beforeHandshake(long c) throws SSLException {
long s = super.beforeHandshake(c);
NativeCrypto.SSL_set_signed_cert_timestamp_list(s, null, SCT_TEST_DATA);
return s;
}
};
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue(clientCallback.onNewSessionEstablishedInvoked);
assertTrue(serverCallback.onNewSessionEstablishedInvoked);
assertTrue(clientCallback.handshakeCompletedCalled);
assertTrue(serverCallback.handshakeCompletedCalled);
}
@Test
@SuppressWarnings("deprecation")
public void test_SSL_use_psk_identity_hint() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_use_psk_identity_hint(s, null, null);
NativeCrypto.SSL_use_psk_identity_hint(s, null, "test");
try {
// 800 characters is much longer than the permitted maximum.
StringBuilder pskIdentityHint = new StringBuilder();
for (int i = 0; i < 160; i++) {
pskIdentityHint.append(" long");
}
assertTrue(pskIdentityHint.length() > PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES);
NativeCrypto.SSL_use_psk_identity_hint(s, null, pskIdentityHint.toString());
fail();
} catch (SSLException expected) {
// Expected.
}
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test(expected = NullPointerException.class)
public void SSL_set_session_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_set_session(NULL, null, NULL);
}
@Test
public void test_SSL_set_session() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
NativeCrypto.SSL_set_session(s, null, NULL);
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
{
final long clientContext = NativeCrypto.SSL_CTX_new();
final long serverContext = NativeCrypto.SSL_CTX_new();
final ServerSocket listener = newServerSocket();
final long[] clientSession = new long[] {NULL};
final long[] serverSession = new long[] {NULL};
{
Hooks cHooks = new Hooks() {
@Override
public long getContext() throws SSLException {
return clientContext;
}
@Override
public void afterHandshake(long session, long s, long 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(), getEncodedServerCertificates()) {
@Override
public long getContext() throws SSLException {
return serverContext;
}
@Override
public void afterHandshake(long session, long s, long 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, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
assertEqualSessions(clientSession[0], serverSession[0]);
{
Hooks cHooks = new Hooks() {
@Override
public long getContext() throws SSLException {
return clientContext;
}
@Override
public long beforeHandshake(long c) throws SSLException {
long s = NativeCrypto.SSL_new(clientContext, null);
NativeCrypto.SSL_set_session(s, null, clientSession[0]);
return s;
}
@Override
public void afterHandshake(long session, long s, long 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(), getEncodedServerCertificates()) {
@Override
public long getContext() throws SSLException {
return serverContext;
}
@Override
public void afterHandshake(long session, long s, long 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, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
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, null);
NativeCrypto.SSL_CTX_free(clientContext, null);
}
}
@Test(expected = NullPointerException.class)
public void SSL_set_session_creation_enabled_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_set_session_creation_enabled(NULL, null, false);
}
@Test
public void test_SSL_set_session_creation_enabled() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
NativeCrypto.SSL_set_session_creation_enabled(s, null, false);
NativeCrypto.SSL_set_session_creation_enabled(s, null, true);
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
final ServerSocket listener = newServerSocket();
// negative test case for SSL_set_session_creation_enabled(false) on client
{
Hooks cHooks = new Hooks() {
@Override
public long beforeHandshake(long c) throws SSLException {
long s = super.beforeHandshake(c);
NativeCrypto.SSL_set_session_creation_enabled(s, null, false);
return s;
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, 0, true, cHooks, null, null);
@SuppressWarnings("unused")
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
try {
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
fail();
} catch (ExecutionException expected) {
assertEquals(SSLProtocolException.class, expected.getCause().getClass());
}
try {
server.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
{
Hooks cHooks = new Hooks();
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public long beforeHandshake(long c) throws SSLException {
long s = super.beforeHandshake(c);
NativeCrypto.SSL_set_session_creation_enabled(s, null, false);
return s;
}
};
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, 0, true, cHooks, null, null);
@SuppressWarnings("unused")
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
try {
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
fail();
} catch (ExecutionException expected) {
assertEquals(SSLHandshakeException.class, expected.getCause().getClass());
}
try {
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
fail();
} catch (ExecutionException expected) {
assertEquals(SSLProtocolException.class, expected.getCause().getClass());
}
}
}
@Test(expected = NullPointerException.class)
public void SSL_set_tlsext_host_name_withNullSslShouldThrow() throws Exception {
NativeCrypto.SSL_set_tlsext_host_name(NULL, null, null);
}
@Test(expected = NullPointerException.class)
public void SSL_set_tlsext_host_name_withNullHostnameShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_set_tlsext_host_name(s, null, null);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test(expected = SSLException.class)
public void SSL_set_tlsext_host_name_withTooLongHostnameShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
char[] longHostname = new char[256];
Arrays.fill(longHostname, 'w');
NativeCrypto.SSL_set_tlsext_host_name(s, null, new String(longHostname));
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test
public void test_SSL_set_tlsext_host_name() throws Exception {
final String hostname = "www.android.com";
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
assertNull(NativeCrypto.SSL_get_servername(s, null));
NativeCrypto.SSL_set_tlsext_host_name(s, null, hostname);
assertEquals(hostname, NativeCrypto.SSL_get_servername(s, null));
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
final ServerSocket listener = newServerSocket();
// normal
Hooks cHooks = new Hooks() {
@Override
public long beforeHandshake(long c) throws SSLException {
long s = super.beforeHandshake(c);
NativeCrypto.SSL_set_tlsext_host_name(s, null, hostname);
return s;
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
SSLHandshakeCallbacks callback) throws Exception {
assertEquals(hostname, NativeCrypto.SSL_get_servername(s, null));
super.afterHandshake(session, s, c, sock, fd, callback);
}
};
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void alpnWithProtocolListShouldSucceed() throws Exception {
final byte[] clientAlpnProtocols =
SSLUtils.encodeProtocols(new String[] {"http/1.1", "foo", "spdy/2"});
final byte[] serverAlpnProtocols =
SSLUtils.encodeProtocols(new String[] {"spdy/2", "foo", "bar"});
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long ssl, long context, Socket socket,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
assertEquals("spdy/2", new String(negotiated, "UTF-8"));
super.afterHandshake(session, ssl, context, socket, fd, callback);
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public void afterHandshake(long session, long ssl, long c, Socket sock,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
assertEquals("spdy/2", new String(negotiated, "UTF-8"));
super.afterHandshake(session, ssl, c, sock, fd, callback);
}
};
ServerSocket listener = newServerSocket();
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, 0, true, cHooks, clientAlpnProtocols, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, serverAlpnProtocols, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void alpnWithProtocolListShouldFail() throws Exception {
final byte[] clientAlpnProtocols =
SSLUtils.encodeProtocols(new String[] {"http/1.1", "foo", "spdy/2"});
final byte[] serverAlpnProtocols =
SSLUtils.encodeProtocols(new String[] {"h2", "bar", "baz"});
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long ssl, long context, Socket socket,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
assertNull(negotiated);
super.afterHandshake(session, ssl, context, socket, fd, callback);
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public void afterHandshake(long session, long ssl, long c, Socket sock,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
assertNull(negotiated);
super.afterHandshake(session, ssl, c, sock, fd, callback);
}
};
ServerSocket listener = newServerSocket();
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, 0, true, cHooks, clientAlpnProtocols, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, serverAlpnProtocols, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void alpnWithServerProtocolSelectorShouldSucceed() throws Exception {
final byte[] clientAlpnProtocols =
SSLUtils.encodeProtocols(new String[] {"http/1.1", "foo", "spdy/2"});
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long ssl, long context, Socket socket,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
assertEquals("spdy/2", new String(negotiated, "UTF-8"));
super.afterHandshake(session, ssl, context, socket, fd, callback);
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public void afterHandshake(long session, long ssl, long c, Socket sock,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
assertEquals("spdy/2", new String(negotiated, "UTF-8"));
super.afterHandshake(session, ssl, c, sock, fd, callback);
}
};
ApplicationProtocolSelector selector = Mockito.mock(ApplicationProtocolSelector.class);
SSLEngine engine = Mockito.mock(SSLEngine.class);
ApplicationProtocolSelectorAdapter adapter = new ApplicationProtocolSelectorAdapter(engine, selector);
when(selector.selectApplicationProtocol(same(engine), Matchers.anyListOf(String.class)))
.thenReturn("spdy/2");
ServerSocket listener = newServerSocket();
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, 0, true, cHooks, clientAlpnProtocols, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, adapter);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void alpnWithServerProtocolSelectorShouldFail() throws Exception {
final byte[] clientAlpnProtocols =
SSLUtils.encodeProtocols(new String[] {"http/1.1", "foo", "spdy/2"});
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long ssl, long context, Socket socket,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
assertNull(negotiated);
super.afterHandshake(session, ssl, context, socket, fd, callback);
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public void afterHandshake(long session, long ssl, long c, Socket sock,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
assertNull(negotiated);
super.afterHandshake(session, ssl, c, sock, fd, callback);
}
};
ApplicationProtocolSelector selector = Mockito.mock(ApplicationProtocolSelector.class);
SSLEngine engine = Mockito.mock(SSLEngine.class);
ApplicationProtocolSelectorAdapter adapter = new ApplicationProtocolSelectorAdapter(engine, selector);
when(selector.selectApplicationProtocol(same(engine), Matchers.anyListOf(String.class)))
.thenReturn("h2");
ServerSocket listener = newServerSocket();
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, 0, true, cHooks, clientAlpnProtocols, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, adapter);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test(expected = NullPointerException.class)
public void test_SSL_get_servername_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_get_servername(NULL, null);
}
@Test
public void SSL_get_servername_shouldReturnNull() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
assertNull(NativeCrypto.SSL_get_servername(s, null));
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
// additional positive testing by test_SSL_set_tlsext_host_name
}
@Test(expected = NullPointerException.class)
public void SSL_get0_peer_certificates_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_get0_peer_certificates(NULL, null);
}
@Test
public void test_SSL_get0_peer_certificates() throws Exception {
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
SSLHandshakeCallbacks callback) throws Exception {
byte[][] cc = NativeCrypto.SSL_get0_peer_certificates(s, null);
assertEqualByteArrays(getEncodedServerCertificates(), cc);
super.afterHandshake(session, s, c, sock, fd, callback);
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void test_SSL_cipher_names() throws Exception {
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks();
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
// Both legacy and standard names are accepted.
cHooks.enabledCipherSuites = Collections.singletonList("ECDHE-RSA-AES128-GCM-SHA256");
sHooks.enabledCipherSuites =
Collections.singletonList("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
// The standard name is always reported.
assertEquals("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", cHooks.negotiatedCipherSuite);
assertEquals("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", sHooks.negotiatedCipherSuite);
}
private final byte[] BYTES = new byte[] {2, -3, 5, 127, 0, -128};
@Test(expected = NullPointerException.class)
public void SSL_read_withNullSslShouldThrow() throws Exception {
NativeCrypto.SSL_read(NULL, null, null, null, null, 0, 0, 0);
}
@Test(expected = NullPointerException.class)
public void SSL_read_withNullFdShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_read(s, null, null, DUMMY_CB, null, 0, 0, 0);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test(expected = NullPointerException.class)
public void SSL_read_withNullCallbacksShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_read(s, null, INVALID_FD, null, null, 0, 0, 0);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test(expected = NullPointerException.class)
public void SSL_read_withNullBytesShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_read(s, null, INVALID_FD, DUMMY_CB, null, 0, 0, 0);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test(expected = SSLException.class)
public void SSL_read_beforeHandshakeShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_read(s, null, INVALID_FD, DUMMY_CB, new byte[1], 0, 1, 0);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test
public void test_SSL_read() throws Exception {
final ServerSocket listener = newServerSocket();
// normal case
{
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long s, long c, Socket sock,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
byte[] in = new byte[256];
assertEquals(BYTES.length,
NativeCrypto.SSL_read(s, null, 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(), getEncodedServerCertificates()) {
@Override
public void afterHandshake(long session, long s, long c, Socket sock,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
NativeCrypto.SSL_write(s, null, fd, callback, BYTES, 0, BYTES.length, 0);
super.afterHandshake(session, s, c, sock, fd, callback);
}
};
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
// timeout case
try {
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long s, long c, Socket sock,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
NativeCrypto.SSL_read(s, null, fd, callback, new byte[1], 0, 1, 1);
fail();
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public void afterHandshake(long session, long s, long c, Socket sock,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
NativeCrypto.SSL_read(s, null, fd, callback, new byte[1], 0, 1, 0);
super.afterHandshake(session, s, c, sock, fd, callback);
}
};
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, 0, true, cHooks, null, null);
@SuppressWarnings("unused")
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
fail();
} catch (ExecutionException expected) {
assertEquals(SocketTimeoutException.class, expected.getCause().getClass());
}
}
@Test(expected = NullPointerException.class)
public void SSL_write_withNullSslShouldThrow() throws Exception {
NativeCrypto.SSL_write(NULL, null, null, null, null, 0, 0, 0);
}
@Test(expected = NullPointerException.class)
public void SSL_write_withNullFdShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_write(s, null, null, DUMMY_CB, null, 0, 1, 0);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test(expected = NullPointerException.class)
public void SSL_write_withNullCallbacksShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_write(s, null, INVALID_FD, null, null, 0, 1, 0);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test(expected = NullPointerException.class)
public void SSL_write_withNullBytesShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_write(s, null, INVALID_FD, DUMMY_CB, null, 0, 1, 0);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test(expected = SSLException.class)
public void SSL_write_beforeHandshakeShouldThrow() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
NativeCrypto.SSL_write(s, null, INVALID_FD, DUMMY_CB, new byte[1], 0, 1, 0);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test
public void SSL_interrupt_withNullShouldSucceed() {
// SSL_interrupt is a rare case that tolerates a null SSL argument
NativeCrypto.SSL_interrupt(NULL, null);
}
@Test
public void SSL_interrupt_withoutHandshakeShouldSucceed() throws Exception {
// also works without handshaking
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
NativeCrypto.SSL_interrupt(s, null);
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
@Test
public void test_SSL_interrupt() throws Exception {
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
SSLHandshakeCallbacks callback) throws Exception {
NativeCrypto.SSL_read(s, null, fd, callback, new byte[1], 0, 1, 0);
super.afterHandshake(session, s, c, sock, fd, callback);
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
@Override
public void afterHandshake(long session, final long s, long c, Socket sock,
FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
NativeCrypto.SSL_interrupt(s, null);
} catch (Exception e) {
// Expected.
}
}
}.start();
assertEquals(-1, NativeCrypto.SSL_read(s, null, fd, callback, new byte[1], 0, 1, 0));
super.afterHandshake(session, s, c, sock, fd, callback);
}
};
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
private static abstract class SSLSessionWrappedTask {
public abstract void run(long sslSession) throws Exception;
}
private void wrapWithSSLSession(SSLSessionWrappedTask task) throws Exception {
long c = NativeCrypto.SSL_CTX_new();
long s = NativeCrypto.SSL_new(c, null);
try {
task.run(s);
} finally {
NativeCrypto.SSL_free(s, null);
NativeCrypto.SSL_CTX_free(c, null);
}
}
@Test
public void SSL_shutdown_withNullFdShouldSucceed() throws Exception {
// We tolerate a null FileDescriptor
wrapWithSSLSession(new SSLSessionWrappedTask() {
@Override
public void run(long sslSession) throws Exception {
NativeCrypto.SSL_shutdown(sslSession, null, null, DUMMY_CB);
}
});
}
@Test(expected = NullPointerException.class)
public void SSL_shutdown_withNullCallbacksShouldThrow() throws Exception {
wrapWithSSLSession(new SSLSessionWrappedTask() {
@Override
public void run(long sslSession) throws Exception {
NativeCrypto.SSL_shutdown(sslSession, null, INVALID_FD, null);
}
});
}
@Test
public void SSL_shutdown_withNullSslShouldSucceed() throws Exception {
// SSL_shutdown is a rare case that tolerates a null SSL argument
NativeCrypto.SSL_shutdown(NULL, null, INVALID_FD, DUMMY_CB);
}
@Test(expected = SocketException.class)
public void SSL_shutdown_beforeHandshakeShouldThrow() throws Exception {
// handshaking not yet performed
wrapWithSSLSession(new SSLSessionWrappedTask() {
@Override
public void run(long sslSession) throws Exception {
NativeCrypto.SSL_shutdown(sslSession, null, INVALID_FD, DUMMY_CB);
}
});
// positively tested elsewhere because handshake uses use
// SSL_shutdown to ensure SSL_SESSIONs are reused.
}
@Test(expected = NullPointerException.class)
public void SSL_free_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_free(NULL, null);
}
@Test
public void test_SSL_free() throws Exception {
long c = NativeCrypto.SSL_CTX_new();
NativeCrypto.SSL_free(NativeCrypto.SSL_new(c, null), null);
NativeCrypto.SSL_CTX_free(c, null);
// additional positive testing elsewhere because handshake
// uses use SSL_free to cleanup in afterHandshake.
}
@Test(expected = NullPointerException.class)
public void SSL_SESSION_session_id_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_SESSION_session_id(NULL);
}
@Test
public void test_SSL_SESSION_session_id() throws Exception {
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long s, long 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(), getEncodedServerCertificates());
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test(expected = NullPointerException.class)
public void SSL_SESSION_get_time_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_SESSION_get_time(NULL);
}
@Test
public void test_SSL_SESSION_get_time() throws Exception {
final ServerSocket listener = newServerSocket();
{
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long s, long 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(), getEncodedServerCertificates());
Future<TestSSLHandshakeCallbacks> client =
handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
}
@Test(expected = NullPointerException.class)
public void SSL_SESSION_get_version_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_SESSION_get_version(NULL);
}
@Test
public void test_SSL_SESSION_get_version() throws Exception {
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long s, long 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(), getEncodedServerCertificates());
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test(expected = NullPointerException.class)
public void SSL_SESSION_cipher_withNullShouldThrow() throws Exception {
NativeCrypto.SSL_SESSION_cipher(NULL);
}
@Test
public void test_SSL_SESSION_cipher() throws Exception {
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
SSLHandshakeCallbacks callback) throws Exception {
String nativeCipher = NativeCrypto.SSL_SESSION_cipher(session);
String javaCipher = NativeCrypto.cipherSuiteFromJava(nativeCipher);
assertTrue(NativeCrypto.SUPPORTED_TLS_1_2_CIPHER_SUITES_SET.contains(javaCipher));
// SSL_SESSION_cipher should return a standard name rather than an OpenSSL name.
assertTrue(nativeCipher.startsWith("TLS_"));
super.afterHandshake(session, s, c, sock, fd, callback);
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
/*
* Additional positive testing elsewhere because handshake
* uses use SSL_SESSION_free to cleanup in afterHandshake.
*/
@Test(expected = NullPointerException.class)
public void SSL_SESSION_free_NullArgument() throws Exception {
NativeCrypto.SSL_SESSION_free(NULL);
}
@Test(expected = NullPointerException.class)
public void i2d_SSL_Session_WithNullSessionShouldThrow() throws Exception {
NativeCrypto.i2d_SSL_SESSION(NULL);
}
@Test
public void test_i2d_SSL_SESSION() throws Exception {
final ServerSocket listener = newServerSocket();
Hooks cHooks = new Hooks() {
@Override
public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
SSLHandshakeCallbacks callback) throws Exception {
byte[] b = NativeCrypto.i2d_SSL_SESSION(session);
assertNotNull(b);
long session2 = NativeCrypto.d2i_SSL_SESSION(b);
assertTrue(session2 != NULL);
// Make sure d2i_SSL_SESSION retores SSL_SESSION_cipher value http://b/7091840
assertTrue(NativeCrypto.SSL_SESSION_cipher(session2) != null);
assertEquals(NativeCrypto.SSL_SESSION_cipher(session),
NativeCrypto.SSL_SESSION_cipher(session2));
NativeCrypto.SSL_SESSION_free(session2);
super.afterHandshake(session, s, c, sock, fd, callback);
}
};
Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
Future<TestSSLHandshakeCallbacks> server =
handshake(listener, 0, false, sHooks, null, null);
client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test(expected = NullPointerException.class)
public void d2i_SSL_SESSION_NullArgument() throws Exception {
NativeCrypto.d2i_SSL_SESSION(null);
}
@Test(expected = IOException.class)
public void d2i_SSL_SESSION_EmptyArgument() throws Exception {
NativeCrypto.d2i_SSL_SESSION(new byte[0]);
}
@Test(expected = IOException.class)
public void d2i_SSL_SESSION_InvalidArgument() throws Exception {
NativeCrypto.d2i_SSL_SESSION(new byte[1]);
}
@Test
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
}
@Test
public void test_RAND_bytes_Success() throws Exception {
byte[] output = new byte[128];
NativeCrypto.RAND_bytes(output);
boolean isZero = true;
for (byte anOutput : output) {
isZero &= (anOutput == 0);
}
assertFalse("Random output was zero. This is a very low probability event (1 in 2^128) "
+ "and probably indicates an error.",
isZero);
}
@Test(expected = RuntimeException.class)
public void RAND_bytes_withNullShouldThrow() throws Exception {
NativeCrypto.RAND_bytes(null);
}
@Test(expected = NullPointerException.class)
public void test_EVP_get_digestbyname_NullArgument() throws Exception {
NativeCrypto.EVP_get_digestbyname(null);
}
@Test(expected = RuntimeException.class)
public void EVP_get_digestbyname_withEmptyShouldThrow() throws Exception {
NativeCrypto.EVP_get_digestbyname("");
}
@Test(expected = RuntimeException.class)
public void EVP_get_digestbyname_withInvalidDigestShouldThrow() throws Exception {
NativeCrypto.EVP_get_digestbyname("foobar");
}
@Test
public void test_EVP_get_digestbyname() throws Exception {
assertTrue(NativeCrypto.EVP_get_digestbyname("sha256") != NULL);
}
@Test
public void test_EVP_DigestSignInit() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair kp = kpg.generateKeyPair();
RSAPrivateCrtKey privKey = (RSAPrivateCrtKey) kp.getPrivate();
NativeRef.EVP_PKEY pkey;
pkey = new NativeRef.EVP_PKEY(NativeCrypto.EVP_PKEY_new_RSA(
privKey.getModulus().toByteArray(), privKey.getPublicExponent().toByteArray(),
privKey.getPrivateExponent().toByteArray(), privKey.getPrimeP().toByteArray(),
privKey.getPrimeQ().toByteArray(), privKey.getPrimeExponentP().toByteArray(),
privKey.getPrimeExponentQ().toByteArray(),
privKey.getCrtCoefficient().toByteArray()));
assertNotNull(pkey);
final NativeRef.EVP_MD_CTX ctx = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
long evpMd = NativeCrypto.EVP_get_digestbyname("sha256");
NativeCrypto.EVP_DigestSignInit(ctx, evpMd, pkey);
try {
NativeCrypto.EVP_DigestSignInit(ctx, 0, pkey);
fail();
} catch (RuntimeException expected) {
// Expected.
}
try {
NativeCrypto.EVP_DigestSignInit(ctx, evpMd, null);
fail();
} catch (RuntimeException expected) {
// Expected.
}
}
@Test(expected = NullPointerException.class)
public void get_RSA_private_params_NullArgument() throws Exception {
NativeCrypto.get_RSA_private_params(null);
}
@Test(expected = RuntimeException.class)
public void test_get_RSA_private_params() throws Exception {
// Test getting params for the wrong kind of key.
final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
assertFalse(groupCtx == NULL);
NativeRef.EC_GROUP group = new NativeRef.EC_GROUP(groupCtx);
NativeRef.EVP_PKEY ctx = new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(group));
NativeCrypto.get_RSA_private_params(ctx);
}
@Test(expected = NullPointerException.class)
public void get_RSA_public_params_NullArgument() throws Exception {
NativeCrypto.get_RSA_public_params(null);
}
@Test(expected = RuntimeException.class)
public void test_get_RSA_public_params() throws Exception {
// Test getting params for the wrong kind of key.
final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
assertFalse(groupCtx == NULL);
NativeRef.EC_GROUP group = new NativeRef.EC_GROUP(groupCtx);
NativeRef.EVP_PKEY ctx = new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(group));
NativeCrypto.get_RSA_public_params(ctx);
}
@Test(expected = NullPointerException.class)
public void RSA_size_NullArgumentFailure() throws Exception {
NativeCrypto.RSA_size(null);
}
@Test(expected = NullPointerException.class)
public void RSA_private_encrypt_NullArgumentFailure() throws Exception {
NativeCrypto.RSA_private_encrypt(0, new byte[0], new byte[0], null, 0);
}
@Test(expected = NullPointerException.class)
public void RSA_private_decrypt_NullArgumentFailure() throws Exception {
NativeCrypto.RSA_private_decrypt(0, new byte[0], new byte[0], null, 0);
}
@Test(expected = NullPointerException.class)
public void test_RSA_public_encrypt_NullArgumentFailure() throws Exception {
NativeCrypto.RSA_public_encrypt(0, new byte[0], new byte[0], null, 0);
}
@Test(expected = NullPointerException.class)
public void test_RSA_public_decrypt_NullArgumentFailure() throws Exception {
NativeCrypto.RSA_public_decrypt(0, new byte[0], new byte[0], null, 0);
}
/*
* Test vector generation:
* openssl rand -hex 16
*/
private static final byte[] AES_128_KEY = new byte[] {
(byte) 0x3d, (byte) 0x4f, (byte) 0x89, (byte) 0x70, (byte) 0xb1, (byte) 0xf2,
(byte) 0x75, (byte) 0x37, (byte) 0xf4, (byte) 0x0a, (byte) 0x39, (byte) 0x29,
(byte) 0x8a, (byte) 0x41, (byte) 0x55, (byte) 0x5f,
};
@Test
public void testEC_GROUP() throws Exception {
/* Test using NIST's P-256 curve */
check_EC_GROUP("prime256v1",
"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
"6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
"4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 1L);
}
private void check_EC_GROUP(String name, String pStr, String aStr, String bStr, String xStr,
String yStr, String nStr, long hLong) throws Exception {
long groupRef = NativeCrypto.EC_GROUP_new_by_curve_name(name);
assertFalse(groupRef == NULL);
NativeRef.EC_GROUP group = new NativeRef.EC_GROUP(groupRef);
// prime
BigInteger p = new BigInteger(pStr, 16);
// first coefficient
BigInteger a = new BigInteger(aStr, 16);
// second coefficient
BigInteger b = new BigInteger(bStr, 16);
// x affine coordinate of generator
BigInteger x = new BigInteger(xStr, 16);
// y affine coordinate of generator
BigInteger y = new BigInteger(yStr, 16);
// order of the generator
BigInteger n = new BigInteger(nStr, 16);
// cofactor of generator
BigInteger h = BigInteger.valueOf(hLong);
byte[][] pab = NativeCrypto.EC_GROUP_get_curve(group);
assertEquals(3, pab.length);
BigInteger p2 = new BigInteger(pab[0]);
assertEquals(p, p2);
BigInteger a2 = new BigInteger(pab[1]);
assertEquals(a, a2);
BigInteger b2 = new BigInteger(pab[2]);
assertEquals(b, b2);
NativeRef.EC_POINT point =
new NativeRef.EC_POINT(NativeCrypto.EC_GROUP_get_generator(group));
byte[][] xy = NativeCrypto.EC_POINT_get_affine_coordinates(group, point);
assertEquals(2, xy.length);
BigInteger x2 = new BigInteger(xy[0]);
assertEquals(x, x2);
BigInteger y2 = new BigInteger(xy[1]);
assertEquals(y, y2);
BigInteger n2 = new BigInteger(NativeCrypto.EC_GROUP_get_order(group));
assertEquals(n, n2);
BigInteger h2 = new BigInteger(NativeCrypto.EC_GROUP_get_cofactor(group));
assertEquals(h, h2);
NativeRef.EVP_PKEY key1 = new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(group));
NativeRef.EC_GROUP groupTmp = new NativeRef.EC_GROUP(NativeCrypto.EC_KEY_get1_group(key1));
assertEquals(NativeCrypto.EC_GROUP_get_curve_name(group),
NativeCrypto.EC_GROUP_get_curve_name(groupTmp));
}
@Test(expected = NullPointerException.class)
public void test_EC_KEY_get_private_key_NullArgumentFailure() throws Exception {
NativeCrypto.EC_KEY_get_private_key(null);
}
@Test(expected = NullPointerException.class)
public void test_EC_KEY_get_public_key_NullArgumentFailure() throws Exception {
NativeCrypto.EC_KEY_get_public_key(null);
}
@Test
public void test_ECKeyPairGenerator_CurvesAreValid() throws Exception {
OpenSSLECKeyPairGenerator.assertCurvesAreValid();
}
@Test
public void test_ECDH_compute_key_null_key_Failure() throws Exception {
final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
assertFalse(groupCtx == NULL);
NativeRef.EC_GROUP groupRef = new NativeRef.EC_GROUP(groupCtx);
NativeRef.EVP_PKEY pkey1Ref =
new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(groupRef));
NativeRef.EVP_PKEY pkey2Ref =
new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(groupRef));
byte[] out = new byte[128];
int outOffset = 0;
// Assert that the method under test works fine with the two
// non-null keys
NativeCrypto.ECDH_compute_key(out, outOffset, pkey1Ref, pkey2Ref);
// Assert that it fails when only the first key is null
try {
NativeCrypto.ECDH_compute_key(out, outOffset, null, pkey2Ref);
fail();
} catch (NullPointerException expected) {
// Expected.
}
// Assert that it fails when only the second key is null
try {
NativeCrypto.ECDH_compute_key(out, outOffset, pkey1Ref, null);
fail();
} catch (NullPointerException expected) {
// Expected.
}
}
@Test(expected = NullPointerException.class)
public void EVP_CipherInit_ex_withNullCtxShouldThrow() throws Exception {
final long evpCipher = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
NativeCrypto.EVP_CipherInit_ex(null, evpCipher, null, null, true);
}
@Test
public void test_EVP_CipherInit_ex_Null_Failure() throws Exception {
final NativeRef.EVP_CIPHER_CTX ctx =
new NativeRef.EVP_CIPHER_CTX(NativeCrypto.EVP_CIPHER_CTX_new());
final long evpCipher = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
/* Initialize encrypting. */
NativeCrypto.EVP_CipherInit_ex(ctx, evpCipher, null, null, true);
NativeCrypto.EVP_CipherInit_ex(ctx, NULL, null, null, true);
/* Initialize decrypting. */
NativeCrypto.EVP_CipherInit_ex(ctx, evpCipher, null, null, false);
NativeCrypto.EVP_CipherInit_ex(ctx, NULL, null, null, false);
}
@Test
public void test_EVP_CipherInit_ex_Success() throws Exception {
final NativeRef.EVP_CIPHER_CTX ctx =
new NativeRef.EVP_CIPHER_CTX(NativeCrypto.EVP_CIPHER_CTX_new());
final long evpCipher = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
NativeCrypto.EVP_CipherInit_ex(ctx, evpCipher, AES_128_KEY, null, true);
}
@Test
public void test_EVP_CIPHER_iv_length() throws Exception {
long aes128ecb = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
assertEquals(0, NativeCrypto.EVP_CIPHER_iv_length(aes128ecb));
long aes128cbc = NativeCrypto.EVP_get_cipherbyname("aes-128-cbc");
assertEquals(16, NativeCrypto.EVP_CIPHER_iv_length(aes128cbc));
}
@Test
public void test_OpenSSLKey_toJava() throws Exception {
OpenSSLKey key1;
BigInteger e = BigInteger.valueOf(65537);
key1 = new OpenSSLKey(NativeCrypto.RSA_generate_key_ex(1024, e.toByteArray()));
assertTrue(key1.getPublicKey() instanceof RSAPublicKey);
final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
assertFalse(groupCtx == NULL);
NativeRef.EC_GROUP group1 = new NativeRef.EC_GROUP(groupCtx);
key1 = new OpenSSLKey(NativeCrypto.EC_KEY_generate_key(group1));
assertTrue(key1.getPublicKey() instanceof ECPublicKey);
}
@Test
public void test_create_BIO_InputStream() throws Exception {
byte[] actual = "Test".getBytes("UTF-8");
ByteArrayInputStream is = new ByteArrayInputStream(actual);
@SuppressWarnings("resource")
OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
try {
byte[] buffer = new byte[1024];
int numRead = NativeCrypto.BIO_read(bis.getBioContext(), buffer);
assertEquals(actual.length, numRead);
assertEquals(Arrays.toString(actual),
Arrays.toString(Arrays.copyOfRange(buffer, 0, numRead)));
} finally {
bis.release();
}
}
@Test
public void test_create_BIO_OutputStream() throws Exception {
byte[] actual = "Test".getBytes("UTF-8");
ByteArrayOutputStream os = new ByteArrayOutputStream();
long ctx = NativeCrypto.create_BIO_OutputStream(os);
try {
NativeCrypto.BIO_write(ctx, actual, 0, actual.length);
assertEquals(actual.length, os.size());
assertEquals(Arrays.toString(actual), Arrays.toString(os.toByteArray()));
} finally {
NativeCrypto.BIO_free_all(ctx);
}
}
@Test
public void test_get_ocsp_single_extension() throws Exception {
final String OCSP_SCT_LIST_OID = "1.3.6.1.4.1.11129.2.4.5";
byte[] ocspResponse = readTestFile("ocsp-response.der");
byte[] expected = readTestFile("ocsp-response-sct-extension.der");
OpenSSLX509Certificate certificate =
OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("cert-ct-poisoned.pem"));
OpenSSLX509Certificate issuer =
OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("ca-cert.pem"));
byte[] extension = NativeCrypto.get_ocsp_single_extension(
ocspResponse, OCSP_SCT_LIST_OID, certificate.getContext(), certificate, issuer.getContext(), issuer);
assertEqualByteArrays(expected, extension);
}
private static long getRawPkeyCtxForEncrypt() throws Exception {
return NativeCrypto.EVP_PKEY_encrypt_init(getRsaPkey(generateRsaKey()));
}
private static NativeRef.EVP_PKEY_CTX getPkeyCtxForEncrypt() throws Exception {
return new NativeRef.EVP_PKEY_CTX(getRawPkeyCtxForEncrypt());
}
@Test(expected = NullPointerException.class)
public void EVP_PKEY_encrypt_NullKeyArgument() throws Exception {
NativeCrypto.EVP_PKEY_encrypt(null, new byte[128], 0, new byte[128], 0, 128);
}
@Test(expected = NullPointerException.class)
public void EVP_PKEY_encrypt_NullOutputArgument() throws Exception {
NativeCrypto.EVP_PKEY_encrypt(getPkeyCtxForEncrypt(), null, 0, new byte[128], 0, 128);
}
@Test(expected = NullPointerException.class)
public void EVP_PKEY_encrypt_NullInputArgument() throws Exception {
NativeCrypto.EVP_PKEY_encrypt(getPkeyCtxForEncrypt(), new byte[128], 0, null, 0, 128);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void EVP_PKEY_encrypt_OutputIndexOOBUnder() throws Exception {
NativeCrypto.EVP_PKEY_encrypt(
getPkeyCtxForEncrypt(), new byte[128], -1, new byte[128], 0, 128);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void EVP_PKEY_encrypt_OutputIndexOOBOver() throws Exception {
NativeCrypto.EVP_PKEY_encrypt(
getPkeyCtxForEncrypt(), new byte[128], 129, new byte[128], 0, 128);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void EVP_PKEY_encrypt_InputIndexOOBUnder() throws Exception {
NativeCrypto.EVP_PKEY_encrypt(
getPkeyCtxForEncrypt(), new byte[128], 0, new byte[128], -1, 128);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void EVP_PKEY_encrypt_InputIndexOOBOver() throws Exception {
NativeCrypto.EVP_PKEY_encrypt(
getPkeyCtxForEncrypt(), new byte[128], 0, new byte[128], 128, 128);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void EVP_PKEY_encrypt_InputLengthNegative() throws Exception {
NativeCrypto.EVP_PKEY_encrypt(
getPkeyCtxForEncrypt(), new byte[128], 0, new byte[128], 0, -1);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void EVP_PKEY_encrypt_InputIndexLengthOOB() throws Exception {
NativeCrypto.EVP_PKEY_encrypt(
getPkeyCtxForEncrypt(), new byte[128], 0, new byte[128], 100, 29);
}
@Test(expected = NullPointerException.class)
public void EVP_PKEY_CTX_set_rsa_mgf1_md_NullPkeyCtx() throws Exception {
NativeCrypto.EVP_PKEY_CTX_set_rsa_mgf1_md(NULL, EvpMdRef.SHA256.EVP_MD);
}
@Test(expected = NullPointerException.class)
public void EVP_PKEY_CTX_set_rsa_mgf1_md_NullMdCtx() throws Exception {
long pkeyCtx = getRawPkeyCtxForEncrypt();
NativeRef.EVP_PKEY_CTX holder = new NativeRef.EVP_PKEY_CTX(pkeyCtx);
NativeCrypto.EVP_PKEY_CTX_set_rsa_mgf1_md(pkeyCtx, NULL);
}
@Test(expected = NullPointerException.class)
public void EVP_PKEY_CTX_set_rsa_oaep_md_NullPkeyCtx() throws Exception {
NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_md(NULL, EvpMdRef.SHA256.EVP_MD);
}
@Test(expected = NullPointerException.class)
public void EVP_PKEY_CTX_set_rsa_oaep_md_NullMdCtx() throws Exception {
long pkeyCtx = getRawPkeyCtxForEncrypt();
new NativeRef.EVP_PKEY_CTX(pkeyCtx);
NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_md(pkeyCtx, NULL);
}
@Test(expected = ParsingException.class)
public void d2i_X509_InvalidFailure() throws Exception {
NativeCrypto.d2i_X509(new byte[1]);
}
private static void assertContains(String actualValue, String expectedSubstring) {
if (actualValue == null) {
return;
}
if (actualValue.contains(expectedSubstring)) {
return;
}
fail("\"" + actualValue + "\" does not contain \"" + expectedSubstring + "\"");
}
private static ServerSocket newServerSocket() throws IOException {
return new ServerSocket(0, 50, TestUtils.getLoopbackAddress());
}
}