diff --git a/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java b/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java
index 00609e4eba225a85ce8419d28d33c00e76a8f69f..18a6d5eb438981b0dd3a3731e787a37c106fa5e1 100644
--- a/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java
+++ b/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java
@@ -72,6 +72,7 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 	private LocalAuthor author0, author1;
 	private PrivateGroup privateGroup0;
 	private GroupId groupId0;
+	private GroupMessage newMemberMsg0;
 
 	@Inject
 	Clock clock;
@@ -221,6 +222,20 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 
 		// assert that message did not arrive
 		assertEquals(2, groupManager1.getHeaders(groupId0).size());
+
+		// create and add test message with previousMsgId of newMemberMsg
+		previousMsgId = newMemberMsg0.getMessage().getId();
+		msg = groupMessageFactory
+				.createGroupMessage(groupId0, clock.currentTimeMillis(), null,
+						author0, "test", previousMsgId);
+		groupManager0.addLocalMessage(msg);
+
+		// sync test message
+		sync0To1();
+		validationWaiter.await(TIMEOUT, 1);
+
+		// assert that message did not arrive
+		assertEquals(2, groupManager1.getHeaders(groupId0).size());
 	}
 
 	@Test
@@ -437,13 +452,13 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 	private void addGroup() throws Exception {
 		// author0 joins privateGroup0
 		long joinTime = clock.currentTimeMillis();
-		GroupMessage newMemberMsg = groupMessageFactory
+		newMemberMsg0 = groupMessageFactory
 				.createNewMemberMessage(privateGroup0.getId(), joinTime,
 						author0, author0);
 		GroupMessage joinMsg = groupMessageFactory
 				.createJoinMessage(privateGroup0.getId(), joinTime, author0,
-						newMemberMsg.getMessage().getId());
-		groupManager0.addPrivateGroup(privateGroup0, newMemberMsg, joinMsg);
+						newMemberMsg0.getMessage().getId());
+		groupManager0.addPrivateGroup(privateGroup0, newMemberMsg0, joinMsg);
 		assertEquals(joinMsg.getMessage().getId(),
 				groupManager0.getPreviousMsgId(groupId0));
 
@@ -457,13 +472,13 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 
 		// author1 joins privateGroup0
 		joinTime = clock.currentTimeMillis();
-		newMemberMsg = groupMessageFactory
+		GroupMessage newMemberMsg1 = groupMessageFactory
 				.createNewMemberMessage(privateGroup0.getId(), joinTime,
 						author0, author1);
 		joinMsg = groupMessageFactory
 				.createJoinMessage(privateGroup0.getId(), joinTime, author1,
-						newMemberMsg.getMessage().getId());
-		groupManager1.addPrivateGroup(privateGroup0, newMemberMsg, joinMsg);
+						newMemberMsg1.getMessage().getId());
+		groupManager1.addPrivateGroup(privateGroup0, newMemberMsg1, joinMsg);
 		assertEquals(joinMsg.getMessage().getId(),
 				groupManager1.getPreviousMsgId(groupId0));
 
diff --git a/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTestComponent.java b/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTestComponent.java
index 7461eb02bcf81e54fe21b008600b587b7759dc36..68191f4f87b7099a9723749fb5d12184a050ab6a 100644
--- a/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTestComponent.java
+++ b/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTestComponent.java
@@ -15,6 +15,7 @@ import org.briarproject.db.DatabaseModule;
 import org.briarproject.event.EventModule;
 import org.briarproject.identity.IdentityModule;
 import org.briarproject.lifecycle.LifecycleModule;
+import org.briarproject.messaging.MessagingModule;
 import org.briarproject.privategroup.PrivateGroupModule;
 import org.briarproject.properties.PropertiesModule;
 import org.briarproject.sharing.SharingModule;
@@ -37,6 +38,7 @@ import dagger.Component;
 		DataModule.class,
 		DatabaseModule.class,
 		EventModule.class,
+		MessagingModule.class,
 		PrivateGroupModule.class,
 		IdentityModule.class,
 		LifecycleModule.class,
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
index 8d61bbff83dd964ed230113e0053f7b4f1027279..abbf10d423508e41b7459f74f389e48f9ea67b2d 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
@@ -2,7 +2,6 @@ package org.briarproject.android.privategroup.conversation;
 
 import android.support.annotation.Nullable;
 
-import org.briarproject.R;
 import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.controller.handler.ResultExceptionHandler;
 import org.briarproject.android.threaded.ThreadListControllerImpl;
@@ -95,8 +94,8 @@ public class GroupControllerImpl extends
 	protected String loadMessageBody(GroupMessageHeader header)
 			throws DbException {
 		if (header instanceof JoinMessageHeader) {
-			return listener.getApplicationContext()
-					.getString(R.string.groups_member_joined);
+			// will be looked up later
+			return "";
 		}
 		return privateGroupManager.getMessageBody(header.getId());
 	}
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageAdapter.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageAdapter.java
index 2a9c75a009f1d4e2c79851713dc1256a2ae74236..58eec29385fc37b8a7e63a38fd626c7970e15c03 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageAdapter.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageAdapter.java
@@ -1,5 +1,6 @@
 package org.briarproject.android.privategroup.conversation;
 
