ch.bfh.evoting.alljoyn.BusHandler.java Source code

Java tutorial

Introduction

Here is the source code for ch.bfh.evoting.alljoyn.BusHandler.java

Source

/* 
 * MobiVote
 * 
 *  MobiVote: Mobile application for boardroom voting
 *  Copyright (C) 2014 Bern
 *  University of Applied Sciences (BFH), Research Institute for Security
 *  in the Information Society (RISIS), E-Voting Group (EVG) Quellgasse 21,
 *  CH-2501 Biel, Switzerland
 * 
 *  Licensed under Dual License consisting of:
 *  1. GNU Affero General Public License (AGPL) v3
 *  and
 *  2. Commercial license
 * 
 *
 *  1. This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 *
 *  2. Licensees holding valid commercial licenses for MobiVote may use this file in
 *   accordance with the commercial license agreement provided with the
 *   Software or, alternatively, in accordance with the terms contained in
 *   a written agreement between you and Bern University of Applied Sciences (BFH), 
 *   Research Institute for Security in the Information Society (RISIS), E-Voting Group (EVG)
 *   Quellgasse 21, CH-2501 Biel, Switzerland.
 * 
 *
 *   For further information contact us: http://e-voting.bfh.ch/
 * 
 *
 * Redistributions of files must retain the above copyright notice.
 */
package ch.bfh.evoting.alljoyn;
/******************************************************************************
 * Copyright 2013, Qualcomm Innovation Center, Inc.
 *
 *    All rights reserved.
 *    This file is licensed under the 3-clause BSD license in the NOTICE.txt
 *    file for this project. A copy of the 3-clause BSD license is found at:
 *
 *        http://opensource.org/licenses/BSD-3-Clause.
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the license is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the license for the specific language governing permissions and
 *    limitations under the license.
 *    
 *    Modified by Philemon von Bergen
 ******************************************************************************/

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;

import org.alljoyn.bus.BusException;
import org.alljoyn.bus.BusObject;
import org.alljoyn.bus.Status;
import org.alljoyn.bus.annotation.BusSignalHandler;
import org.alljoyn.cops.peergroupmanager.PeerGroupManager;
import org.alljoyn.cops.peergroupmanager.BusObjectData;
import org.alljoyn.cops.peergroupmanager.PeerGroupListener;

import ch.bfh.evoting.alljoyn.AllJoynMessage.Type;
import ch.bfh.evoting.alljoyn.util.JavaSerialization;
import ch.bfh.evoting.alljoyn.util.SerializationUtil;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Base64;
import android.util.Log;

/**
 * This handler run in its own thread an queues the message it has to process
 * Original class from AllJoyn PeerManagerApp example
 * Adapted by Philemon von Bergen
 *
 */
public class BusHandler extends Handler {

    /*
     * Constants
     */
    private static final String TAG = BusHandler.class.getSimpleName();
    private static final String SERVICE_NAME = "ch.bfh.evoting";
    private static final String PREFS_NAME = "network_preferences";

    private static final String MESSAGE_PARTS_SEPARATOR = "||";

    /*
     * AllJoyn
     */
    private SimpleService mSimpleService = new SimpleService();
    private PeerGroupManager mGroupManager;

    /*
     * Identity informations
     */
    private HashMap<String, Identity> identityMap = new HashMap<String, Identity>();
    private SharedPreferences userDetails;

    /*
     * Other
     */
    private Context context;
    private String lastJoinedNetwork;
    private boolean amIAdmin = false;
    private boolean connected = false;

    private MessageEncrypter messageEncrypter;
    private MessageAuthenticater messageAuthenticater;
    private AllJoynMessage signatureVerificationTask;
    private SerializationUtil su;
    protected boolean feedbackReceived;

    public static final int INIT = 1;
    public static final int CREATE_GROUP = 2;
    public static final int DESTROY_GROUP = 3;
    public static final int JOIN_GROUP = 4;
    public static final int LEAVE_GROUP = 5;
    public static final int UNLOCK_GROUP = 6;
    public static final int LOCK_GROUP = 7;
    public static final int SET_PORT = 8;
    public static final int JOIN_OR_CREATE = 9;
    public static final int DISCONNECT = 10;
    public static final int PING = 13;
    public static final int REPROCESS_MESSAGE = 14;

