edu.biu.scapi.comm.CommunicationSetup.java Source code

Java tutorial

Introduction

Here is the source code for edu.biu.scapi.comm.CommunicationSetup.java

Source

/**
* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
* 
* Copyright (c) 2012 - SCAPI (http://crypto.biu.ac.il/scapi)
* This file is part of the SCAPI project.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* 
* We request that any publication and/or code referring to and/or based on SCAPI contain an appropriate citation to SCAPI, including a reference to
* http://crypto.biu.ac.il/SCAPI.
* 
* SCAPI uses Crypto++, Miracl, NTL and Bouncy Castle. Please see these projects for any further licensing issues.
* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
* 
*/

package edu.biu.scapi.comm;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Level;

import org.apache.commons.exec.TimeoutObserver;
import org.apache.commons.exec.Watchdog;

import edu.biu.scapi.exceptions.DuplicatePartyException;
import edu.biu.scapi.generals.Logging;

/** 
 * The CommunicationSetup class is the heart of the Communications Layer. The Communications Layer package is a tool used by a client that is interested in setting up connections 
 * between itself and other parties. As such, this layer does not initiate any independent tasks, but the opposite. Given a list of parties, it attempts to set connections to them 
 * according to parameters given by the calling application. If succeeds, it returns these connections so that the calling client can send and receive data over them.<p>
 * An application written for running an MPC protocol can be the client of the Communications Layer. An example of a possible usage follows:<p>
 * <ul>
 * <li>Instantiate an object of type CommunicationSetup.</li>
 * <li>Call the prepareForCommunication method of that object with a list of parties to connect to and other setup parameters. (prepareForCommunication is the only public method of this class).</li>
 * <li>Get from prepareForCommunication a container holding all ready connections.</li>
 * <li>Start the MPC protocol.</li> 
 * <li>Call the send and receive methods of the ready connections as needed by the MPC.</li>
 * </ul>
 * The application may be interested in putting each connection in a different thread but it is up to the application to do so and not the responsibility of the Communications Layer. This provides more flexibility of use.
 * The Communications Layer encapsulates the stage of connecting to other parties. In actuality, the connection to other parties is performed in a few steps, which are not visible to the outside user.
 * These steps are:<p>
 * <ul> 
 * <li>create an actual TCP connection with each party</li>
 * <li>exchange keys between my party and other parties</li>
 * <li>run a protocol that checks (for different types of required success) if all the necessary connections were set between my party and other parties.</li>
 * </ul>
 * In the end, the Communications Layer via the CommunicationSetup class returns to the calling application, a set of connected and ready channels to be used throughout a cryptographic protocol.<br/>
 * From this point onwards, the application can send and receive messages in each connection as required by the protocol.<p>
 * CommunicationSetup implements the org.apache.commons.exec.TimeoutObserver interface. 
 * This interface supplies a mechanism for notifying classes that a timeout has arrived. 
 * 
 * This class deprecated since there are new communication implementations. A communication between two parties can be 
 * found at edu.biu.scapi.twoPartyComm package and communication between multiple parties can be found at 
 * edu.biu.scapi.multiPartyComm package. 
 * 
 * @author Cryptography and Computer Security Research Group Department of Computer Science Bar-Ilan University
 */
@Deprecated
public class CommunicationSetup implements TimeoutObserver {
    private boolean bTimedOut = false;
    private boolean enableNagle = false;
    private List<Party> partiesList;
    private EstablishedConnections establishedConnections;
    private KeyExchangeProtocol keyExchangeProtocol;
    private ConnectivitySuccessVerifier connectivitySuccessVerifier;
    private ListeningThread listeningThread;
    private Vector<SecuringConnectionThread> threadsVector;
    private Map<InetSocketAddress, KeyExchangeOutput> keyExchangeMap;
    private Watchdog watchdog;

    /**
     * Create a CommunicationSetup instance which is the heart of the Communication Layer. 
     */
    public CommunicationSetup() {

    }