+import android.support.annotation.LayoutRes;
 import android.support.annotation.UiThread;
 import android.support.v7.widget.LinearLayoutManager;
 import android.view.LayoutInflater;
@@ -19,12 +20,11 @@ public class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
 		super(listener, layoutManager);
 	}
 
+	@LayoutRes
 	@Override
 	public int getItemViewType(int position) {
 		GroupMessageItem item = getVisibleItem(position);
-		if (item instanceof JoinMessageItem) {
-			return R.layout.list_item_thread_notice;
-		}
+		if (item != null) return item.getLayout();
 		return R.layout.list_item_thread;
 	}
 
@@ -34,7 +34,7 @@ public class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
 		View v = LayoutInflater.from(parent.getContext())
 				.inflate(type, parent, false);
 		if (type == R.layout.list_item_thread_notice) {
-			return new BaseThreadItemViewHolder<>(v);
+			return new JoinMessageItemHolder(v);
 		}
 		return new ThreadItemViewHolder<>(v);
 	}
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageItem.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageItem.java
index b961fd3f6bd5d34d3c30b4b280015a74dd78f3d5..9deb0424e574fc582b56dc687246c3f74d3438fb 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageItem.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageItem.java
@@ -1,11 +1,19 @@
 package org.briarproject.android.privategroup.conversation;
 
+import android.support.annotation.LayoutRes;
+import android.support.annotation.UiThread;
+
+import org.briarproject.R;
 import org.briarproject.android.threaded.ThreadItem;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.Author.Status;
 import org.briarproject.api.privategroup.GroupMessageHeader;
 import org.briarproject.api.sync.MessageId;
 