    /**
     * Initialization of the bus handler
     * @param looper looper
     * @param ctx Android context of the application
     */
    public BusHandler(Looper looper, Context ctx) {
        super(looper);
        // Connect to an AllJoyn object.
        this.context = ctx;
        this.doInit();

        userDetails = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);

        su = new SerializationUtil(new JavaSerialization());

        messageEncrypter = new MessageEncrypter(ctx);
        messageAuthenticater = new MessageAuthenticater();
        //We generate a new key pair each time the app is started, so we do not need to save it in order to reuse it later
        //Creating a new key pair takes time, so we create only 512 bits keys. This is sufficient since the key pair is used during 
        //maximum one hour and a brute force attack should be done online
        messageAuthenticater.generateKeys();
    }

    @Override
    public void handleMessage(Message msg) {
        Status status = null;
        Intent intent;
        switch (msg.what) {
        case INIT: {
            doInit();
            break;
        }
        case CREATE_GROUP: {
            Bundle data = msg.getData();
            String groupName = data.getString("groupName");
            String groupPassword = data.getString("groupPassword");
            status = doCreateGroup(groupName, groupPassword);
            Log.d(TAG, "status of group creation " + status);
            switch (status) {
            case OK:
                LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("NetworkServiceStarted"));
                break;
            case ALLJOYN_JOINSESSION_REPLY_ALREADY_JOINED:
                LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("NetworkServiceStarted"));
                break;
            case INVALID_DATA:
                //invalid group name
                intent = new Intent("networkConnectionFailed");
                intent.putExtra("error", 1);
                LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
                break;
            case ALREADY_FINDING:
                //group name already exists
                intent = new Intent("networkConnectionFailed");
                intent.putExtra("error", 2);
                LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
                break;
            default:
                LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("networkConnectionFailed"));
                break;
            }
            break;
        }
        case DESTROY_GROUP: {
            status = doDestroyGroup((String) msg.obj);
            break;
        }
        case JOIN_GROUP: {
            Bundle data = msg.getData();
            String groupName = data.getString("groupName");
            String groupPassword = data.getString("groupPassword");
            String saltShortDigest = data.getString("saltShortDigest");
            status = doJoinGroup(groupName, groupPassword, saltShortDigest);
            Log.d(TAG, "status of group join " + status);
            switch (status) {

            case OK:
                LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("NetworkServiceStarted"));
                break;
            case ALLJOYN_JOINSESSION_REPLY_ALREADY_JOINED:
                LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("NetworkServiceStarted"));
                break;
            case INVALID_DATA:
                //invalid group name
                intent = new Intent("networkConnectionFailed");
                intent.putExtra("error", 1);
                LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
                break;
            case ALLJOYN_FINDADVERTISEDNAME_REPLY_FAILED:
                //group not found
                intent = new Intent("networkConnectionFailed");
                intent.putExtra("error", 3);
                LocalBroadcastManager.getInstance(context).sendBroadcast(intent);

                break;
            case ALLJOYN_JOINSESSION_REPLY_FAILED:
                //group not joined
                intent = new Intent("networkConnectionFailed");
                intent.putExtra("error", 4);
                LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
                break;
            default:
                LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("networkConnectionFailed"));
                break;
            }
            break;
        }
        case LEAVE_GROUP: {
            status = doLeaveGroup((String) msg.obj);
            break;
        }
        case UNLOCK_GROUP: {
            status = doUnlockGroup((String) msg.obj);
            break;
        }
        case LOCK_GROUP: {
            status = doLockGroup((String) msg.obj);
            break;
        }
        case SET_PORT: {
            doSetPort((Short) msg.obj);
            break;
        }
        case JOIN_OR_CREATE: {
            status = doJoinOrCreate((String) msg.obj);
            break;
        }
        case PING: {
            Bundle data = msg.getData();
            String groupName = data.getString("groupName");
            String pingString = data.getString("pingString");
            boolean encrypted = data.getBoolean("encrypted", true);
            Type type = (Type) data.getSerializable("type");
            doPing(groupName, pingString, encrypted, type);
            break;
        }
        case REPROCESS_MESSAGE: {
            Bundle data = msg.getData();
            AllJoynMessage message = (AllJoynMessage) data.getSerializable("message");
            this.processMessage(message);
            break;
        }
        case DISCONNECT: {
            doDisconnect();
            break;
        }
        default:
            break;
        }
    }

    /******************************************************************************
     * 
     * Network actions
     *
     ******************************************************************************/

    /**
     * Initialize AllJoyn
     */
    private void doInit() {
        PeerGroupListener pgListener = new PeerGroupListener() {

            @Override
            public void foundAdvertisedName(String groupName, short transport) {
                Intent i = new Intent("advertisedGroupChange");
                LocalBroadcastManager.getInstance(context).sendBroadcast(i);
            }

            @Override
            public void lostAdvertisedName(String groupName, short transport) {
                Intent i = new Intent("advertisedGroupChange");
                LocalBroadcastManager.getInstance(context).sendBroadcast(i);
            }

            @Override
            public void groupLost(String groupName) {
                if (mGroupManager.listHostedGroups().contains(groupName)
                        && mGroupManager.getNumPeers(groupName) == 1) {
                    //signal was send because admin stays alone in the group
                    //not necessary to manage this case for us
                    Log.d(TAG, "Group destroyed event ignored");
                    return;
                }
                Log.d(TAG, "Group " + groupName + " was destroyed.");
                Intent i = new Intent("groupDestroyed");
                i.putExtra("groupName", groupName);
                LocalBroadcastManager.getInstance(context).sendBroadcast(i);
            }

            @Override
            public void peerAdded(String busId, String groupName, int numParticipants) {
                Log.d(TAG, "peer added");

                if (amIAdmin) {
                    Log.d(TAG, "Sending salt " + Base64.encodeToString(messageEncrypter.getSalt(), Base64.DEFAULT));
                    Message msg = obtainMessage(BusHandler.PING);
                    Bundle data = new Bundle();
                    data.putString("groupName", lastJoinedNetwork);
                    data.putString("pingString", Base64.encodeToString(messageEncrypter.getSalt(), Base64.DEFAULT));
                    data.putBoolean("encrypted", false);
                    data.putSerializable("type", Type.SALT);
                    msg.setData(data);
                    sendMessage(msg);
                }

                //update UI
                LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("participantStateUpdate"));

            }

            @Override
            public void peerRemoved(String peerId, String groupName, int numPeers) {
                //update UI
                Log.d(TAG, "peer left");
                Intent intent = new Intent("participantStateUpdate");
                intent.putExtra("action", "left");
                intent.putExtra("id", peerId);
                LocalBroadcastManager.getInstance(context).sendBroadcast(intent);

                super.peerRemoved(peerId, groupName, numPeers);
            }

        };

        ArrayList<BusObjectData> busObjects = new ArrayList<BusObjectData>();
        Handler mainHandler = new Handler(context.getMainLooper());

        Runnable myRunnable = new Runnable() {

            @Override
            public void run() {
                org.alljoyn.bus.alljoyn.DaemonInit.PrepareDaemon(context);
            }

        };
        mainHandler.post(myRunnable);

        busObjects.add(new BusObjectData(mSimpleService, "/SimpleService"));
        mGroupManager = new PeerGroupManager(SERVICE_NAME, pgListener, busObjects);
        mGroupManager.registerSignalHandlers(this);
    }

    /**
     * Disconnect AllJoyn
     */
    private void doDisconnect() {
        mGroupManager.cleanup();
        connected = false;
        getLooper().quit();
    }

    /**
     * Create a group
     * @param groupName name of the group (must begin with a character, not a number!)
     * @return status of the creation
     */
    private Status doCreateGroup(String groupName, String groupPassword) {
        connected = false;
        this.identityMap.clear();
        lastJoinedNetwork = null;
        //If group already exists, connection will fail
        //if multicast is not supported on the network, listFoundGroups
        //will be empty, so this it will only be detected during the group creation
        //that this name is already used.
        if (mGroupManager.listFoundGroups().contains(groupName)) {
            return Status.ALREADY_FINDING;
        }
        messageEncrypter.reset();

        //Create a salt and derive the key
        messageEncrypter.setPassword(groupPassword);
        messageEncrypter.generateSalt();

        //create the group
        Status status = mGroupManager.createGroup(groupName);
        if (status == Status.OK || status == Status.ALLJOYN_JOINSESSION_REPLY_ALREADY_JOINED) {
            //save my identity
            String myName = userDetails.getString("identification", "");
            identityMap.put(this.getIdentification(), new Identity(myName, messageAuthenticater.getMyPublicKey()));
            //some flags
            lastJoinedNetwork = groupName;
            amIAdmin = true;
            connected = true;
        }
        return status;
    }

    /**
     * Destroy a group
     * @param groupName name of the group
     * @return status of the destruction of the group
     */
    private Status doDestroyGroup(String groupName) {
        amIAdmin = false;
        lastJoinedNetwork = null;
        messageEncrypter.reset();
        return mGroupManager.destroyGroup(groupName);
    }

    /**
     * Join an existing group
     * @param groupName name of the group
     * @return status of join
     */
    private Status doJoinGroup(String groupName, String groupPassword, String saltShortDigest) {
        this.identityMap.clear();
        amIAdmin = false;
        connected = false;
        messageEncrypter.reset();
        messageEncrypter.setPassword(groupPassword);
        messageEncrypter.setSaltShortDigest(saltShortDigest);
        Log.d(TAG, "Joining group...");
        Status status = mGroupManager.joinGroup(groupName);

        if (status == Status.OK) {
            String myName = userDetails.getString("identification", "");
            identityMap.put(this.getIdentification(), new Identity(myName, messageAuthenticater.getMyPublicKey()));

            lastJoinedNetwork = groupName;
            connected = true;

            if (messageEncrypter.isReady())
                sendMyIdentity();
        }
        return status;
    }

    /**
     * Leave a group
     * @param groupName name of the group
     * @return status
     */
    private Status doLeaveGroup(String groupName) {
        return mGroupManager.leaveGroup(groupName);
    }

    /**
     * Unlock a group
     * @param groupName name of the group
     * @return status
     */
    private Status doUnlockGroup(String groupName) {
        return mGroupManager.unlockGroup(groupName);
    }

    /**
     * Lock a group
     * @param groupName
     * @return status
     */
    private Status doLockGroup(String groupName) {
        return mGroupManager.lockGroup(groupName);
    }

    /**
     * Set the port on which the message exchange should be done
     * @param sessionPort
     */
    private void doSetPort(short sessionPort) {
        mGroupManager.setSessionPort(sessionPort);
    }

    /**
     * Create or join a group depending on if it already exists or not
     * @param groupName  name of the group
     * @return status
     */
    private Status doJoinOrCreate(String groupName) {
        return mGroupManager.joinOrCreateGroup(groupName).getStatus();
    }

    /******************************************************************************
     * 
     * Network informations
     *
     ******************************************************************************/

    /**
     * Get my peer unique id
     * @return my peer unique id
     */
    public String getIdentification() {
        return mGroupManager.getMyPeerId();
    }

    /**
     * Get all participants of the given group
     * @param groupName the name of the group of which we want to get the participants
     * @return list of participant unique id
     */
    public ArrayList<String> getParticipants(String groupName) {
        return mGroupManager.getPeers(groupName);
    }

    /**
     * Get the well-known name of the given peer
     * @param peerId the peer unique identifier
     * @return the well-known name of the given peer
     */
    public String getPeerWellKnownName(String peerId) {

        if (!identityMap.containsKey(peerId)) {
            return null;
        } else {
            return identityMap.get(peerId).getName();
        }
    }

    /**
     * Get the public key of the given peer
     * @param peerId the peer unique identifier
     * @return the SHA-256 mod 2^64 of the ASN-1 representation of the public key of the given peer
     */
    public String getPeerPublicKey(String peerId) {

        if (!identityMap.containsKey(peerId)) {
            return null;
        } else {
            byte[] encodedKey = identityMap.get(peerId).getPublicKey().getEncoded();
            MessageDigest digest;
            try {
                digest = MessageDigest.getInstance("SHA-256");
                byte[] hash = digest.digest(encodedKey);
                BigInteger hashBI = new BigInteger(hash);
                return bytesToHex(hashBI.mod(BigInteger.valueOf(2).pow(64)).toByteArray());
            } catch (NoSuchAlgorithmException e) {

                e.printStackTrace();
                return "";
            }

        }
    }

    final private char[] hexArray = "0123456789ABCDEF".toCharArray();

    /**
     * Converts a byte array to a hex encoded string
     * @param bytes
     * @return
     */
    public String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 3];
        for (int j = 0; j < bytes.length; j++) {
            if (j * 2 % 2 == 0 && j > 0)
                hexChars[j * 3] = ':';
            int v = bytes[j] & 0xFF;
            hexChars[j * 3 + 1] = hexArray[v >>> 4];
            hexChars[j * 3 + 2] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    /**
     * List the existing group on the network
     * @return a list of name of the existing groups
     */
    public ArrayList<String> listGroups() {
        return mGroupManager.listFoundGroups();
    }

    /**
     * Get the three first letters of the digest (Base64 encoded) of the generated salt
     * @return the three first letters of the digest of the generated salt
     */
    public String getSaltShortDigest() {
        return this.messageEncrypter.getSaltShortDigest(messageEncrypter.getSalt());
    }

    /**
     * Indicate if AllJoyn is connected in a group
     * @return true if connected, false otherwise
     */
    public boolean getConnected() {
        return connected;
    }

    /******************************************************************************
     * 
     * Message processing
     *
     ******************************************************************************/

    /**
     * Send a message to the given group
     * @param groupName group to send the message to
     * @param message message to send
     * @param encrypted indicate if message must be encrypted or not 
     * @param type type of the content in the message
     */
    private void doPing(String groupName, String message, boolean encrypted, Type type) {

        //if messageEncrypter is not ready or group joining is not terminated, we enqueue the message
        if ((!messageEncrypter.isReady() && encrypted) || !connected) {
            Message msg = this.obtainMessage(BusHandler.PING);
            Bundle data = new Bundle();
            data.putString("groupName", groupName);
            data.putString("pingString", message);
            data.putBoolean("encrypted", encrypted);
            data.putSerializable("type", type);
            msg.setData(data);
            this.sendMessage(msg);
            Log.d(TAG, "Queueing message to send " + message);
            return;
        }

        //Create the message object with the received parameter
        AllJoynMessage messageObject = new AllJoynMessage(this.messageEncrypter, this.messageAuthenticater);
        if (type == null)
            type = Type.NORMAL;
        messageObject.setType(type);
        messageObject.setSender(this.getIdentification());
        boolean messageEncrypted = messageObject.setMessage(message, encrypted);
        if (!messageEncrypted) {
            Log.e(TAG, "Message encryption failed");
            return;
        }
        boolean messageSigned = messageObject.signMessage();
        if (!messageSigned) {
            Log.e(TAG, "Signature failed");
            return;
        }

        //Serialize the message
        messageObject.setMessageAuthenticater(null);
        messageObject.setMessageEncrypter(null);
        String toSend = su.serialize(messageObject);

        try {
            Log.d(TAG, "sending message of size " + toSend.getBytes("UTF-8").length
                    + " bytes. Maximum allowed by AllJoyn is 128Kb.");
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }

        //Send the message
        SimpleInterface simpleInterface = mGroupManager.getSignalInterface(groupName, mSimpleService,
                SimpleInterface.class);
        try {
            if (simpleInterface != null) {
                simpleInterface.Ping(toSend);
            }
        } catch (BusException e) {
            e.printStackTrace();
        }
    }

    /**
     * Simple class implementing the AllJoyn interface
     */
    class SimpleService implements SimpleInterface, BusObject {
        public void Ping(String Str) {
        }
    }

    /**
     * Signal Handler for the Ping signal
     * This method receives the message from the other peers
     * @param str content of the message
     */
    @BusSignalHandler(iface = "org.alljoyn.bus.samples.simple.SimpleInterface", signal = "Ping")
    public void Ping(String str) {

        if (str == null)
            return;

        //Deserialize the message
        AllJoynMessage message = (AllJoynMessage) su.deserialize(str, AllJoynMessage.class);
        if (message == null) {
            //deserialization failed, that means that this was not a message sent by our application
            Log.d(TAG, "Deserialization of message failed.");
            return;
        }
        message.setMessageAuthenticater(this.messageAuthenticater);
        message.setMessageEncrypter(this.messageEncrypter);

        if (connected) {
            String sender = mGroupManager.getSenderPeerId();
            //we ask alljoin for the sender id and compare it with the sender found in the message
            //if it is different, someone is trying to send a message on behalf of another peer
            if (!message.getSender().equals(sender)) {
                Intent intent = new Intent("attackDetected");
                intent.putExtra("type", 2);
                LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
                Log.e(TAG, "The name of the sender of the message indicated by AllJoyn is " + sender
                        + " but the sender indicated in the message is " + message.getSender() + "!!");
                return;
            }
        }

        processMessage(message);
    }

    /**
     * Method processing the message received
     * @param messageObject object containing the message transmitted over the newtork
     */
    private void processMessage(AllJoynMessage messageObject) {

        /*
         * First we check if decrypter is ready. If not, only Salt message can go further
         */
        //if messageEncrypter isn't ready to decrypt and message is encrypted
        if (!messageEncrypter.isReady() && messageObject.isEncrypted()) {
            //requeue the message in order to process it later
            Message msg = this.obtainMessage(BusHandler.REPROCESS_MESSAGE);
            Bundle data = new Bundle();
            data.putSerializable("message", messageObject);
            msg.setData(data);
            this.sendMessage(msg);
            Log.d(TAG, "Requeueing message received");
            return;
        }

        /*
         * Second, we check if the message contains the salt and extract it
         */
        //Check if the message contains the salt
        if (messageObject.getType().equals(Type.SALT)) {
            if (messageEncrypter.getSalt() == null) {
                this.saltReceived(messageObject);
                return;
            }
        }

        /*
         * Third, we check if the message contains an identity
         */
        //Check if message contain an identity
        if (messageObject.getType().equals(Type.IDENTITY)) {
            extractIdentity(messageObject);
            return;
        }

        /*
         * Fourth, we verify the signature, if we know the sender, otherwise we set a flag
         */
        if (identityMap.containsKey(messageObject.getSender())) {
            boolean result = messageObject.verifyMessage(identityMap.get(messageObject.getSender()).getPublicKey());//messageAuthenticater.verifySignature(identityMap.get(sender).getPublicKey(), Base64.decode(signature,Base64.DEFAULT), Base64.decode(message,Base64.DEFAULT));
            if (!result) {
                //signature verification failed
                //ignoring message
                Log.e(TAG, "Wrong signature");
                return;
            } else {
                Log.d(TAG, "Signature correct");
            }
        } else {
            //message not containing a salt nor an identity coming from an unknown person => ignore 
            Log.d(TAG, "Ignoring message since sender is not known");
            //try to decrypt in order to detect if it could be a password error
            messageObject.getMessage();
            return;
        }

        /*
         * Fifth, we decrypt the message
         */
        String decryptedString = messageObject.getMessage();
        if (decryptedString == null || decryptedString.equals("")) {
            //decryption failed
            Log.d(TAG, "Message decryption failed");
            return;
        }

        /*
         * Sixth, we transmit message to the application
         */
        //Send the message to the app
        Intent i = new Intent("messageArrived");
        i.putExtra("senderId", messageObject.getSender());
        i.putExtra("senderName", identityMap.get(messageObject.getSender()).getName());
        i.putExtra("message", decryptedString);
        LocalBroadcastManager.getInstance(context).sendBroadcast(i);
    }

    /******************************************************************************
     * 
     * Helper methods
     *
     ******************************************************************************/

    /**
     * Helper method extracting an identity from a received message
     * @param messageObject the original message received
     */
    private void extractIdentity(AllJoynMessage messageObject) {
        Log.d(TAG, "Exctracting identity " + messageObject.getMessage());

        //Get the name and its corresponding key key
        StringTokenizer tokenizer = new StringTokenizer(messageObject.getMessage(), MESSAGE_PARTS_SEPARATOR);
        if (tokenizer.countTokens() != 2) {
            //malformed string
            Log.d(TAG, "String was not composed of 2 parts");
            return;
        }

        String peerId = messageObject.getSender();
        //then peerName
        String peerName = (String) tokenizer.nextElement();
        //and finally peerKey
        String peerKey = (String) tokenizer.nextElement();

        Identity newIdentity = new Identity(peerName, messageAuthenticater.decodePublicKey(peerKey));

        //Check if identity is already known
        if (identityMap.containsKey(peerId)) {
            //if yes, check if the same identity as received before
            if (!identityMap.get(peerId).equals(newIdentity)) {
                //If not someone is trying to impersonate somebody else
                Intent intent = new Intent("attackDetected");
                intent.putExtra("type", 1);
                LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
                Log.e(TAG, "Two different data received for peer " + peerId);
                return;
            }
        } else {
            boolean verification = messageObject.verifyMessage(newIdentity.getPublicKey());
            if (verification) {
                //Save the new identity
                Log.d(TAG, "identity received " + newIdentity.getName());
                identityMap.put(peerId, newIdentity);
                this.sendMyIdentity();
                //Update the UI
                LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("participantStateUpdate"));

                if (peerId.equals(signatureVerificationTask.getSender())) {
                    verifySignatureSalt();
                }
            } else {
                Log.e(TAG, "Wrong signature for identiy message from " + peerId);
                return;
            }
        }
    }

    /**
     * Helper method extracting the salt from the recived message
     * @param message original message containing the salt
     */
    private void saltReceived(AllJoynMessage message) {

        Log.d(TAG, "Exctracting salt " + message.getMessage());

        if (messageEncrypter.getSalt() == null) {
            messageEncrypter.setSalt(message.getMessage());
        } else {
            if (!messageEncrypter.getSalt().equals(message.getMessage())) {
                //someone is sending a false salt
                Intent intent = new Intent("attackDetected");
                intent.putExtra("type", 3);
                LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
                Log.e(TAG, "Different salts have been received!");
                return;
            }
        }

        //Add to verification task in order to check the signature later
        signatureVerificationTask = message;

        //send my identity
        if (connected) {
            this.sendMyIdentity();
        }
    }

    /**
     * Helper method send my identity to the other peers
     */
    private void sendMyIdentity() {
        Log.d(TAG, "Sending my identity");

        String myName = userDetails.getString("identification", "");

        //save my own identity in identityMap
        Identity myIdentity = new Identity(myName, messageAuthenticater.getMyPublicKey());
        identityMap.put(this.getIdentification(), myIdentity);

        byte[] publicKeyBytes = messageAuthenticater.getMyPublicKey().getEncoded();
        if (publicKeyBytes == null) {
            Log.e(TAG, "Key encoding not supported");
        }
        String myKey = Base64.encodeToString(publicKeyBytes, Base64.DEFAULT);
        String identity = myName + MESSAGE_PARTS_SEPARATOR + myKey;
        Log.d(TAG, "Send my identity");

        //send my identity
        Message msg = obtainMessage(BusHandler.PING);
        Bundle data = new Bundle();
        data.putString("groupName", lastJoinedNetwork);
        data.putString("pingString", identity);
        data.putSerializable("type", Type.IDENTITY);
        msg.setData(data);
        sendMessage(msg);
    }

    /**
     * Helper method called when we received the identity of the sender of the salt message
     * in order to check if the signature was correct
     */
    private void verifySignatureSalt() {
        AllJoynMessage message = signatureVerificationTask;

        boolean verification = message.verifyMessage(identityMap.get(message.getSender()).getPublicKey());//messageAuthenticater.verifySignature(identityMap.get(message.getSender()).getPublicKey(), Base64.decode(message.getSignature(),Base64.DEFAULT), message.getMessage().getBytes());
        if (verification) {
            //everything OK
            Log.d(TAG, "Signature of earlyier received message was OK");
            return;
        } else {
            //when verification failed
            //signature of salt message was false
            //=> reinit salt
            messageEncrypter.reset();
            Log.e(TAG, "Signature of salt message was incorrect");
        }
    }

}