Package org.hive2hive.core.network.messages

Source Code of org.hive2hive.core.network.messages.MessageManager

package org.hive2hive.core.network.messages;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.util.HashMap;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;

import net.tomp2p.futures.FutureDirect;
import net.tomp2p.futures.FutureSend;
import net.tomp2p.p2p.RequestP2PConfiguration;
import net.tomp2p.peers.Number160;

import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.hive2hive.core.H2HConstants;
import org.hive2hive.core.H2HSession;
import org.hive2hive.core.exceptions.NoSessionException;
import org.hive2hive.core.network.NetworkManager;
import org.hive2hive.core.network.messages.direct.BaseDirectMessage;
import org.hive2hive.core.network.messages.direct.response.IResponseCallBackHandler;
import org.hive2hive.core.network.messages.direct.response.ResponseMessage;
import org.hive2hive.core.network.messages.futures.FutureDirectListener;
import org.hive2hive.core.network.messages.futures.FutureRoutedListener;
import org.hive2hive.core.network.messages.request.IRequestMessage;
import org.hive2hive.core.security.EncryptionUtil;
import org.hive2hive.core.security.HybridEncryptedContent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This class handles the sending of messages.
*
* @author Seppi
*/
public final class MessageManager implements IMessageManager {

  private static final Logger logger = LoggerFactory.getLogger(MessageManager.class);

  private final NetworkManager networkManager;
  private final HashMap<String, IResponseCallBackHandler> callBackHandlers;

  public MessageManager(NetworkManager networkManager) {
    this.networkManager = networkManager;
    this.callBackHandlers = new HashMap<String, IResponseCallBackHandler>();
  }

  @Override
  public boolean send(BaseMessage message, PublicKey targetPublicKey) {
    if (message.getTargetKey() == null)
      throw new IllegalArgumentException("target key cannot be null");
    if (targetPublicKey == null)
      throw new IllegalArgumentException("target public key cannot be null");

    // prepare message
    prepareMessage(message);
    message.increaseRoutedSendingCounter();

    // encrypt the message with the given public key
    HybridEncryptedContent encryptedMessage = signAndEncryptMessage(message, targetPublicKey);
    if (encryptedMessage == null)
      return false;

    // send message to the peer which is responsible for the given key
    FutureSend futureSend = networkManager.getConnection().getPeer()
        .send(Number160.createHash(message.getTargetKey())).setObject(encryptedMessage)
        .setRequestP2PConfiguration(createSendingConfiguration()).start();

    // attach a future listener to log, handle and notify events
    FutureRoutedListener listener = new FutureRoutedListener(message, targetPublicKey, this);
    futureSend.addListener(listener);
    boolean success = listener.await();

    if (success) {
      logger.debug("Message sent. Target key = '{}', Message ID = '{}'.",
          message.getTargetKey(), message.getMessageID());
    } else {
      logger.error("Message could not be sent. Target key = '{}', Message ID = '{}'.",
          message.getTargetKey(), message.getMessageID());
    }
    return success;
  }

  @Override
  public boolean sendDirect(BaseDirectMessage message, PublicKey targetPublicKey) {
    if (message.getTargetAddress() == null)
      throw new IllegalArgumentException("Target address cannot be null.");
    if (targetPublicKey == null)
      throw new IllegalArgumentException("Target public key cannot be null.");

    // prepare message
    prepareMessage(message);
    message.increaseDirectSendingCounter();

    // encrypt the message with the given public key
    HybridEncryptedContent encryptedMessage = signAndEncryptMessage(message, targetPublicKey);
    if (encryptedMessage == null)
      return false;

    // send message directly to the peer with the given peer address
    FutureDirect futureDirect = networkManager.getConnection().getPeer()
        .sendDirect(message.getTargetAddress()).setObject(encryptedMessage).start();
    // attach a future listener to log, handle and notify events
    FutureDirectListener listener = new FutureDirectListener(message, targetPublicKey, this);
    futureDirect.addListener(listener);
    boolean success = listener.await();

    if (success) {
      logger.debug("Message (direct) sent. Message ID = '{}', Target address = '{}', Sender address = '{}'.",
          message.getMessageID(), message.getTargetAddress(), message.getSenderAddress());
    } else {
      logger.error("Message (direct) could not be sent. Message ID = '{}', Target address = '{}', Sender address = '{}'.",
          message.getMessageID(), message.getTargetAddress(), message.getSenderAddress());
    }
    return success;
  }

  /**
   * Gets and removes a message callback handler
   *
   * @param messageId
   *            a unique message id
   * @return a callback handler or <code>null</code> if doesn't exist
   */
  public IResponseCallBackHandler getCallBackHandler(String messageId) {
    return callBackHandlers.remove(messageId);
  }

  /**
   * Check if a message callback handler exists.
   *
   * @param messageId
   *            a unique message id
   * @return <code>true</code> if exists and not <code>null</code>
   */
  public boolean checkIfCallbackHandlerExists(String messageId) {
    return (callBackHandlers.get(messageId) != null);
  }

  private void prepareMessage(BaseMessage message) {
    message.setSenderAddress(networkManager.getConnection().getPeer().getPeerAddress());
    configureCallbackHandlerIfNeeded(message);
    setSenderPublicKeyIfNeeded(message);
  }

  private RequestP2PConfiguration createSendingConfiguration() {
    return new RequestP2PConfiguration(1, 10, 0);
  }

  private void configureCallbackHandlerIfNeeded(BaseMessage message) {
    if (message instanceof IRequestMessage) {
      IRequestMessage requestMessage = (IRequestMessage) message;
      callBackHandlers.put(message.getMessageID(), requestMessage.getCallBackHandler());
      requestMessage.setCallBackHandler(null);
    }
  }

  /**
   * Sets the public key of the sender if message is a {@link IRequestMessage} so that the responding node
   * can easily encrypt the {@link ResponseMessage}.
   *
   * @param message
   *            message which will be send
   */
  private void setSenderPublicKeyIfNeeded(BaseMessage message) {
    if (message instanceof IRequestMessage) {
      try {
        message.setSenderPublicKey(networkManager.getSession().getKeyPair().getPublic());
      } catch (NoSessionException e) {
        logger.error("Could not set the sender's public key.");
        message.setSenderPublicKey(null);
      }
    }
  }

  private HybridEncryptedContent signAndEncryptMessage(BaseMessage message, PublicKey targetPublicKey) {
    H2HSession session;
    try {
      session = networkManager.getSession();
    } catch (NoSessionException e2) {
      logger.error("No logged in user / no session. The message will not be sent.");
      return null;
    }

    try {
      // asymmetrically encrypt message
      byte[] messageBytes = EncryptionUtil.serializeObject(message);
      HybridEncryptedContent encryptedMessage = EncryptionUtil.encryptHybrid(messageBytes,
          targetPublicKey, H2HConstants.KEYLENGTH_HYBRID_AES);

      // create signature
      try {
        byte[] signature = EncryptionUtil.sign(messageBytes, session.getKeyPair().getPrivate());
        encryptedMessage.setSignature(session.getUserId(), signature);
      } catch (InvalidKeyException | SignatureException e1) {
        logger.error("An exception occured while signing the message. The message will not be sent.",
            e1);
        return null;
      }

      return encryptedMessage;
    } catch (DataLengthException | InvalidKeyException | IllegalStateException
        | InvalidCipherTextException | IllegalBlockSizeException | BadPaddingException | IOException e) {
      logger.error("An exception occured while encrypting the message. The message will not be sent.",
          e);
      return null;
    }
  }

}
TOP

Related Classes of org.hive2hive.core.network.messages.MessageManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.