+import javax.annotation.concurrent.NotThreadSafe;
+
+@UiThread
+@NotThreadSafe
 class GroupMessageItem extends ThreadItem {
 
 	private GroupMessageItem(MessageId messageId, MessageId parentId,
@@ -19,4 +27,9 @@ class GroupMessageItem extends ThreadItem {
 				h.getAuthorStatus(), h.isRead());
 	}
 
+	@LayoutRes
+	public int getLayout() {
+		return R.layout.list_item_thread;
+	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItem.java b/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItem.java
index e21399127a16c29b07449b15f159da11f08ffa3c..44c732ffd48970cc59a4abd2a92e4bebac232219 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItem.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItem.java
@@ -1,7 +1,15 @@
 package org.briarproject.android.privategroup.conversation;
 
+import android.support.annotation.LayoutRes;
+import android.support.annotation.UiThread;
+
+import org.briarproject.R;
 import org.briarproject.api.privategroup.GroupMessageHeader;
 
+import javax.annotation.concurrent.NotThreadSafe;
+
+@UiThread
+@NotThreadSafe
 class JoinMessageItem extends GroupMessageItem {
 
 	JoinMessageItem(GroupMessageHeader h,
@@ -19,4 +27,9 @@ class JoinMessageItem extends GroupMessageItem {
 		return false;
 	}
 
+	@LayoutRes
+	public int getLayout() {
+		return R.layout.list_item_thread_notice;
+	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItemHolder.java b/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItemHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..8a2942a4c46f44a442df7f1e7214d118752dca3e
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItemHolder.java
@@ -0,0 +1,30 @@
+package org.briarproject.android.privategroup.conversation;
+
+import android.support.annotation.UiThread;
+import android.view.View;
+
+import org.briarproject.R;
+import org.briarproject.android.threaded.BaseThreadItemViewHolder;
+import org.briarproject.android.threaded.ThreadItemAdapter;
+import org.briarproject.android.threaded.ThreadItemAdapter.ThreadItemListener;
+import org.briarproject.api.nullsafety.NotNullByDefault;
+
+@UiThread
+@NotNullByDefault
+public class JoinMessageItemHolder
+		extends BaseThreadItemViewHolder<GroupMessageItem> {
+
+	public JoinMessageItemHolder(View v) {
+		super(v);
+	}
+
+	@Override
+	public void bind(final ThreadItemAdapter<GroupMessageItem> adapter,
+			final ThreadItemListener<GroupMessageItem> listener,
+			final GroupMessageItem item, int pos) {
+		super.bind(adapter, listener, item, pos);
+
+		textView.setText(getContext().getString(R.string.groups_member_joined));
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/threaded/BaseThreadItemViewHolder.java b/briar-android/src/org/briarproject/android/threaded/BaseThreadItemViewHolder.java
index 47ed3502fa1dbb7b1b546c5167864273784f524a..af8e800b33ed2d308198ffc58a914279a10abea2 100644
--- a/briar-android/src/org/briarproject/android/threaded/BaseThreadItemViewHolder.java
+++ b/briar-android/src/org/briarproject/android/threaded/BaseThreadItemViewHolder.java
@@ -21,13 +21,13 @@ import org.briarproject.util.StringUtils;
 
 @UiThread
 @NotNullByDefault
-public class BaseThreadItemViewHolder<I extends ThreadItem>
+public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
 		extends RecyclerView.ViewHolder {
 
 	private final static int ANIMATION_DURATION = 5000;
 
+	protected final TextView textView;
 	private final ViewGroup layout;
-	private final TextView textView;
 	private final AuthorView author;
 	private final View topDivider;
 
diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadItem.java b/briar-android/src/org/briarproject/android/threaded/ThreadItem.java
index c4281e9ac1a5ea1b4f96739403151ea868fa738d..e4c055e2b39a31c228ff72c77964d76dd48a5ce2 100644
--- a/briar-android/src/org/briarproject/android/threaded/ThreadItem.java
+++ b/briar-android/src/org/briarproject/android/threaded/ThreadItem.java
@@ -1,7 +1,5 @@
 package org.briarproject.android.threaded;
 
-import android.support.annotation.UiThread;
-
 import org.briarproject.api.clients.MessageTree.MessageNode;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.Author.Status;
@@ -11,7 +9,6 @@ import javax.annotation.concurrent.NotThreadSafe;
 
 import static org.briarproject.android.threaded.ThreadItemAdapter.UNDEFINED;
 
-@UiThread
 @NotThreadSafe
 public abstract class ThreadItem implements MessageNode {
 
@@ -97,4 +94,5 @@ public abstract class ThreadItem implements MessageNode {
 	public void setDescendantCount(int descendantCount) {
 		this.descendantCount = descendantCount;
 	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadItemAdapter.java b/briar-android/src/org/briarproject/android/threaded/ThreadItemAdapter.java
index 9e93f8f0714cf10635060ff28213e154e646c9be..21425ced3815804046343b2c9fb8bd404a9a3994 100644
--- a/briar-android/src/org/briarproject/android/threaded/ThreadItemAdapter.java
+++ b/briar-android/src/org/briarproject/android/threaded/ThreadItemAdapter.java
@@ -316,7 +316,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
 		revision++;
 	}
 
-	protected interface ThreadItemListener<I> {
+	public interface ThreadItemListener<I> {
 
 		void onItemVisible(I item);
 
diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadListController.java b/briar-android/src/org/briarproject/android/threaded/ThreadListController.java
index 43ce1d5c283dbe154b17e66976fef62451a4ee02..f2e7570a820d89f66cf7e3452d74b3a1a4256514 100644
--- a/briar-android/src/org/briarproject/android/threaded/ThreadListController.java
+++ b/briar-android/src/org/briarproject/android/threaded/ThreadListController.java
@@ -1,6 +1,5 @@
 package org.briarproject.android.threaded;
 
-import android.content.Context;
 import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
 
@@ -40,8 +39,6 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
 
 		@UiThread
 		void onGroupRemoved();
-
-		Context getApplicationContext();
 	}
 
 }
diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java b/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java
index cf6ffb8d6cfc33657d7b0905da404e9e78a19958..6ea72c892357f669bc1fcd8d322ddca3b4b787cc 100644
--- a/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java
@@ -42,7 +42,6 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
 			Logger.getLogger(ThreadListControllerImpl.class.getName());
 
 	protected final IdentityManager identityManager;
-	@CryptoExecutor
 	protected final Executor cryptoExecutor;
 	protected final AndroidNotificationManager notificationManager;
 	protected final Clock clock;
diff --git a/briar-api/src/org/briarproject/api/clients/ClientHelper.java b/briar-api/src/org/briarproject/api/clients/ClientHelper.java
index 4622431d407532206c8ba19499bd09e0062930a8..b24c9d2b715456a4137bef4246209bdc5853a741 100644
--- a/briar-api/src/org/briarproject/api/clients/ClientHelper.java
+++ b/briar-api/src/org/briarproject/api/clients/ClientHelper.java
@@ -84,6 +84,6 @@ public interface ClientHelper {
 			throws FormatException, GeneralSecurityException;
 
 	void verifySignature(byte[] sig, byte[] publicKey, BdfList signed)
-			throws InvalidMessageException;
+			throws FormatException, GeneralSecurityException;
 
 }
diff --git a/briar-api/src/org/briarproject/api/privategroup/MessageType.java b/briar-api/src/org/briarproject/api/privategroup/MessageType.java
index afe6a07d1c3dd17b37fe7e4498b45b3fb3e06402..7cffb0df312973ac3b4955547037782aa7a294fe 100644
--- a/briar-api/src/org/briarproject/api/privategroup/MessageType.java
+++ b/briar-api/src/org/briarproject/api/privategroup/MessageType.java
@@ -12,19 +12,11 @@ public enum MessageType {
 	}
 
 	public static MessageType valueOf(int value) {
-		switch (value) {
-			case 0:
-				return NEW_MEMBER;
-			case 1:
-				return JOIN;
-			case 2:
-				return POST;
-			default:
-				throw new IllegalArgumentException();
-		}
+		for (MessageType m : values()) if (m.value == value) return m;
+		throw new IllegalArgumentException();
 	}
 
 	public int getInt() {
 		return value;
 	}
-}
\ No newline at end of file
+}
diff --git a/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java b/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java
index b01cd88c7f6d03c41d2593aebe5229aa326850fe..49bee81f8788f1edacda917d4d0244544fe001e8 100644
--- a/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java
+++ b/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java
@@ -18,8 +18,9 @@ public interface PrivateGroupManager extends MessageTracker {
 	 * Adds a new private group and joins it.
 	 *
 	 * @param group        The private group to add
-	 * @param newMemberMsg The creator's message announcing the first new member
-	 * @param joinMsg      The first new member's join message
+	 * @param newMemberMsg The creator's message announcing herself as
+	 *                     first new member
+	 * @param joinMsg      The creator's own join message
 	 */
 	void addPrivateGroup(PrivateGroup group, GroupMessage newMemberMsg,
 			GroupMessage joinMsg) throws DbException;
@@ -27,10 +28,11 @@ public interface PrivateGroupManager extends MessageTracker {
 	/** Removes a dissolved private group. */
 	void removePrivateGroup(GroupId g) throws DbException;
 
-	/** Gets the MessageId of the  */
+	/** Gets the MessageId of your previous message sent to the group */
 	MessageId getPreviousMsgId(GroupId g) throws DbException;
 
 	/** Returns the timestamp of the message with the given ID */
+	// TODO change to getPreviousMessageHeader()
 	long getMessageTimestamp(MessageId id) throws DbException;
 
 	/** Stores (and sends) a local group message. */
diff --git a/briar-core/src/org/briarproject/blogs/BlogPostValidator.java b/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
index 75b4797d0a92ab3e78d3cd4a6574ad9988e85e01..cd2347bc65ea79d8b3110ff28b556fab4a169eb4 100644
--- a/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
+++ b/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
@@ -20,6 +20,7 @@ import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.system.Clock;
 import org.briarproject.clients.BdfMessageValidator;
 
+import java.security.GeneralSecurityException;
 import java.util.Collection;
 import java.util.Collections;
 
@@ -101,7 +102,11 @@ class BlogPostValidator extends BdfMessageValidator {
 		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), postBody);
 		Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
 		Author a = b.getAuthor();
-		clientHelper.verifySignature(sig, a.getPublicKey(), signed);
+		try {
+			clientHelper.verifySignature(sig, a.getPublicKey(), signed);
+		} catch (GeneralSecurityException e) {
+			throw new InvalidMessageException(e);
+		}
 
 		// Return the metadata and dependencies
 		BdfDictionary meta = new BdfDictionary();
@@ -142,7 +147,11 @@ class BlogPostValidator extends BdfMessageValidator {
 						currentId);
 		Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
 		Author a = b.getAuthor();
-		clientHelper.verifySignature(sig, a.getPublicKey(), signed);
+		try {
+			clientHelper.verifySignature(sig, a.getPublicKey(), signed);
+		} catch (GeneralSecurityException e) {
+			throw new InvalidMessageException(e);
+		}
 
 		// Return the metadata and dependencies
 		BdfDictionary meta = new BdfDictionary();
diff --git a/briar-core/src/org/briarproject/clients/ClientHelperImpl.java b/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
index 612f80e815280b7276bd5c07dc7ced684307e314..9c0354419a0250f0ae85c122e7da4519d99c1f48 100644
--- a/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
+++ b/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
@@ -20,7 +20,6 @@ import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Metadata;
 import org.briarproject.api.db.Transaction;
 import org.briarproject.api.sync.GroupId;
-import org.briarproject.api.sync.InvalidMessageException;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageFactory;
 import org.briarproject.api.sync.MessageId;
@@ -325,22 +324,16 @@ class ClientHelperImpl implements ClientHelper {
 
 	@Override
 	public void verifySignature(byte[] sig, byte[] publicKey, BdfList signed)
-			throws InvalidMessageException {
-		try {
-			// Parse the public key
-			KeyParser keyParser = cryptoComponent.getSignatureKeyParser();
-			PublicKey key = keyParser.parsePublicKey(publicKey);
-			// Verify the signature
-			Signature signature = cryptoComponent.getSignature();
-			signature.initVerify(key);
-			signature.update(toByteArray(signed));
-			if (!signature.verify(sig)) {
-				throw new InvalidMessageException("Invalid signature");
-			}
-		} catch (GeneralSecurityException e) {
-			throw new InvalidMessageException("Invalid public key");
-		} catch (FormatException e) {
-			throw new InvalidMessageException(e);
+			throws FormatException, GeneralSecurityException {
+		// Parse the public key
+		KeyParser keyParser = cryptoComponent.getSignatureKeyParser();
+		PublicKey key = keyParser.parsePublicKey(publicKey);
+		// Verify the signature
+		Signature signature = cryptoComponent.getSignature();
+		signature.initVerify(key);
+		signature.update(toByteArray(signed));
+		if (!signature.verify(sig)) {
+			throw new GeneralSecurityException("Invalid signature");
 		}
 	}
 
diff --git a/briar-core/src/org/briarproject/forum/ForumPostValidator.java b/briar-core/src/org/briarproject/forum/ForumPostValidator.java
index f25181f1ce69032ada72bdfcba2691124d148d8f..ce4bddb4364212326571fdce95d9dee020749dfc 100644
--- a/briar-core/src/org/briarproject/forum/ForumPostValidator.java
+++ b/briar-core/src/org/briarproject/forum/ForumPostValidator.java
@@ -16,6 +16,7 @@ import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.system.Clock;
 import org.briarproject.clients.BdfMessageValidator;
 
+import java.security.GeneralSecurityException;
 import java.util.Collection;
 import java.util.Collections;
 
@@ -73,10 +74,15 @@ class ForumPostValidator extends BdfMessageValidator {
 		}
 		// Verify the signature, if any
 		if (author != null) {
-			// Serialise the data to be signed
+			// Serialise the data to be verified
 			BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), parent,
 					authorList, contentType, forumPostBody);
-			clientHelper.verifySignature(sig, author.getPublicKey(), signed);
+			try {
+				clientHelper
+						.verifySignature(sig, author.getPublicKey(), signed);
+			} catch (GeneralSecurityException e) {
+				throw new InvalidMessageException(e);
+			}
 		}
 		// Return the metadata and dependencies
 		BdfDictionary meta = new BdfDictionary();
diff --git a/briar-core/src/org/briarproject/privategroup/Constants.java b/briar-core/src/org/briarproject/privategroup/Constants.java
index 3c83d232618f074ac612e095eba4772aebf9bd95..12219f78d789193398edea716259c41cba7f7847 100644
--- a/briar-core/src/org/briarproject/privategroup/Constants.java
+++ b/briar-core/src/org/briarproject/privategroup/Constants.java
@@ -11,8 +11,8 @@ interface Constants {
 	String KEY_PARENT_MSG_ID = "parentMsgId";
 	String KEY_NEW_MEMBER_MSG_ID = "newMemberMsgId";
 	String KEY_PREVIOUS_MSG_ID = "previousMsgId";
-	String KEY_AUTHOR_ID = "authorId";
-	String KEY_AUTHOR_NAME = "authorName";
-	String KEY_AUTHOR_PUBLIC_KEY = "authorPublicKey";
+	String KEY_MEMBER_ID = "memberId";
+	String KEY_MEMBER_NAME = "memberName";
+	String KEY_MEMBER_PUBLIC_KEY = "memberPublicKey";
 
 }
diff --git a/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java b/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java
index 4569bc50d5d052f886e609b13f49c7d141ff61a1..228b6425448958ae1ebaee41e372160b8092a078 100644
--- a/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java
+++ b/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java
@@ -36,14 +36,15 @@ class GroupMessageFactoryImpl implements GroupMessageFactory {
 			LocalAuthor creator, Author member) {
 		try {
 			// Generate the signature
-			BdfList toSign = BdfList.of(groupId, timestamp, member.getName(),
-					member.getPublicKey());
+			int type = NEW_MEMBER.getInt();
+			BdfList toSign = BdfList.of(groupId, timestamp, type,
+					member.getName(), member.getPublicKey());
 			byte[] signature =
 					clientHelper.sign(toSign, creator.getPrivateKey());
 
 			// Compose the message
 			BdfList body =
-					BdfList.of(NEW_MEMBER.getInt(), member.getName(),
+					BdfList.of(type, member.getName(),
 							member.getPublicKey(), signature);
 			Message m = clientHelper.createMessage(groupId, timestamp, body);
 
@@ -60,14 +61,15 @@ class GroupMessageFactoryImpl implements GroupMessageFactory {
 			LocalAuthor member, MessageId newMemberId) {
 		try {
 			// Generate the signature
-			BdfList toSign = BdfList.of(groupId, timestamp, member.getName(),
-					member.getPublicKey(), newMemberId);
+			int type = JOIN.getInt();
+			BdfList toSign = BdfList.of(groupId, timestamp, type,
+					member.getName(), member.getPublicKey(), newMemberId);
 			byte[] signature =
 					clientHelper.sign(toSign, member.getPrivateKey());
 
 			// Compose the message
 			BdfList body =
-					BdfList.of(JOIN.getInt(), member.getName(),
+					BdfList.of(type, member.getName(),
 							member.getPublicKey(), newMemberId, signature);
 			Message m = clientHelper.createMessage(groupId, timestamp, body);
 
@@ -85,14 +87,16 @@ class GroupMessageFactoryImpl implements GroupMessageFactory {
 			MessageId previousMsgId) {
 		try {
 			// Generate the signature
-			BdfList toSign = BdfList.of(groupId, timestamp, author.getName(),
-					author.getPublicKey(), parentId, previousMsgId, content);
+			int type = POST.getInt();
+			BdfList toSign = BdfList.of(groupId, timestamp, type,
+					author.getName(), author.getPublicKey(), parentId,
+					previousMsgId, content);
 			byte[] signature =
 					clientHelper.sign(toSign, author.getPrivateKey());
 
 			// Compose the message
 			BdfList body =
-					BdfList.of(POST.getInt(), author.getName(),
+					BdfList.of(type, author.getName(),
 							author.getPublicKey(), parentId, previousMsgId,
 							content, signature);
 			Message m = clientHelper.createMessage(groupId, timestamp, body);
diff --git a/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java b/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
index 01385793966e2ad89f6321c4a84edb67909c237b..25bf14730cc4b988b91728de14f9ef10fa22f2f7 100644
--- a/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
+++ b/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
@@ -18,6 +18,7 @@ import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.system.Clock;
 import org.briarproject.clients.BdfMessageValidator;
 
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -25,10 +26,13 @@ import java.util.Collections;
 import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
+import static org.briarproject.api.privategroup.MessageType.JOIN;
+import static org.briarproject.api.privategroup.MessageType.NEW_MEMBER;
+import static org.briarproject.api.privategroup.MessageType.POST;
 import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_POST_BODY_LENGTH;
-import static org.briarproject.privategroup.Constants.KEY_AUTHOR_ID;
-import static org.briarproject.privategroup.Constants.KEY_AUTHOR_NAME;
-import static org.briarproject.privategroup.Constants.KEY_AUTHOR_PUBLIC_KEY;
+import static org.briarproject.privategroup.Constants.KEY_MEMBER_ID;
+import static org.briarproject.privategroup.Constants.KEY_MEMBER_NAME;
+import static org.briarproject.privategroup.Constants.KEY_MEMBER_PUBLIC_KEY;
 import static org.briarproject.privategroup.Constants.KEY_NEW_MEMBER_MSG_ID;
 import static org.briarproject.privategroup.Constants.KEY_PARENT_MSG_ID;
 import static org.briarproject.privategroup.Constants.KEY_PREVIOUS_MSG_ID;
@@ -60,29 +64,29 @@ class GroupMessageValidator extends BdfMessageValidator {
 		body.removeElementAt(0);
 
 		// member_name (string)
-		String member_name = body.getString(0);
-		checkLength(member_name, 1, MAX_AUTHOR_NAME_LENGTH);
+		String memberName = body.getString(0);
+		checkLength(memberName, 1, MAX_AUTHOR_NAME_LENGTH);
 
 		// member_public_key (raw)
-		byte[] member_public_key = body.getRaw(1);
-		checkLength(member_public_key, 1, MAX_PUBLIC_KEY_LENGTH);
+		byte[] memberPublicKey = body.getRaw(1);
+		checkLength(memberPublicKey, 1, MAX_PUBLIC_KEY_LENGTH);
 
 		BdfMessageContext c;
 		switch (MessageType.valueOf(type)) {
 			case NEW_MEMBER:
-				c = validateNewMember(m, g, body, member_name,
-						member_public_key);
-				addMessageMetadata(c, member_name, member_public_key,
+				c = validateNewMember(m, g, body, memberName,
+						memberPublicKey);
+				addMessageMetadata(c, memberName, memberPublicKey,
 						m.getTimestamp());
 				break;
 			case JOIN:
-				c = validateJoin(m, g, body, member_name, member_public_key);
-				addMessageMetadata(c, member_name, member_public_key,
+				c = validateJoin(m, g, body, memberName, memberPublicKey);
+				addMessageMetadata(c, memberName, memberPublicKey,
 						m.getTimestamp());
 				break;
 			case POST:
-				c = validatePost(m, g, body, member_name, member_public_key);
-				addMessageMetadata(c, member_name, member_public_key,
+				c = validatePost(m, g, body, memberName, memberPublicKey);
+				addMessageMetadata(c, memberName, memberPublicKey,
 						m.getTimestamp());
 				break;
 			default:
@@ -93,7 +97,7 @@ class GroupMessageValidator extends BdfMessageValidator {
 	}
 
 	private BdfMessageContext validateNewMember(Message m, Group g,
-			BdfList body, String member_name, byte[] member_public_key)
+			BdfList body, String memberName, byte[] memberPublicKey)
 			throws InvalidMessageException, FormatException {
 
 		// The content is a BDF list with three elements
@@ -105,11 +109,16 @@ class GroupMessageValidator extends BdfMessageValidator {
 		checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
 
 		// Verify Signature
-		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), member_name,
-				member_public_key);
+		BdfList signed =
+				BdfList.of(g.getId(), m.getTimestamp(), NEW_MEMBER.getInt(),
+						memberName, memberPublicKey);
 		PrivateGroup group = groupFactory.parsePrivateGroup(g);
 		byte[] creatorPublicKey = group.getAuthor().getPublicKey();
-		clientHelper.verifySignature(signature, creatorPublicKey, signed);
+		try {
+			clientHelper.verifySignature(signature, creatorPublicKey, signed);
+		} catch (GeneralSecurityException e) {
+			throw new InvalidMessageException(e);
+		}
 
 		// Return the metadata and no dependencies
 		BdfDictionary meta = new BdfDictionary();
@@ -117,7 +126,7 @@ class GroupMessageValidator extends BdfMessageValidator {
 	}
 
 	private BdfMessageContext validateJoin(Message m, Group g, BdfList body,
-			String member_name, byte[] member_public_key)
+			String memberName, byte[] memberPublicKey)
 			throws InvalidMessageException, FormatException {
 
 		// The content is a BDF list with four elements
@@ -126,8 +135,8 @@ class GroupMessageValidator extends BdfMessageValidator {
 		// new_member_id (raw)
 		// the identifier of a new member message
 		// with the same member_name and member_public_key
-		byte[] new_member_id = body.getRaw(2);
-		checkLength(new_member_id, MessageId.LENGTH);
+		byte[] newMemberId = body.getRaw(2);
+		checkLength(newMemberId, MessageId.LENGTH);
 
 		// signature (raw)
 		// a signature with the member's private key over a list with 5 elements
@@ -135,22 +144,26 @@ class GroupMessageValidator extends BdfMessageValidator {
 		checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
 
 		// Verify Signature
-		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), member_name,
-				member_public_key, new_member_id);
-		clientHelper.verifySignature(signature, member_public_key, signed);
+		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), JOIN.getInt(),
+				memberName, memberPublicKey, newMemberId);
+		try {
+			clientHelper.verifySignature(signature, memberPublicKey, signed);
+		} catch (GeneralSecurityException e) {
+			throw new InvalidMessageException(e);
+		}
 
 		// The new member message is a dependency
 		Collection<MessageId> dependencies =
-				Collections.singleton(new MessageId(new_member_id));
+				Collections.singleton(new MessageId(newMemberId));
 
 		// Return the metadata and dependencies
 		BdfDictionary meta = new BdfDictionary();
-		meta.put(KEY_NEW_MEMBER_MSG_ID, new_member_id);
+		meta.put(KEY_NEW_MEMBER_MSG_ID, newMemberId);
 		return new BdfMessageContext(meta, dependencies);
 	}
 
 	private BdfMessageContext validatePost(Message m, Group g, BdfList body,
-			String member_name, byte[] member_public_key)
+			String memberName, byte[] memberPublicKey)
 			throws InvalidMessageException, FormatException {
 
 		// The content is a BDF list with six elements
@@ -158,15 +171,13 @@ class GroupMessageValidator extends BdfMessageValidator {
 
 		// parent_id (raw or null)
 		// the identifier of the post to which this is a reply, if any
-		byte[] parent_id = body.getOptionalRaw(2);
-		if (parent_id != null) {
-			checkLength(parent_id, MessageId.LENGTH);
-		}
+		byte[] parentId = body.getOptionalRaw(2);
+		checkLength(parentId, MessageId.LENGTH);
 
 		// previous_message_id (raw)
 		// the identifier of the member's previous post or join message
-		byte[] previous_message_id = body.getRaw(3);
-		checkLength(previous_message_id, MessageId.LENGTH);
+		byte[] previousMessageId = body.getRaw(3);
+		checkLength(previousMessageId, MessageId.LENGTH);
 
 		// content (string)
 		String content = body.getString(4);
@@ -178,20 +189,25 @@ class GroupMessageValidator extends BdfMessageValidator {
 		checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
 
 		// Verify Signature
-		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), member_name,
-				member_public_key, parent_id, previous_message_id, content);
-		clientHelper.verifySignature(signature, member_public_key, signed);
+		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), POST.getInt(),
+				memberName, memberPublicKey, parentId, previousMessageId,
+				content);
+		try {
+			clientHelper.verifySignature(signature, memberPublicKey, signed);
+		} catch (GeneralSecurityException e) {
+			throw new InvalidMessageException(e);
+		}
 
 		// The parent post, if any,
 		// and the member's previous message are dependencies
 		Collection<MessageId> dependencies = new ArrayList<MessageId>();
-		if (parent_id != null) dependencies.add(new MessageId(parent_id));
-		dependencies.add(new MessageId(previous_message_id));
+		if (parentId != null) dependencies.add(new MessageId(parentId));
+		dependencies.add(new MessageId(previousMessageId));
 
 		// Return the metadata and dependencies
 		BdfDictionary meta = new BdfDictionary();
-		if (parent_id != null) meta.put(KEY_PARENT_MSG_ID, parent_id);
-		meta.put(KEY_PREVIOUS_MSG_ID, previous_message_id);
+		if (parentId != null) meta.put(KEY_PARENT_MSG_ID, parentId);
+		meta.put(KEY_PREVIOUS_MSG_ID, previousMessageId);
 		return new BdfMessageContext(meta, dependencies);
 	}
 
@@ -200,9 +216,9 @@ class GroupMessageValidator extends BdfMessageValidator {
 		c.getDictionary().put(KEY_TIMESTAMP, time);
 		c.getDictionary().put(KEY_READ, false);
 		Author a = authorFactory.createAuthor(authorName, pubKey);
-		c.getDictionary().put(KEY_AUTHOR_ID, a.getId());
-		c.getDictionary().put(KEY_AUTHOR_NAME, authorName);
-		c.getDictionary().put(KEY_AUTHOR_PUBLIC_KEY, pubKey);
+		c.getDictionary().put(KEY_MEMBER_ID, a.getId());
+		c.getDictionary().put(KEY_MEMBER_NAME, authorName);
+		c.getDictionary().put(KEY_MEMBER_PUBLIC_KEY, pubKey);
 	}
 
 }
diff --git a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
index eb3a888f0406ddd475772b8efafd74946911d4b6..769e065b87a200cd8e80b2cbcc64bb4c42fe5437 100644
--- a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
+++ b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
@@ -44,9 +44,9 @@ import static org.briarproject.api.identity.Author.Status.OURSELVES;
 import static org.briarproject.api.privategroup.MessageType.JOIN;
 import static org.briarproject.api.privategroup.MessageType.NEW_MEMBER;
 import static org.briarproject.api.privategroup.MessageType.POST;
-import static org.briarproject.privategroup.Constants.KEY_AUTHOR_ID;
-import static org.briarproject.privategroup.Constants.KEY_AUTHOR_NAME;
-import static org.briarproject.privategroup.Constants.KEY_AUTHOR_PUBLIC_KEY;
+import static org.briarproject.privategroup.Constants.KEY_MEMBER_ID;
+import static org.briarproject.privategroup.Constants.KEY_MEMBER_NAME;
+import static org.briarproject.privategroup.Constants.KEY_MEMBER_PUBLIC_KEY;
 import static org.briarproject.privategroup.Constants.KEY_NEW_MEMBER_MSG_ID;
 import static org.briarproject.privategroup.Constants.KEY_PARENT_MSG_ID;
 import static org.briarproject.privategroup.Constants.KEY_PREVIOUS_MSG_ID;
@@ -141,8 +141,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 	private MessageId getPreviousMsgId(Transaction txn, GroupId g)
 			throws DbException, FormatException {
 		BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g);
-		byte[] previousMsgIdBytes = d.getOptionalRaw(KEY_PREVIOUS_MSG_ID);
-		if (previousMsgIdBytes == null) throw new DbException();
+		byte[] previousMsgIdBytes = d.getRaw(KEY_PREVIOUS_MSG_ID);
 		return new MessageId(previousMsgIdBytes);
 	}
 
@@ -191,9 +190,9 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 			boolean read) {
 		meta.put(KEY_TIMESTAMP, m.getMessage().getTimestamp());
 		meta.put(KEY_READ, read);
-		meta.put(KEY_AUTHOR_ID, m.getMember().getId());
-		meta.put(KEY_AUTHOR_NAME, m.getMember().getName());
-		meta.put(KEY_AUTHOR_PUBLIC_KEY, m.getMember().getPublicKey());
+		meta.put(KEY_MEMBER_ID, m.getMember().getId());
+		meta.put(KEY_MEMBER_NAME, m.getMember().getName());
+		meta.put(KEY_MEMBER_PUBLIC_KEY, m.getMember().getPublicKey());
 	}
 
 	@Override
@@ -269,11 +268,10 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 					clientHelper.getMessageMetadataAsDictionary(txn, g);
 			// get all authors we need to get the status for
 			Set<AuthorId> authors = new HashSet<AuthorId>();
-			for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
-				BdfDictionary meta = entry.getValue();
+			for (BdfDictionary meta : metadata.values()) {
 				if (meta.getLong(KEY_TYPE) == NEW_MEMBER.getInt())
 					continue;
-				byte[] idBytes = meta.getRaw(KEY_AUTHOR_ID);
+				byte[] idBytes = meta.getRaw(KEY_MEMBER_ID);
 				authors.add(new AuthorId(idBytes));
 			}
 			// get statuses for all authors
@@ -308,9 +306,9 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 		}
 		long timestamp = meta.getLong(KEY_TIMESTAMP);
 
-		AuthorId authorId = new AuthorId(meta.getRaw(KEY_AUTHOR_ID));
-		String name = meta.getString(KEY_AUTHOR_NAME);
-		byte[] publicKey = meta.getRaw(KEY_AUTHOR_PUBLIC_KEY);
+		AuthorId authorId = new AuthorId(meta.getRaw(KEY_MEMBER_ID));
+		String name = meta.getString(KEY_MEMBER_NAME);
+		byte[] publicKey = meta.getRaw(KEY_MEMBER_PUBLIC_KEY);
 		Author author = new Author(authorId, name, publicKey);
 
 		Status status;
@@ -361,8 +359,8 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 					return false;
 				}
 				// NEW_MEMBER must have same member_name and member_public_key
-				if (!Arrays.equals(meta.getRaw(KEY_AUTHOR_ID),
-						newMemberMeta.getRaw(KEY_AUTHOR_ID))) {
+				if (!Arrays.equals(meta.getRaw(KEY_MEMBER_ID),
+						newMemberMeta.getRaw(KEY_MEMBER_ID))) {
 					// FIXME throw new InvalidMessageException() (#643)
 					db.deleteMessage(txn, m.getId());
 					return false;
@@ -401,8 +399,16 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 					return false;
 				}
 				// previous message must be from same member
-				if (!Arrays.equals(meta.getRaw(KEY_AUTHOR_ID),
-						previousMeta.getRaw(KEY_AUTHOR_ID))) {
+				if (!Arrays.equals(meta.getRaw(KEY_MEMBER_ID),
+						previousMeta.getRaw(KEY_MEMBER_ID))) {
+					// FIXME throw new InvalidMessageException() (#643)
+					db.deleteMessage(txn, m.getId());
+					return false;
+				}
+				// previous message must be a POST or JOIN
+				MessageType previousType = MessageType
+						.valueOf(previousMeta.getLong(KEY_TYPE).intValue());
+				if (previousType != JOIN && previousType != POST) {
 					// FIXME throw new InvalidMessageException() (#643)
 					db.deleteMessage(txn, m.getId());
 					return false;