Implement HMAC verification

This commit is contained in:
Tobias Eidelpes 2020-12-30 14:32:18 +01:00
parent 5ad8cded18
commit a7994736f1
5 changed files with 113 additions and 4 deletions

View File

@ -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();
}

View File

@ -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.
*
* <p>
* The challenge message is of the format:
* ok <client-challenge> <secret-key> <iv>
* The parameters are base64 encoded individually, then they are concatenated:
* ok <base64-client-challenge> <base64-secret-key> <base64-iv>
* 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

View File

@ -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 {

View File

@ -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();

View File

@ -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();