    /**  
     * This function is package private and is called by the public prepareFunctions. It requests a KeyExchangeProtocol that has not been implemented yet.
     * It initiates the creation of the final actual socket connections between the parties. If this function succeeds, the 
     * application may use the send and receive functions of the created channels to pass messages.
     * 
     * @param listOfParties the original list of parties to connect to. As a convention, we will set the <B>first party</B> in the list to be the <B>requesting party</B>, that is, 
     *                    the party represented by the application.
     * @param keyExchange the key exchange algorithm protocol to use after a channel is connected
     * @param successLevel the ConnectivitySuccessVerifier algorithm to use
     * @param timeOut the maximum amount of time we allow for the connection stage
     * @return a set of connected and ready channels to be used by the parties to send and receive data, it may be null if none succeeded
     */
    Map<InetSocketAddress, Channel> prepareForCommunication(List<Party> listOfParties,
            KeyExchangeProtocol keyExchange, ConnectivitySuccessVerifier successLevel, long timeOut) {
        //set parameters
        partiesList = listOfParties;
        keyExchangeProtocol = keyExchange;
        connectivitySuccessVerifier = successLevel;

        establishedConnections = new EstablishedConnections();

        //initialize the threadVector and the map of the key exchange outputs
        threadsVector = new Vector<SecuringConnectionThread>();
        keyExchangeMap = new HashMap<InetSocketAddress, KeyExchangeOutput>();

        //start the watch dog with timeout
        watchdog = new Watchdog(timeOut);
        //add this instance as the observer in order to receive the event of time out.
        watchdog.addTimeoutObserver(this);

        watchdog.start();

        //establish connections.
        try {
            establishAndSecureConnections();
        } catch (DuplicatePartyException e) {

            Logging.getLogger().log(Level.SEVERE, e.toString());
        }

        //verify connection
        verifyConnectingStatus();

        //run success function
        if (!runSuccessAlgo()) {
            //remove connections from the list of established connections
            //Eventually, when different ConnectivitySuccessVerifiers are implemented there might be an impact what happens to the connections that did succeed.
            //For example, assume that only 80% of the connections requested were established but the ConnectivitySuccessVerifier specified requires 100%. Then, obviously
            //the function didn't succeed and null should be returned. However, it would be a pity to let go (delete and remove) the 80% of connections that did succeed.
            //It may be possible (and this will depend on the requirements of the ConnectivitySuccessVerifier) to keep the already established connections and only try 
            //to establish what is missing. In some other cases it will be necessary to start everything over form the beginning. As a conclusion, this is left for future implementation.
            return null;
        }

        //remove all connections with not READY state
        establishedConnections.removeNotReadyConnections();

        //set nagle algorithm
        establishedConnections.enableNagle(enableNagle);

        //update the security level for each connection
        //setSecurityLevel();

        //return the map of channels held in the established connection object.
        return establishedConnections.getConnections();

    }

    /**
     * This function is package private and is called by the public prepareFunctions. It requests a KeyExchangeProtocol that has not been implemented yet.
     * Does the same as the above prepareForCommunication function only sets the flag of enableNagle first.
     * 
     * @param enableNagle a flag indicating weather or not to use the Nagle optimization algorithm 
     * @return a set of connected and ready channels to be used by the parties to send and receive data, it may be null if none succeeded
     */
    Map<InetSocketAddress, Channel> prepareForCommunication(List<Party> listOfParties,
            KeyExchangeProtocol keyExchange, ConnectivitySuccessVerifier successLevel, long timeOut,
            boolean enableNagle) {

        this.enableNagle = enableNagle;

        return prepareForCommunication(listOfParties, keyExchange, successLevel, timeOut);
    }

    /** 
     * An application that wants to use the communication layer will call this function in order to prepare for communication after providing the required parameters. 
     * This function initiates the creation of the final actual socket connections between the parties. If this function succeeds, the 
     * application may use the send and receive functions of the created channels to pass messages.<p> 
     * Note that using this function you can choose to use or not to use the Nagle algorithm.
     * 
     * @param listOfParties the original list of parties to connect to. As a convention, we will set the <B>first party</B> in the list to be the <B>requesting party</B>, that is, 
     *                    the party represented by the application.
     * @param successLevel the ConnectivitySuccessVerifier algorithm to use
     * @param timeOut the maximum amount of time we allow for the connection stage
     * @param enableNagle a flag indicating weather or not to use the Nagle optimization algorithm. For Cryptographic algorithms is better to have it disabled
     * @return a set of connected and ready channels to be used by the parties to send and receive data, it may be null if none succeeded
     */
    public Map<InetSocketAddress, Channel> prepareForCommunication(List<Party> listOfParties,
            ConnectivitySuccessVerifier successLevel, long timeOut, boolean enableNagle) {

        KeyExchangeProtocol keyExchange = new KeyExchangeProtocol();
        return prepareForCommunication(listOfParties, keyExchange, successLevel, timeOut, enableNagle);
    }

