diff --git a/JavaTestPorts/titan.JavaTestPorts.Common_Components.Abstract_Socket/user_provided/org/eclipse/titan/titan_JavaTestPorts_Common_Components_Abstract_Socket/user_provided/Abstract_Socket.java b/JavaTestPorts/titan.JavaTestPorts.Common_Components.Abstract_Socket/user_provided/org/eclipse/titan/titan_JavaTestPorts_Common_Components_Abstract_Socket/user_provided/Abstract_Socket.java index c33e51a1dcf067b730e1af26df888243e6217c77..5f86e461d80b85f9a471582b7b2b8fb37feb4cf3 100644 --- a/JavaTestPorts/titan.JavaTestPorts.Common_Components.Abstract_Socket/user_provided/org/eclipse/titan/titan_JavaTestPorts_Common_Components_Abstract_Socket/user_provided/Abstract_Socket.java +++ b/JavaTestPorts/titan.JavaTestPorts.Common_Components.Abstract_Socket/user_provided/org/eclipse/titan/titan_JavaTestPorts_Common_Components_Abstract_Socket/user_provided/Abstract_Socket.java @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright (c) 2000-2021 Ericsson Telecom AB + * Copyright (c) 2000-2022 Ericsson Telecom AB * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -7,6 +7,9 @@ ******************************************************************************/ package org.eclipse.titan.titan_JavaTestPorts_Common_Components_Abstract_Socket.user_provided; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; @@ -21,8 +24,20 @@ import java.nio.channels.SelectableChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.channels.UnresolvedAddressException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; import org.eclipse.titan.runtime.core.TTCN_Buffer; import org.eclipse.titan.runtime.core.TTCN_Logger; @@ -44,6 +59,25 @@ public abstract class Abstract_Socket { private static final int NI_MAXSERV = 32; + // SSLContext Algorithms + public static final String TLS13 = "TLSv1.3"; // Recommended to use !!! Supports RFC 8446: TLS version 1.3; may support other SSL/TLS versions. + public static final String TLS12 = "TLSv1.2"; // Supports RFC 5246: TLS version 1.2; may support other SSL/TLS versions. + public static final String SSLv3 = "SSLv3"; // Supports SSL version 3; may support other SSL/TLS versions. + public static final String SSLv2 = "SSLv2"; // Supports SSL version 2 or later; may support other SSL/TLS versions. + + /* + * Default KeyStore directory. + * + * The default is working directory / resources / ssl + */ + private static final String DEFAULT_KEYSTORE_DIRECTORY = System.getProperty("user.dir") + File.pathSeparator + "resources" + File.pathSeparator + "ssl" + File.pathSeparator; + + private static final String DEFAULT_KEYSTORE_PASSWORD = "changeme"; + + private static final String DEFAULT_KEYSTORE_FILE_NAME = "sslKeyStore"; + + private static final String DEFAULT_TRUSTSTORE_FILE_NAME = "sslTrustedKeyStore"; + private boolean halt_on_connection_reset_set; private boolean halt_on_connection_reset; private boolean client_TCP_reconnect; @@ -68,6 +102,34 @@ public abstract class Abstract_Socket { private List peer_list_root; + //SSL related member variables + private SSLContext sslContext; + private SSLEngine sslEngine; + private SSLSession sslSession; + + private KeyStore sslKeyFile; // KeyStore + private KeyStore sslTrustedFile; // TrustStore + + private ByteBuffer myAppData; + private ByteBuffer myNetData; + private ByteBuffer peerAppData; + private ByteBuffer peerNetData; + + private String keyStorePassword; + private String trustStorePassword; + + private boolean ssl_verify_certificate; // Configures the engine to request client authentication. + private boolean ssl_use_ssl; // Whether to use SSL. + private boolean ssl_initialized; // Whether SSL already initialized or not. + private List ssl_cipher_list; // SSL cipher list restriction to apply. + private String ssl_password; // Password to decode the private key. + + // Executor for SSL handshake tasks + // TODO: Check running time with other executors + private ExecutorService singleThreadExecutor; + + //TODO: more member variables needed for testport parameters + /******************************** ** Abstract_Socket ** abstract base type for TCP socket handling @@ -93,6 +155,15 @@ public abstract class Abstract_Socket { use_connection_ASPs = false; handle_half_close = false; peer_list_root = null; + + ssl_use_ssl = false; + ssl_initialized = false; + sslKeyFile = null; + sslTrustedFile = null; + ssl_verify_certificate = false; + ssl_cipher_list = null; + ssl_password = null; + singleThreadExecutor = Executors.newSingleThreadExecutor(); } public Abstract_Socket(String test_port_type, String test_port_name) { @@ -116,6 +187,15 @@ public abstract class Abstract_Socket { use_connection_ASPs = false; handle_half_close = false; peer_list_root = null; + + ssl_use_ssl = false; + ssl_initialized = false; + sslKeyFile = null; + sslTrustedFile = null; + ssl_verify_certificate = false; + ssl_cipher_list = null; + ssl_password = null; + singleThreadExecutor = Executors.newSingleThreadExecutor(); } //////////////////////////////////////////////////////////////////////// @@ -427,7 +507,7 @@ public abstract class Abstract_Socket { } /** - * Called after a peer is connectedd + * Called after a peer is connected * * Do nothing in here. Override in your testport implementation. * @@ -1170,11 +1250,8 @@ public abstract class Abstract_Socket { protected String ssl_verifycertificate_name() { return "ssl_verify_certificate";} protected String ssl_disable_SSLv2() { return "ssl_disable_SSLv2";} protected String ssl_disable_SSLv3() { return "ssl_disable_SSLv3";} - protected String ssl_disable_TLSv1() { return "ssl_disable_TLSv1";} - protected String ssl_disable_TLSv1_1() { return "ssl_disable_TLSv1_1";} protected String ssl_disable_TLSv1_2() { return "ssl_disable_TLSv1_2";} protected String ssl_disable_TLSv1_3() { return "ssl_disable_TLSv1_3";} - protected String ssl_keystore_type() { return "ssl_keystore_type";} protected boolean add_user_data(SelectableChannel id) { return true; @@ -1314,6 +1391,141 @@ public abstract class Abstract_Socket { protected abstract void Remove_Fd_All_Handlers(SelectableChannel fd); protected abstract void Handler_Uninstall(); + private KeyStore initKeyStore(final String filePath, final String password) { + return initKeyStore(filePath, password, KeyStore.getDefaultType()); + } + + //TODO: finish function and return ks variable at the end + private KeyStore initKeyStore(final String filePath, final String password, final String keyStoreType) { + boolean defaultFilePath = false; + boolean defaultPassword = false; + boolean defaultKeyStoreType = false; + + if (filePath == null || filePath.isEmpty()) { + TtcnError.TtcnWarning("Invalid KeyStore file path! Using the default file path."); + defaultFilePath = true; + } + if (password == null || password.isEmpty()) { + TtcnError.TtcnWarning("Invalid KeyStore password! Using the default password."); + defaultPassword = true; + } + if (keyStoreType == null || keyStoreType.isEmpty()) { + TtcnError.TtcnWarning("Invalid KeyStore type! Using the default type."); + defaultKeyStoreType = true; + } + KeyStore ks = null; + try { + ks = defaultKeyStoreType ? KeyStore.getInstance(KeyStore.getDefaultType()) : KeyStore.getInstance(keyStoreType); + } catch (KeyStoreException e) { + throw new TtcnError(e.getMessage()); + } + File keyFile = new File(filePath); + if (!keyFile.exists()) { + try { + ks.load(null, defaultPassword ? DEFAULT_KEYSTORE_PASSWORD.toCharArray() : password.toCharArray()); + } catch (NoSuchAlgorithmException e) { + throw new TtcnError(e.getMessage()); + } catch (CertificateException e) { + throw new TtcnError(e.getMessage()); + } catch (IOException e) { + throw new TtcnError(e.getMessage()); + } + // TODO: finish create key file + } + return null; + } + + private void sslHandshake(final SocketChannel socketChannel) throws IOException { + SSLEngineResult result = null; + SSLEngineResult.HandshakeStatus handshakeStatus = null; + final int applicationBufferSize = sslSession.getApplicationBufferSize(); + + ByteBuffer myAppData = ByteBuffer.allocate(applicationBufferSize); + ByteBuffer peerAppData = ByteBuffer.allocate(applicationBufferSize); + + sslEngine.beginHandshake(); + + handshakeStatus = sslEngine.getHandshakeStatus(); + + while (handshakeStatus != HandshakeStatus.FINISHED && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING) { + switch (handshakeStatus) { + case NEED_UNWRAP: + if (socketChannel.read(peerNetData) < 0) { + // TODO close connection properly + } + peerNetData.flip(); + result = sslEngine.unwrap(peerNetData, peerAppData); + peerNetData.compact(); + handshakeStatus = result.getHandshakeStatus(); + // Check status + switch (result.getStatus()) { + case OK: + // Nothing done + break; + case BUFFER_UNDERFLOW: + if (sslEngine.getSession().getPacketBufferSize() > peerNetData.capacity()) { + peerNetData = ByteBuffer.allocate(sslSession.getPacketBufferSize()); + } else { + peerNetData.clear(); + } + break; + case BUFFER_OVERFLOW: + if (sslEngine.getSession().getApplicationBufferSize() > peerAppData.capacity()) { + peerAppData = ByteBuffer.allocate(sslEngine.getSession().getApplicationBufferSize()); + } else { + peerAppData.clear(); + } + break; + case CLOSED: + if (!sslEngine.isOutboundDone()) { + sslEngine.closeOutbound(); + handshakeStatus = sslEngine.getHandshakeStatus(); + } + break; + default: + throw new IllegalStateException("Invalid SSLEngineResult status"); + } + break; + case NEED_WRAP: + myNetData.clear(); + result = sslEngine.wrap(myAppData, myNetData); + handshakeStatus = result.getHandshakeStatus(); + // Check status + switch (result.getStatus()) { + case OK: + myNetData.flip(); + while (myNetData.hasRemaining()) { + socketChannel.write(myNetData); + } + break; + case BUFFER_OVERFLOW: + // TODO implement + break; + case BUFFER_UNDERFLOW: + // TODO implement + break; + case CLOSED: + // TODO implement + break; + default: + break; + } + break; + case NEED_TASK: + if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { + Runnable runnable; + while ((runnable = sslEngine.getDelegatedTask()) != null) { + singleThreadExecutor.execute(runnable); + } + handshakeStatus = sslEngine.getHandshakeStatus(); + } + break; + default: + break; + } + } + } + public static class as_client_struct { // proper implementation of void* type public Object user_data; diff --git a/JavaTestPorts/titan.JavaTestPorts.Common_Components.Abstract_Socket/user_provided/org/eclipse/titan/titan_JavaTestPorts_Common_Components_Abstract_Socket/user_provided/PacketHeaderDescr.java b/JavaTestPorts/titan.JavaTestPorts.Common_Components.Abstract_Socket/user_provided/org/eclipse/titan/titan_JavaTestPorts_Common_Components_Abstract_Socket/user_provided/PacketHeaderDescr.java index 35f491c8aab6d0d7d07d677980b1fba5e444a6e7..b63fcf232fe76ac7c4ae4675622570e954d88b88 100644 --- a/JavaTestPorts/titan.JavaTestPorts.Common_Components.Abstract_Socket/user_provided/org/eclipse/titan/titan_JavaTestPorts_Common_Components_Abstract_Socket/user_provided/PacketHeaderDescr.java +++ b/JavaTestPorts/titan.JavaTestPorts.Common_Components.Abstract_Socket/user_provided/org/eclipse/titan/titan_JavaTestPorts_Common_Components_Abstract_Socket/user_provided/PacketHeaderDescr.java @@ -1,10 +1,10 @@ /****************************************************************************** -* Copyright (c) 2000-2021 Ericsson Telecom AB -* All rights reserved. This program and the accompanying materials -* are made available under the terms of the Eclipse Public License v2.0 -* which accompanies this distribution, and is available at -* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html -******************************************************************************/ + * Copyright (c) 2000-2022 Ericsson Telecom AB + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html + ******************************************************************************/ package org.eclipse.titan.titan_JavaTestPorts_Common_Components_Abstract_Socket.user_provided; /********************************