diff --git a/src/main/java/dslab/Message.java b/src/main/java/dslab/Message.java
index fa6321e..0eee42f 100644
--- a/src/main/java/dslab/Message.java
+++ b/src/main/java/dslab/Message.java
@@ -1,8 +1,17 @@
package dslab;
import dslab.exception.MissingInputException;
+import dslab.util.Keys;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
+import java.util.Base64;
import java.util.stream.Collectors;
public class Message {
@@ -89,6 +98,23 @@ public class Message {
this.hash = hash;
}
+ /**
+ * Calculate a HMAC hash of the message.
+ *
+ * @return A Base64 encoded hash of the message.
+ * @throws IOException Thrown if the HMAC key (in "keys/hmac.key") cannot be found.
+ * @throws NoSuchAlgorithmException Thrown if HmacSHA256 is not known.
+ * @throws InvalidKeyException Thrown if the key is not valid.
+ */
+ public String calculateHash() throws IOException, NoSuchAlgorithmException, InvalidKeyException {
+ byte[] hashFormat = String.join("\n", getFrom().toString(), printTo(), getSubject(), getData())
+ .getBytes(StandardCharsets.UTF_8);
+ Mac mac = Mac.getInstance("HmacSHA256");
+ SecretKeySpec hmac = Keys.readSecretKey(new File("keys/hmac.key"));
+ mac.init(hmac);
+ return (Base64.getEncoder().encodeToString(mac.doFinal(hashFormat)));
+ }
+
public String listMessage() {
return getId() + " " + getFrom() + " " + getSubject();
}
diff --git a/src/main/java/dslab/client/MessageClient.java b/src/main/java/dslab/client/MessageClient.java
index b783d4e..dd23ba6 100644
--- a/src/main/java/dslab/client/MessageClient.java
+++ b/src/main/java/dslab/client/MessageClient.java
@@ -10,13 +10,17 @@ import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
+import java.util.logging.Level;
import java.util.logging.Logger;
import at.ac.tuwien.dsg.orvell.Shell;
import at.ac.tuwien.dsg.orvell.StopShellException;
import at.ac.tuwien.dsg.orvell.annotation.Command;
import dslab.ComponentFactory;
+import dslab.Email;
+import dslab.Message;
import dslab.exception.FailedVerificationException;
+import dslab.exception.MalformedInputException;
import dslab.util.Config;
import javax.crypto.*;
@@ -277,12 +281,13 @@ public class MessageClient implements IMessageClient, Runnable {
/**
* Generates the full challenge message to be sent to the server.
- *
+ *
* The challenge message is of the format:
* ok
* The parameters are base64 encoded individually, then they are concatenated:
* ok
* The whole string is then AES encrypted and the result base64 encoded again.
+ *
* @return A base64 encoded full client challenge.
*/
private String generateChallengeMessage(PublicKey serverPublicKey) {
@@ -331,11 +336,70 @@ public class MessageClient implements IMessageClient, Runnable {
}
+ /**
+ * Pulls a message by id from the MailboxServer and compares the calculated hash against the one received via the
+ * 'hash' field.
+ *
+ * @param id The message id.
+ */
@Command
@Override
public void verify(String id) {
logger.info("Received 'verify' command for id " + id);
+ Message msg = new Message();
+ String response;
+ // Get message from MailboxServer
+ try {
+ dmapOut.println(getAesCiphertext("show " + id));
+ response = getAesPlaintext(dmapIn.readLine());
+ if (response.startsWith("error")) {
+ this.shell.out().println(response);
+ return;
+ }
+ String[] commands = response.split("\n");
+ if (commands.length != 6) {
+ logger.severe("error expected different message format");
+ return;
+ }
+ msg.setFrom(new Email(commands[0].split("\\s+")[1]));
+ String[] addresses = commands[1].split("\\s+")[1].split(",");
+ for (String email : addresses) {
+ msg.addTo(new Email(email));
+ }
+
+ String[] split = commands[2].split("\\s+", 2);
+ msg.setSubject(split.length == 2 ? split[1] : "");
+
+ split = commands[3].split("\\s+", 2);
+ msg.setData(split.length == 2 ? split[1] : "");
+
+ split = commands[4].split("\\s+", 2);
+ msg.setHash(split.length == 2 ? split[1] : "");
+
+ if (!commands[5].startsWith("ok")) {
+ logger.severe("error expected 'ok', got " + commands[5] + " instead");
+ }
+
+ String calculatedHash = msg.calculateHash();
+ logger.info("Calculated hash: " + calculatedHash);
+ if (msg.getHash().equals(calculatedHash)) {
+ this.shell.out().println("ok");
+ } else {
+ this.shell.out().println("error");
+ }
+ } catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
+ logger.severe("Error during encryption/decryption");
+ e.printStackTrace();
+ shutdown();
+ } catch (IOException e) {
+ logger.severe("Cannot communicate with server");
+ e.printStackTrace();
+ shutdown();
+ } catch (MalformedInputException e) {
+ logger.severe("Error during reading of message");
+ e.printStackTrace();
+ }
}
@Command
diff --git a/src/main/java/dslab/mailbox/DMAPConnection.java b/src/main/java/dslab/mailbox/DMAPConnection.java
index 01af5e8..4c63113 100644
--- a/src/main/java/dslab/mailbox/DMAPConnection.java
+++ b/src/main/java/dslab/mailbox/DMAPConnection.java
@@ -109,6 +109,8 @@ public class DMAPConnection implements Runnable {
if (secure) {
out.println(getAesCiphertext("You are already secure!"));
} else startSecure();
+ } else if (userInput.startsWith("inbox")) {
+ printInbox();
} else {
if (secure) {
out.println(getAesCiphertext("error protocol error"));
@@ -130,6 +132,9 @@ public class DMAPConnection implements Runnable {
}
}
+ private void printInbox() {
+ }
+
private void loginLoop() {
String userInput;
@@ -330,8 +335,8 @@ public class DMAPConnection implements Runnable {
for (Message m : storage.get(currentUser)) {
if (m.getId() == i) {
if (secure) {
- out.println(getAesCiphertext(m.toString()));
- } else out.println(m.toString());
+ out.println(getAesCiphertext(m.toString() + "ok"));
+ } else out.println(m.toString() + "\nok");
return;
}
}
@@ -349,11 +354,14 @@ public class DMAPConnection implements Runnable {
}
for (Message m : storage.get(currentUser)) {
- logger.info("Printing message from user: " + m.listMessage());
+ logger.fine("Printing message from user: " + m.listMessage());
if (secure) {
out.println(getAesCiphertext(m.listMessage()));
} else out.println(m.listMessage());
}
+ if (secure) {
+ out.println(getAesCiphertext("ok"));
+ } else out.println("ok");
}
public void deleteMessage(String id) throws MessageNotFoundException, BadPaddingException, IllegalBlockSizeException {
diff --git a/src/main/java/dslab/mailbox/DMTPConnection.java b/src/main/java/dslab/mailbox/DMTPConnection.java
index da4d009..60805b0 100644
--- a/src/main/java/dslab/mailbox/DMTPConnection.java
+++ b/src/main/java/dslab/mailbox/DMTPConnection.java
@@ -110,6 +110,10 @@ public class DMTPConnection implements Runnable {
String data = userInput.split("\\s+", 2)[1];
msg.setData(data);
out.println("ok");
+ } else if (userInput.startsWith("hash")) {
+ String hash = userInput.split("\\s+", 2)[1];
+ msg.setHash(hash);
+ out.println("ok");
} else {
out.println("error protocol error");
shutdown();
diff --git a/src/main/java/dslab/transfer/ClientConnection.java b/src/main/java/dslab/transfer/ClientConnection.java
index 7a1ef64..58212ff 100644
--- a/src/main/java/dslab/transfer/ClientConnection.java
+++ b/src/main/java/dslab/transfer/ClientConnection.java
@@ -92,6 +92,13 @@ public class ClientConnection implements Runnable {
logger.info("Setting data to: " + data);
msg.setData(data);
out.println("ok");
+ } else if (userInput.startsWith("hash")) {
+ String hash = "";
+ if (userInput.split("\\s+", 2).length > 1)
+ hash = userInput.split("\\s+", 2)[1];
+ logger.info("Setting hash to: " + hash);
+ msg.setHash(hash);
+ out.println("ok");
} else {
out.println("error protocol error");
shutdown();