    /**
     * An application that wants to use the communication layer will call this function in order to prepare for communication after providing the required parameters. 
     * This function initiates the creation of the final actual socket connections between the parties. If this function succeeds, the 
     * application may use the send and receive functions of the created channels to pass messages.<p> 
     * In this function, Nagles algorithm is disabled; for cryptographic protocols this is typically much better.
     *  
     * @param listOfParties the original list of parties to connect to. As a convention, we will set the <B>first party</B> in the list to be the <B>requesting party</B>, that is, 
     *                    the party represented by the application.
     * @param successLevel the ConnectivitySuccessVerifier algorithm to use
     * @param timeOut the maximum amount of time we allow for the connection stage
     * @return a set of connected and ready channels to be used by the parties to send and receive data, it may be null if none succeeded
     */
    public Map<InetSocketAddress, Channel> prepareForCommunication(List<Party> listOfParties,
            ConnectivitySuccessVerifier successLevel, long timeOut) {
        KeyExchangeProtocol keyExchange = new KeyExchangeProtocol();
        return prepareForCommunication(listOfParties, keyExchange, successLevel, timeOut);
    }

    /**
     * 
     * Using the SecuringConnectionThread and the ListeningThread we connect the parties via sockets.
     * We either connect by initiating a connection or by listening to incoming connection requests.
     * @throws DuplicatePartyException This exception is for the case where there are two parties in the list of parties with the same ip+port
     */
    private void establishAndSecureConnections() throws DuplicatePartyException {

        //Create an iterator to go over the list of parties 
        Iterator<Party> itr = partiesList.iterator();
        Party firstParty = null;
        Party party;
        int numOfIncomingConnections = 0;

        //temp map
        Map<InetAddress, Vector<SecuringConnectionThread>> listeningThreadMap = new HashMap<InetAddress, Vector<SecuringConnectionThread>>();

        //the first party is me. Other parties identity will be compared with this party
        if (itr.hasNext()) {

            firstParty = itr.next();
        }

        //go over the elements of the list of parties
        while (itr.hasNext()) {

            //get the next party in the list.
            party = itr.next();

            //create an InetSocketAddress
            InetSocketAddress inetSocketAdd = new InetSocketAddress(party.getIpAddress(), party.getPort());
            //create a channel for this party
            PlainChannel channel = new PlainTCPChannel(inetSocketAdd);
            //set to NOT_INIT state
            channel.setState(PlainChannel.State.NOT_INIT);
            //add to the established connection object
            establishedConnections.addConnection(inetSocketAdd, channel);

            //create a key exchange output to pass to the SecuringConnectionThread
            KeyExchangeOutput keyExchangeOutput = new KeyExchangeOutput();

            //add the key exchange output to the map
            keyExchangeMap.put(inetSocketAdd, keyExchangeOutput);

            int partyCompare = firstParty.compareTo(party);
            if (partyCompare == 0) {//should not happen since it means that there is another party in the list with the same ip+port
                throw new DuplicatePartyException("Another party with the same ip address and port");
            }
            //UPWARD connection
            else if (firstParty.compareTo(party) > 0) {

                upwardConnection(party, channel, keyExchangeOutput);

            } else { //DOWN connection

                numOfIncomingConnections = downConnection(party, numOfIncomingConnections, listeningThreadMap,
                        channel, keyExchangeOutput);

            }
        }

        if (listeningThreadMap.size() > 0) {//there are down connections need to listen to connections using the listeningThread
            //send information to the listening thread
            listeningThread = new ListeningThread(listeningThreadMap, firstParty, numOfIncomingConnections);
            listeningThread.start();
        }

    }

