com.DSC.client.SecureChannel.java Source code

Java tutorial

Introduction

Here is the source code for com.DSC.client.SecureChannel.java

Source

/**
 * Distributed Secure Channel
 * A novel distributed cryptosystem based on the concepts of PGP and Bitcoin.
 *
 * Copyright (C) 2013, Jonathan Gillett, Joseph Heron, and Daniel Smullen
 * All rights reserved.
 *
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.DSC.client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.security.SecureRandom;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.net.ntp.NTPUDPClient;
import org.apache.commons.net.ntp.TimeInfo;
import org.bouncycastle.crypto.engines.ISAACEngine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.jgroups.Address;
import org.jgroups.JChannel;
import org.joda.time.DateTimeUtils;
import org.joda.time.format.DateTimeFormat;

import com.DSC.chat.CommandParser;
import com.DSC.chat.Create;
import com.DSC.chat.Join;
import com.DSC.chat.Nick;
import com.DSC.chat.Quit;
import com.DSC.chat.Request;
import com.DSC.controller.ReceiveController;
import com.DSC.controller.SendController;
import com.DSC.crypto.ECKey;
import com.DSC.crypto.ISAACRandomGenerator;
import com.DSC.message.MessageType;
import com.DSC.utility.Colour;
import com.DSC.utility.ProgramState;
import com.google.common.collect.ConcurrentHashMultiset;

public class SecureChannel {
    private static final String NTP_SERVER = "time-a.nist.gov";
    private static ReceiveController receiveController;
    private static SendController sendController;

    /**
     * Joins the JGroups channel specified
     * @throws Exception
     */
    private static void join(String name) throws Exception {
        ProgramState.channel = new JChannel();
        ProgramState.channel.setDiscardOwnMessages(true);
        ProgramState.channel.setReceiver(receiveController);
        ProgramState.channel.connect(name);
    }

    /**
     * Clears the terminal, used on initial load and after connecting to JGroups
     */
    private static void clearScreen() {
        System.out.print("\u001b[2J");
        System.out.flush();
        System.out.print(String.format("%c[%d;%df", 0x1B, 0, 0));
    }

    /**
     * Gets the date time offset for the client using a NTP server
     * @throws IOException 
     */
    private static long getTimeOffset() throws IOException {
        NTPUDPClient timeClient = new NTPUDPClient();
        InetAddress inetAddress = InetAddress.getByName(NTP_SERVER);
        TimeInfo timeInfo = timeClient.getTime(inetAddress);
        return DateTimeUtils.currentTimeMillis() - timeInfo.getReturnTime();
    }

    /**
     * The main eventLoop that handles the user input for the IRC client
     * @throws InterruptedException 
     */
    private static void eventLoop() throws InterruptedException {
        clearScreen();
        ProgramState.in = new BufferedReader(new InputStreamReader(System.in));

        while (true) {
            try {
                System.out.flush();

                /* Don't display the username prompt when authenticating */
                if (!ProgramState.AUTHENTICATION_DECISION) {
                    System.out.print(ProgramState.nick + "> ");
                }

                String line = ProgramState.in.readLine();

                if (CommandParser.isCommand(line)) {
                    Nick nick;
                    Quit quit;
                    Create create;
                    Join join;
                    Request request;

                    if ((nick = Nick.parse(line)) != null) {
                        System.out.print("> Enter a nickname: ");
                        String nickname = ProgramState.in.readLine();
                        nick.setNickname(nickname);

                        nick.executeCommand();
                        System.out
                                .println(Colour.YELLOW + "> Nickname changed to " + nick.getNick() + Colour.RESET);
                    } else if ((quit = Quit.parse(line)) != null) {
                        System.out.println(Colour.YELLOW + "Goodbye." + Colour.RESET);
                        quit.executeCommand();
                        break;
                    } else if ((create = Create.parse(line)) != null) {
                        System.out.print("> Enter the channel name: ");
                        String channelName = ProgramState.in.readLine();
                        System.out.print("> Enter the channel passphrase: ");
                        String passphrase = ProgramState.in.readLine();
                        create.setChannel(channelName);
                        create.setPassphrase(passphrase);

                        if (create.executeCommand()) {
                            if (ProgramState.channel == null || !ProgramState.channel.equals(channelName)) {
                                join(channelName);
                                clearScreen();

                                System.out.println(Colour.GREEN + "> Channel " + channelName
                                        + " created successfully." + Colour.RESET);
                            }
                        } else {
                            System.out.println(Colour.RED + "> Channel " + channelName + " has failed to create"
                                    + Colour.RESET);
                        }

                    } else if ((join = Join.parse(line)) != null) {
                        System.out.print("> Enter the channel to join: ");
                        String channelName = ProgramState.in.readLine();
                        join.setChannel(channelName);

                        if (join.executeCommand()) {
                            if (ProgramState.channel == null || !ProgramState.channel.equals(channelName)) {
                                join(channelName);
                                clearScreen();

                                System.out.println(Colour.YELLOW + "> Joined channel: " + channelName
                                        + ", but not yet authenticated!" + Colour.RESET);
                            }
                        } else {
                            System.out.println(Colour.RED + "> Invalid channel name" + Colour.RESET);
                        }
                    } else if ((request = Request.parse(line)) != null) {
                        /* Check that they have first joined a channel */
                        if (ProgramState.channel == null) {
                            System.out
                                    .println(Colour.RED + "> Error, you must join a channel first." + Colour.RESET);
                            continue;
                        }

                        System.out.print("> Enter authentication: ");
                        String passphrase = ProgramState.in.readLine();
                        request.setPassphrase(passphrase);

                        if (request.executeCommand()) {
                            System.out.println("> Signing key...");
                            System.out.println("> Requesting access...");

                            /* Send out the request to join */
                            sendController.send(MessageType.AUTH_REQUEST, null, null);

                            /* Wait maximum of 20 seconds to see if authenticated */
                            for (int i = 0; (!ProgramState.AUTHENTICATED && i < 20); ++i) {
                                System.out.print(".");
                                Thread.sleep(1000);
                            }

                            /* Send key exchange request if authenticated */
                            if (ProgramState.AUTHENTICATED) {
                                System.out.println("\n> Requesting network key...");
                                sendController.send(MessageType.KEY_EXCHANGE, null, null);

                                /* Wait a maximum of 10 seconds to see if key received */
                                for (int i = 0; (!ProgramState.KEY_RECEIVED && i < 10); ++i) {
                                    System.out.print(".");
                                    Thread.sleep(1000);
                                }
                                if (ProgramState.KEY_RECEIVED) {
                                    System.out.println(
                                            Colour.GREEN + "\n> Successfully joined channel." + Colour.RESET);
                                } else {
                                    System.out.println(Colour.RED + "\n> No key received, failed to join channel."
                                            + Colour.RESET);
                                }
                            } else {
                                System.out.println(Colour.RED + "\n> Failed to be authenticated." + Colour.RESET);
                            }
                        } else {
                            System.out.println(Colour.RED + "> An unspecified error occurred." + Colour.RESET);
                        }
                    }
                } else {
                    /* Authentication prompt */
                    if (ProgramState.AUTHENTICATION_DECISION) {
                        ProgramState.symbol.setInputReady(line);
                    } else if (ProgramState.AUTHENTICATED) {
                        String message = Colour.BLUE + ProgramState.fmt.print(DateTimeUtils.currentTimeMillis())
                                + Colour.RESET + " " + ProgramState.nick + "> " + line;

                        /* Send the encrypted message to each trusted contact */
                        for (Address dest : ProgramState.trustedKeys.values()) {
                            sendController.send(MessageType.ENCRYPTED_MESSAGE, message, dest);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            Thread.sleep(100);
        }
    }

    /**
     * @param args
     * @throws InterruptedException 
     * @throws IOException 
     */
    public static void main(String[] args) throws InterruptedException, IOException {
        /* Create their private & public keys */
        ECKey key = new ECKey();
        key.init();
        ProgramState.publicKey = (ECPublicKeyParameters) key.getPublic();
        ProgramState.privateKey = (ECPrivateKeyParameters) key.getPrivate();

        /* Create the IV engine */
        byte[] seed = new byte[64]; // 512 bit seed 
        SecureRandom random = new SecureRandom();
        random.nextBytes(seed);
        ProgramState.IVEngine = new ISAACRandomGenerator(new ISAACEngine());
        ProgramState.IVEngine.init(seed);

        /* Create the blacklist and trusted contacts */
        ProgramState.blacklist = ConcurrentHashMultiset.create();
        ProgramState.trustedKeys = new ConcurrentHashMap<String, Address>();

        /* Set the time for the client accurately using a NTP server */
        DateTimeUtils.setCurrentMillisOffset(getTimeOffset());
        ProgramState.fmt = DateTimeFormat.forPattern("HH:mm:ss");

        /* Set the default nick as anonymous */
        ProgramState.nick = "anonymous";

        /* Initialize ISAACRandomGenerator, set ProgramState.IVEngine */
        receiveController = new ReceiveController();
        sendController = new SendController();

        /* Start input event handler loop */
        eventLoop();
    }
}