Allow 'startsecure' to be issued at any time

This commit is contained in:
Tobias Eidelpes 2020-12-29 19:02:36 +01:00
parent a9eab7cfb9
commit 93a2a87f07
2 changed files with 91 additions and 20 deletions

View File

@ -12,6 +12,8 @@ import java.util.Arrays;
import java.util.Base64; import java.util.Base64;
import java.util.logging.Logger; import java.util.logging.Logger;
import at.ac.tuwien.dsg.orvell.Shell;
import at.ac.tuwien.dsg.orvell.annotation.Command;
import dslab.ComponentFactory; import dslab.ComponentFactory;
import dslab.exception.FailedVerificationException; import dslab.exception.FailedVerificationException;
import dslab.util.Config; import dslab.util.Config;
@ -22,6 +24,8 @@ import javax.crypto.spec.SecretKeySpec;
public class MessageClient implements IMessageClient, Runnable { public class MessageClient implements IMessageClient, Runnable {
private static final Logger logger = Logger.getLogger(MessageClient.class.getName()); private static final Logger logger = Logger.getLogger(MessageClient.class.getName());
private final String componentId;
private final InputStream consoleIn;
private final String transferHost; private final String transferHost;
private final int transferPort; private final int transferPort;
@ -38,9 +42,11 @@ public class MessageClient implements IMessageClient, Runnable {
private PrintWriter dmtpOut; private PrintWriter dmtpOut;
private BufferedReader dmtpIn; private BufferedReader dmtpIn;
private PrintWriter dmapOut; private PrintStream dmapOut;
private BufferedReader dmapIn; private BufferedReader dmapIn;
private final Shell shell;
private Cipher aesEncryptCipher; private Cipher aesEncryptCipher;
private Cipher aesDecryptCipher; private Cipher aesDecryptCipher;
private byte[] challenge; private byte[] challenge;
@ -62,6 +68,13 @@ public class MessageClient implements IMessageClient, Runnable {
this.mailboxUser = config.getString("mailbox.user"); this.mailboxUser = config.getString("mailbox.user");
this.mailboxPassword = config.getString("mailbox.password"); this.mailboxPassword = config.getString("mailbox.password");
this.componentId = componentId;
this.consoleIn = in;
this.shell = new Shell(in, out);
this.shell.register(this);
this.shell.setPrompt(this.componentId + "> ");
logger.info(String.format("TransferHost: %s\nTransferPort: %d\nMailboxHost: %s\nMailboxPort: %d\nTransferEmail: %s\nMailboxUser: %s\nMailboxPassword: %s", logger.info(String.format("TransferHost: %s\nTransferPort: %d\nMailboxHost: %s\nMailboxPort: %d\nTransferEmail: %s\nMailboxUser: %s\nMailboxPassword: %s",
transferHost, transferPort, mailboxHost, mailboxPort, transferEmail, mailboxUser, mailboxPassword)); transferHost, transferPort, mailboxHost, mailboxPort, transferEmail, mailboxUser, mailboxPassword));
} }
@ -72,7 +85,7 @@ public class MessageClient implements IMessageClient, Runnable {
logger.info("Starting connection to MailboxHost on " + this.mailboxHost + " on port " + this.mailboxPort); logger.info("Starting connection to MailboxHost on " + this.mailboxHost + " on port " + this.mailboxPort);
this.dmapSocket = new Socket(this.mailboxHost, this.mailboxPort); this.dmapSocket = new Socket(this.mailboxHost, this.mailboxPort);
this.dmapIn = new BufferedReader(new InputStreamReader(this.dmapSocket.getInputStream())); this.dmapIn = new BufferedReader(new InputStreamReader(this.dmapSocket.getInputStream()));
this.dmapOut = new PrintWriter(this.dmapSocket.getOutputStream(), true); this.dmapOut = new PrintStream(this.dmapSocket.getOutputStream(), true);
String input = null; String input = null;
String message = null; String message = null;
@ -84,10 +97,8 @@ public class MessageClient implements IMessageClient, Runnable {
startSecure(); startSecure();
loginUser(); loginUser();
shell.run();
while (!Thread.currentThread().isInterrupted() && ((input = this.dmapIn.readLine()) != null)) {
// TODO implement commands
}
} catch (IOException e) { } catch (IOException e) {
logger.severe("Could not connect to MailboxHost " + this.mailboxHost + " on port " + this.mailboxPort); logger.severe("Could not connect to MailboxHost " + this.mailboxHost + " on port " + this.mailboxPort);
shutdown(); shutdown();
@ -103,7 +114,7 @@ public class MessageClient implements IMessageClient, Runnable {
message = "login " + this.mailboxUser + " " + this.mailboxPassword; message = "login " + this.mailboxUser + " " + this.mailboxPassword;
this.dmapOut.println(getAesCiphertext(message)); this.dmapOut.println(getAesCiphertext(message));
String response = getAesPlaintext(this.dmapIn.readLine()); String response = getAesPlaintext(this.dmapIn.readLine());
if (!response.startsWith("ok DMAP2.0")) if (!response.startsWith("ok"))
shutdown(); shutdown();
} }
@ -128,7 +139,6 @@ public class MessageClient implements IMessageClient, Runnable {
KeyFactory kf = KeyFactory.getInstance("RSA"); KeyFactory kf = KeyFactory.getInstance("RSA");
// Make generator generate public key from X509 spec // Make generator generate public key from X509 spec
serverPublicKey = kf.generatePublic(spec); serverPublicKey = kf.generatePublic(spec);
logger.info("Server's Public Key: " + serverPublicKey);
String clientChallenge = generateChallengeMessage(serverPublicKey); String clientChallenge = generateChallengeMessage(serverPublicKey);
// Send clientChallenge to server // Send clientChallenge to server
logger.info("Send clientchallenge to Server: " + clientChallenge); logger.info("Send clientchallenge to Server: " + clientChallenge);
@ -274,21 +284,25 @@ public class MessageClient implements IMessageClient, Runnable {
return (Base64.getEncoder().encodeToString(cipherTextChallenge)); return (Base64.getEncoder().encodeToString(cipherTextChallenge));
} }
@Command
@Override @Override
public void inbox() { public void inbox() {
} }
@Command
@Override @Override
public void delete(String id) { public void delete(String id) {
} }
@Command
@Override @Override
public void verify(String id) { public void verify(String id) {
} }
@Command
@Override @Override
public void msg(String to, String subject, String data) { public void msg(String to, String subject, String data) {
try { try {
@ -302,6 +316,7 @@ public class MessageClient implements IMessageClient, Runnable {
} }
} }
@Command
@Override @Override
public void shutdown() { public void shutdown() {
if (dmtpSocket != null) { if (dmtpSocket != null) {

View File

@ -13,14 +13,12 @@ import javax.crypto.spec.SecretKeySpec;
import java.io.*; import java.io.*;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.security.*; import java.security.*;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays; import java.util.Arrays;
import java.util.Base64; import java.util.Base64;
import java.util.LinkedList; import java.util.LinkedList;
@ -41,6 +39,8 @@ public class DMAPConnection implements Runnable {
private Cipher aesEncryptCipher; private Cipher aesEncryptCipher;
private Cipher aesDecryptCipher; private Cipher aesDecryptCipher;
private boolean secure = false;
public DMAPConnection(Socket connection, ConcurrentHashMap<Email, LinkedList<Message>> storage, ConcurrentHashMap<String, String> userStorage, String componentId) { public DMAPConnection(Socket connection, ConcurrentHashMap<Email, LinkedList<Message>> storage, ConcurrentHashMap<String, String> userStorage, String componentId) {
this.componentId = componentId; this.componentId = componentId;
this.socket = connection; this.socket = connection;
@ -56,20 +56,21 @@ public class DMAPConnection implements Runnable {
this.out = new PrintWriter(socket.getOutputStream(), true); this.out = new PrintWriter(socket.getOutputStream(), true);
this.in = new BufferedReader(new InputStreamReader(socket.getInputStream())); this.in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out.println("ok DMAP2.0"); out.println("ok DMAP2.0");
startSecure();
login(); loginLoop();
String userInput; String userInput;
while (!Thread.currentThread().isInterrupted() && (userInput = in.readLine()) != null) { while (!Thread.currentThread().isInterrupted() && (userInput = in.readLine()) != null) {
if ("quit".equals(userInput)) { if ("quit".equals(userInput)) {
out.println("ok bye"); out.println("ok bye");
shutdown(); loginLoop();
} else if ("logout".equals(userInput)) { } else if ("logout".equals(userInput)) {
out.println("ok"); out.println("ok");
shutdown(); currentUser = null;
loginLoop();
} else if ("list".equals(userInput)) { } else if ("list".equals(userInput)) {
listMessages(); listMessages();
} else if ("delete".equals(userInput.split("\\s+")[0])) { } else if (userInput.startsWith("delete")) {
if (userInput.split("\\s+").length == 2) { if (userInput.split("\\s+").length == 2) {
try { try {
deleteMessage(userInput.split("\\s+")[1]); deleteMessage(userInput.split("\\s+")[1]);
@ -79,7 +80,7 @@ public class DMAPConnection implements Runnable {
} else { } else {
out.println("Please supply a message id to delete!"); out.println("Please supply a message id to delete!");
} }
} else if ("show".equals(userInput.split("\\s+")[0])) { } else if (userInput.startsWith("show")) {
if (userInput.split("\\s+").length == 2) { if (userInput.split("\\s+").length == 2) {
try { try {
showMessage(userInput.split("\\s+")[1]); showMessage(userInput.split("\\s+")[1]);
@ -89,6 +90,8 @@ public class DMAPConnection implements Runnable {
} else { } else {
out.println("Please supply a message id to show!"); out.println("Please supply a message id to show!");
} }
} else if (userInput.startsWith("startsecure")) {
startSecure();
} else { } else {
out.println("error protocol error"); out.println("error protocol error");
shutdown(); shutdown();
@ -104,6 +107,57 @@ public class DMAPConnection implements Runnable {
} }
} }
private void loginLoop() {
String userInput;
try {
while (!Thread.currentThread().isInterrupted()) {
userInput = in.readLine();
if (userInput.startsWith("quit")) {
out.println("ok bye");
shutdown();
} else if (userInput.startsWith("login")) {
String[] args = userInput.split("\\s+");
if (args.length != 3)
out.println("Please specify a username and password to login!");
if (this.userStorage.containsKey(args[1])) {
// Check if username exists
if (args[2].equals(this.userStorage.get(args[1]))) {
// Check if password matches
for (Email email : this.storage.keySet()) {
if (args[1].equals(email.getUsername())) {
// Set current user if login successful
currentUser = email;
logger.info("User successfully logged in: " + currentUser.toString());
out.println("ok");
return;
}
}
} else {
out.println("error wrong password");
}
} else {
out.println("error unknown user");
}
} else if (userInput.startsWith("startsecure")) {
startSecure();
} else {
out.println("error not logged in");
}
}
} catch (InterruptedIOException ioe) {
logger.info("Received interrupt from parent. Shutting down...");
shutdown();
} catch (SocketException e) {
logger.finer("Received interrupt. Exiting " + this.toString());
shutdown();
} catch (IOException e) {
logger.severe("Failed to get IO-Stream");
e.printStackTrace();
shutdown();
}
}
/** /**
* Handles the startsecure command issued by the MessageClient. * Handles the startsecure command issued by the MessageClient.
*/ */
@ -111,7 +165,6 @@ public class DMAPConnection implements Runnable {
String userInput; String userInput;
try { try {
userInput = in.readLine(); // Should always be "startsecure"
// Send componentId // Send componentId
out.println("ok " + componentId); out.println("ok " + componentId);
// Receive client challenge, secret key and iv // Receive client challenge, secret key and iv
@ -129,6 +182,7 @@ public class DMAPConnection implements Runnable {
logger.severe("Received " + userInput + "from MessageClient. Did not match 'ok'"); logger.severe("Received " + userInput + "from MessageClient. Did not match 'ok'");
shutdown(); shutdown();
} }
this.secure = true; // Encrypted communication from now on
} catch (InterruptedIOException ioe) { } catch (InterruptedIOException ioe) {
logger.info("Received interrupt from parent. Shutting down..."); logger.info("Received interrupt from parent. Shutting down...");
shutdown(); shutdown();
@ -230,11 +284,11 @@ public class DMAPConnection implements Runnable {
try { try {
while (!Thread.currentThread().isInterrupted()) { while (!Thread.currentThread().isInterrupted()) {
userInput = getAesPlaintext(in.readLine()); userInput = in.readLine();
if ("quit".equals(userInput.split("\\s+")[0])) { if (userInput.startsWith("quit")) {
out.println(getAesCiphertext("ok bye")); out.println("ok bye");
shutdown(); shutdown();
} else if ("login".equals(userInput.split("\\s+")[0])) { } else if (userInput.startsWith("login")) {
String[] args = userInput.split("\\s+"); String[] args = userInput.split("\\s+");
if (args.length != 3) if (args.length != 3)
out.println("Please specify a username and password to login!"); out.println("Please specify a username and password to login!");
@ -297,12 +351,14 @@ public class DMAPConnection implements Runnable {
} }
private void listMessages() { private void listMessages() {
logger.info("'list' command received");
if (storage.get(currentUser).isEmpty()) { if (storage.get(currentUser).isEmpty()) {
out.println("You do not have any messages at the moment!"); out.println("You do not have any messages at the moment!");
return; return;
} }
for (Message m : storage.get(currentUser)) { for (Message m : storage.get(currentUser)) {
logger.info("Printing message from user: " + m.listMessage());
out.println(m.listMessage()); out.println(m.listMessage());
} }
} }