package dslab.crypto; import dslab.protocol.Message; import dslab.routing.Address; import javax.crypto.Mac; import java.io.File; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.Comparator; import java.util.logging.Logger; import java.util.stream.Collectors; import static dslab.util.Keys.readSecretKey; import static dslab.util.Utils.listElements; import static java.util.Comparator.comparing; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; public class MessageClientHash { private final Mac mac; private static final Logger LOG = Logger.getLogger(MessageClientHash.class.getSimpleName()); public MessageClientHash() { try { this.mac = Mac.getInstance("HmacSHA256"); mac.init(readSecretKey(new File("keys/hmac.key"))); } catch (IOException | InvalidKeyException | NoSuchAlgorithmException e) { throw new RuntimeException("Unable to initialize hash functionality", e); } } public String calculateHashValue(Message message){ // Build the message from all given components String messageString = String.join("\n", message.getSender().toString(), message.getRecipients().stream().map(Address::toString).sorted().collect(joining(",")), message.getSubject(), message.getData()); return calculateHashValue(messageString); } public String calculateHashValue(String message) { // Build the MAC in a single step byte[] hash = mac.doFinal(message.getBytes()); // Because our DMTP protocol transfers data using plain text -> We will // use the Base64 binary-to-text encoding to encode the hash before sending it. var result = Base64.getEncoder().encodeToString(hash); LOG.info("Calculated hash '" + result + "' for message " + message); return result; } public boolean isValid(Message message) { return calculateHashValue(message).equals(message.getHash()); } public void calculateAndSet(Message message) { message.setHash(calculateHashValue(message)); } }