    /**
     * Connect to a party with ID less than mine.
     * @param party
     * @param numOfIncomingConnections
     * @param listeningThreadMap
     * @param channel
     * @param keyExchangeOutput
     * @return
     */
    private int downConnection(Party party, int numOfIncomingConnections,
            Map<InetAddress, Vector<SecuringConnectionThread>> listeningThreadMap, PlainChannel channel,
            KeyExchangeOutput keyExchangeOutput) {
        boolean doConnect;
        //increase the index of incoming connections
        numOfIncomingConnections++;

        //set doConnect to false. We do not want the thread to try to connect.
        doConnect = false;

        //create a new SecuringConnectionThread 
        SecuringConnectionThread scThread = new SecuringConnectionThread(channel, party.getIpAddress(),
                party.getPort(), doConnect, keyExchangeProtocol, keyExchangeOutput);

        //add to the thread vector
        threadsVector.add(scThread);

        //a vector holding the securing threads
        Vector<SecuringConnectionThread> vector;
        if (listeningThreadMap.containsKey(party.getIpAddress())) {
            //ip already exists insert to the vector
            vector = listeningThreadMap.get(party.getIpAddress());

            //add the thread to the existing vector
            vector.add(scThread);
        } else {//there is no such an ip. create a new vector 

            vector = new Vector<SecuringConnectionThread>();
            vector.add(scThread);

            //add thread to the local vector so the listening thread can start the securing thread.
            listeningThreadMap.put(party.getIpAddress(), vector);

        }
        return numOfIncomingConnections;
    }

    /**
     * Connect to a party with ID more than mine.
     * @param party
     * @param channel
     * @param keyExchangeOutput
     */
    private void upwardConnection(Party party, PlainChannel channel, KeyExchangeOutput keyExchangeOutput) {
        boolean doConnect;
        //set doConnect to true. We need the thread to connect to the other side of the channel. 
        doConnect = true;

        //create a new SecuringConnectionThread 
        SecuringConnectionThread scThread = new SecuringConnectionThread(channel, party.getIpAddress(),
                party.getPort(), doConnect, keyExchangeProtocol, keyExchangeOutput);

        //add to the thread vector
        threadsVector.add(scThread);

        //start the thread
        scThread.start();
    }

    /** 
     * This function serves as a barrier. It is called from the prepareForCommunication function. The idea
     * is to let all the threads finish running before proceeding. 
     */
    private void verifyConnectingStatus() {
        boolean allConnected = false;
        //while the thread has not been stopped and not all the channels are connected
        while (!bTimedOut && !(allConnected = establishedConnections.areAllConnected())) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {

                Logging.getLogger().log(Level.FINEST, e.toString());
            }
        }
        //If we already know that all the connectios were established we can stop the watchdog.
        if (allConnected)
            watchdog.stop();
    }

    /** 
     * 
     * Runs the success algorithm. 
     * @return true if the level of connectivity specified by the {@link ConnectivitySuccessVerifier} was reached 
     *          false otherwise
     */
    private boolean runSuccessAlgo() {

        //call the relevant success algorithm
        return connectivitySuccessVerifier.hasSucceded(establishedConnections, partiesList);
    }

    /**
     * This function is called by the infrastructure of the Watchdog if the previously set timeout has passed. (Do not call this function).
     */
    public void timeoutOccured(Watchdog w) {

        Logging.getLogger().log(Level.INFO, "Timeout occured");

        //timeout has passed set the flag
        bTimedOut = true;

        //stop all threads in the vector and the listening thread
        for (int i = 0; i < threadsVector.size(); i++) {

            //get a thread from the vector of threads
            SecuringConnectionThread thread = threadsVector.elementAt(i);

            //sets the flag of the thread to stopped. This will make the run function of the thread to terminate if it has not finished yet.
            thread.stopConnecting();
            thread.interrupt();

        }

        //further stop the listening thread if it still runs. Similarly, it sets the flag of the listening thread to stopped.
        if (listeningThread != null) {
            listeningThread.stopConnecting();
            listeningThread.interrupt();
        }
    }

}