diff --git a/briar-api/src/org/briarproject/api/blogs/BlogConstants.java b/briar-api/src/org/briarproject/api/blogs/BlogConstants.java
index 7bbaacdb8b2c33bd74c86ee1e5ce0a103479514c..5a9bff7f4e0c3b1b0c544964c2d690397f3e1d04 100644
--- a/briar-api/src/org/briarproject/api/blogs/BlogConstants.java
+++ b/briar-api/src/org/briarproject/api/blogs/BlogConstants.java
@@ -29,16 +29,19 @@ public interface BlogConstants {
 	String BLOG_PUBLIC_KEY = "blogPublicKey";
 
 	// Metadata keys
+	String KEY_TYPE = "type";
 	String KEY_DESCRIPTION = "description";
 	String KEY_TITLE = "title";
 	String KEY_TIMESTAMP = "timestamp";
 	String KEY_TIME_RECEIVED = "timeReceived";
-	String KEY_PARENT = "parent";
 	String KEY_AUTHOR_ID = "id";
 	String KEY_AUTHOR_NAME = "name";
 	String KEY_PUBLIC_KEY = "publicKey";
 	String KEY_AUTHOR = "author";
 	String KEY_CONTENT_TYPE = "contentType";
 	String KEY_READ = "read";
+	String KEY_COMMENT = "comment";
+	String KEY_ORIGINAL_MSG_ID = "originalMessageId";
+	String KEY_CURRENT_MSG_ID = "currentMessageId";
 
 }
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogPostHeader.java b/briar-api/src/org/briarproject/api/blogs/BlogPostHeader.java
index 980ea27eb4005139004322a3a290dd0f888171dd..f9ef7bdfeaa7d6e5184b469734d2ba167c8f0b50 100644
--- a/briar-api/src/org/briarproject/api/blogs/BlogPostHeader.java
+++ b/briar-api/src/org/briarproject/api/blogs/BlogPostHeader.java
@@ -14,10 +14,10 @@ public class BlogPostHeader extends PostHeader {
 	private final long timeReceived;
 
 	public BlogPostHeader(@Nullable String title, @NotNull MessageId id,
-			@Nullable MessageId parentId, long timestamp, long timeReceived,
-			@NotNull Author author,	@NotNull Status authorStatus,
-			@NotNull String contentType, boolean read) {
-		super(id, parentId, timestamp, author, authorStatus, contentType, read);
+			long timestamp, long timeReceived, @NotNull Author author,
+			@NotNull Status authorStatus, @NotNull String contentType,
+			boolean read) {
+		super(id, null, timestamp, author, authorStatus, contentType, read);
 
 		this.title = title;
 		this.timeReceived = timeReceived;
diff --git a/briar-api/src/org/briarproject/api/blogs/MessageType.java b/briar-api/src/org/briarproject/api/blogs/MessageType.java
new file mode 100644
index 0000000000000000000000000000000000000000..9be155679cfc83ea0c7113565d9656e00154884b
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/blogs/MessageType.java
@@ -0,0 +1,33 @@
+package org.briarproject.api.blogs;
+
+public enum MessageType {
+	POST(0),
+	COMMENT(1),
+	WRAPPED_POST(2),
+	WRAPPED_COMMENT(3);
+
+	int value;
+
+	MessageType(int value) {
+		this.value = value;
+	}
+
+	public static MessageType valueOf(int value) {
+		switch (value) {
+			case 0:
+				return POST;
+			case 1:
+				return COMMENT;
+			case 2:
+				return WRAPPED_POST;
+			case 3:
+				return WRAPPED_COMMENT;
+			default:
+				throw new IllegalArgumentException();
+		}
+	}
+
+	public int getInt() {
+		return value;
+	}
+}
\ No newline at end of file
diff --git a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
index 0b99f74cb2f4b4e29d43b847bb7734ad5b310402..543b97e0a190e3f61663f916b31f9583491e727b 100644
--- a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
+++ b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
@@ -52,7 +52,6 @@ import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
 import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
 import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
 import static org.briarproject.api.blogs.BlogConstants.KEY_DESCRIPTION;
-import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT;
 import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
 import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
 import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
@@ -240,7 +239,6 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 			meta = new BdfDictionary();
 			if (p.getTitle() != null) meta.put(KEY_TITLE, p.getTitle());
 			meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp());
-			if (p.getParent() != null) meta.put(KEY_PARENT, p.getParent());
 
 			Author a = p.getAuthor();
 			BdfDictionary authorMeta = new BdfDictionary();
@@ -409,9 +407,6 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 		String title = meta.getOptionalString(KEY_TITLE);
 		long timestamp = meta.getLong(KEY_TIMESTAMP);
 		long timeReceived = meta.getLong(KEY_TIME_RECEIVED, timestamp);
-		MessageId parentId = null;
-		if (meta.containsKey(KEY_PARENT))
-			parentId = new MessageId(meta.getRaw(KEY_PARENT));
 
 		BdfDictionary d = meta.getDictionary(KEY_AUTHOR);
 		AuthorId authorId = new AuthorId(d.getRaw(KEY_AUTHOR_ID));
@@ -427,7 +422,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 
 		String contentType = meta.getString(KEY_CONTENT_TYPE);
 		boolean read = meta.getBoolean(KEY_READ);
-		return new BlogPostHeader(title, id, parentId, timestamp, timeReceived,
-				author, authorStatus, contentType, read);
+		return new BlogPostHeader(title, id, timestamp, timeReceived, author,
+				authorStatus, contentType, read);
 	}
 }
diff --git a/briar-core/src/org/briarproject/blogs/BlogPostValidator.java b/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
index d412f10aea4ebd7f5bf4948f213b59abce090cea..15b74adbe4f36d3b980dfedb5b62f1d16f3f92d9 100644
--- a/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
+++ b/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
@@ -4,6 +4,7 @@ import org.briarproject.api.FormatException;
 import org.briarproject.api.UniqueId;
 import org.briarproject.api.blogs.Blog;
 import org.briarproject.api.blogs.BlogFactory;
+import org.briarproject.api.blogs.MessageType;
 import org.briarproject.api.clients.BdfMessageContext;
 import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.crypto.CryptoComponent;
@@ -16,8 +17,10 @@ import org.briarproject.api.data.BdfList;
 import org.briarproject.api.data.MetadataEncoder;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.sync.Group;
+import org.briarproject.api.sync.GroupFactory;
 import org.briarproject.api.sync.InvalidMessageException;
 import org.briarproject.api.sync.Message;
+import org.briarproject.api.sync.MessageFactory;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.system.Clock;
 import org.briarproject.clients.BdfMessageValidator;
@@ -29,13 +32,16 @@ import java.util.Collections;
 import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
 import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
 import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
+import static org.briarproject.api.blogs.BlogConstants.KEY_COMMENT;
 import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
-import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT;
+import static org.briarproject.api.blogs.BlogConstants.KEY_CURRENT_MSG_ID;
+import static org.briarproject.api.blogs.BlogConstants.KEY_ORIGINAL_MSG_ID;
 import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
 import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
 import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
 import static org.briarproject.api.blogs.BlogConstants.KEY_TIME_RECEIVED;
 import static org.briarproject.api.blogs.BlogConstants.KEY_TITLE;
+import static org.briarproject.api.blogs.BlogConstants.KEY_TYPE;
 import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
 import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TITLE_LENGTH;
 import static org.briarproject.api.blogs.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
@@ -44,14 +50,19 @@ import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH
 class BlogPostValidator extends BdfMessageValidator {
 
 	private final CryptoComponent crypto;
+	private final GroupFactory groupFactory;
+	private final MessageFactory messageFactory;
 	private final BlogFactory blogFactory;
 
-	BlogPostValidator(CryptoComponent crypto, BlogFactory blogFactory,
+	BlogPostValidator(CryptoComponent crypto, GroupFactory groupFactory,
+			MessageFactory messageFactory, BlogFactory blogFactory,
 			ClientHelper clientHelper, MetadataEncoder metadataEncoder,
 			Clock clock) {
 		super(clientHelper, metadataEncoder, clock);
 
 		this.crypto = crypto;
+		this.groupFactory = groupFactory;
+		this.messageFactory = messageFactory;
 		this.blogFactory = blogFactory;
 	}
 
@@ -59,14 +70,51 @@ class BlogPostValidator extends BdfMessageValidator {
 	protected BdfMessageContext validateMessage(Message m, Group g,
 			BdfList body) throws InvalidMessageException, FormatException {
 
+		BdfMessageContext c;
+
+		// TODO Remove! For Temporary Backwards Compatibility only!
+		if (body.get(0) instanceof BdfList) {
+			c = validatePost(m, g, body);
+			addMessageMetadata(c, m.getTimestamp());
+			return c;
+		}
+
+		int type = body.getLong(0).intValue();
+		body.removeElementAt(0);
+		switch (MessageType.valueOf(type)) {
+			case POST:
+				c = validatePost(m, g, body);
+				addMessageMetadata(c, m.getTimestamp());
+				break;
+			case COMMENT:
+				c = validateComment(m, g, body);
+				addMessageMetadata(c, m.getTimestamp());
+				break;
+			case WRAPPED_POST:
+				c = validateWrappedPost(m, g, body);
+				break;
+			case WRAPPED_COMMENT:
+				c = validateWrappedComment(m, g, body);
+				break;
+			default:
+				throw new InvalidMessageException("Unknown Message Type");
+		}
+		c.getDictionary().put(KEY_TYPE, type);
+		return c;
+	}
+
+	private BdfMessageContext validatePost(Message m, Group g, BdfList body)
+			throws InvalidMessageException, FormatException {
+
 		// Content, Signature
 		checkSize(body, 2);
 		BdfList content = body.getList(0);
 
-		// Content: Parent ID, content type, title (optional), post body,
+		// Content: content type, title (optional), post body,
 		//          attachments (optional)
 		checkSize(content, 5);
 		// Parent ID is optional
+		// TODO remove when breaking backwards compatibility
 		byte[] parent = content.getOptionalRaw(0);
 		checkLength(parent, UniqueId.LENGTH);
 		// Content type
@@ -81,23 +129,166 @@ class BlogPostValidator extends BdfMessageValidator {
 		byte[] postBody = content.getRaw(3);
 		checkLength(postBody, 0, MAX_BLOG_POST_BODY_LENGTH);
 		// Attachments
-		BdfDictionary attachments = content.getOptionalDictionary(4);
-		// TODO handle attachments somehow
+		content.getOptionalDictionary(4);
 
-		// Signature
+		// Verify Signature
 		byte[] sig = body.getRaw(1);
+		checkLength(sig, 1, MAX_SIGNATURE_LENGTH);
+		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), content);
+		Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
+		Author a = b.getAuthor();
+		verifySignature(sig, a.getPublicKey(), signed);
+
+		// Return the metadata and dependencies
+		BdfDictionary meta = new BdfDictionary();
+		if (title != null) meta.put(KEY_TITLE, title);
+		meta.put(KEY_AUTHOR, authorToBdfDictionary(a));
+		meta.put(KEY_CONTENT_TYPE, contentType);
+		return new BdfMessageContext(meta, null);
+	}
+
+	private BdfMessageContext validateComment(Message m, Group g, BdfList body)
+			throws InvalidMessageException, FormatException {
+
+		// comment, parent_original_id, signature, parent_current_id
+		checkSize(body, 4);
+
+		// Comment
+		String comment = body.getOptionalString(0);
+		checkLength(comment, 0, MAX_BLOG_POST_BODY_LENGTH);
+
+		// parent_original_id
+		// The ID of a post or comment in this group or another group
+		byte[] originalIdBytes = body.getRaw(1);
+		checkLength(originalIdBytes, MessageId.LENGTH);
+		MessageId originalId = new MessageId(originalIdBytes);
+
+		// Signature
+		byte[] sig = body.getRaw(2);
 		checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
-		// Verify the signature
-		Author a;
+		BdfList signed =
+				BdfList.of(g.getId(), m.getTimestamp(), comment, originalId);
+		Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
+		Author a = b.getAuthor();
+		verifySignature(sig, a.getPublicKey(), signed);
+
+		// parent_current_id
+		// The ID of a post, comment, wrapped post or wrapped comment in this
+		// group, which had the ID parent_original_id in the group
+		// where it was originally posted
+		byte[] currentIdBytes = body.getRaw(3);
+		checkLength(currentIdBytes, MessageId.LENGTH);
+		MessageId currentId = new MessageId(currentIdBytes);
+
+		// Return the metadata and dependencies
+		BdfDictionary meta = new BdfDictionary();
+		if (comment != null) meta.put(KEY_COMMENT, comment);
+		meta.put(KEY_ORIGINAL_MSG_ID, originalId);
+		meta.put(KEY_CURRENT_MSG_ID, currentId);
+		meta.put(KEY_AUTHOR, authorToBdfDictionary(a));
+		Collection<MessageId> dependencies = Collections.singleton(currentId);
+		return new BdfMessageContext(meta, dependencies);
+	}
+
+	private BdfMessageContext validateWrappedPost(Message m, Group g,
+			BdfList body) throws InvalidMessageException, FormatException {
+
+		// group descriptor, timestamp, content, signature
+		checkSize(body, 4);
+
+		// Group Descriptor
+		byte[] descriptor = body.getRaw(0);
+
+		// Timestamp of Wrapped Post
+		long wTimestamp = body.getLong(1);
+
+		// Content of Wrapped Post
+		BdfList content = body.getList(2);
+
+		// Signature of Wrapped Post
+		byte[] signature = body.getRaw(3);
+		checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
+
+		// Get and Validate the Wrapped Message
+		Group wGroup = groupFactory
+				.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
+		BdfList wBodyList = BdfList.of(content, signature);
+		byte[] wBody = clientHelper.toByteArray(wBodyList);
+		Message wMessage =
+				messageFactory.createMessage(wGroup.getId(), wTimestamp, wBody);
+		BdfMessageContext c = validatePost(wMessage, wGroup, wBodyList);
+
+		// Return the metadata and dependencies
+		BdfDictionary meta = new BdfDictionary();
+		meta.put(KEY_TIMESTAMP, wTimestamp);
+		meta.put(KEY_AUTHOR, c.getDictionary().getDictionary(KEY_AUTHOR));
+		meta.put(KEY_CONTENT_TYPE,
+				c.getDictionary().getString(KEY_CONTENT_TYPE));
+		return new BdfMessageContext(meta, null);
+	}
+
+	private BdfMessageContext validateWrappedComment(Message m, Group g,
+			BdfList body) throws InvalidMessageException, FormatException {
+
+		// group descriptor, timestamp, comment, parent_original_id, signature,
+		// parent_current_id
+		checkSize(body, 6);
+
+		// Group Descriptor
+		byte[] descriptor = body.getRaw(0);
+
+		// Timestamp of Wrapped Comment
+		long wTimestamp = body.getLong(1);
+
+		// Body of Wrapped Comment
+		String comment = body.getOptionalString(2);
+
+		// parent_original_id
+		// Taken from the original comment
+		byte[] originalIdBytes = body.getRaw(3);
+		checkLength(originalIdBytes, MessageId.LENGTH);
+		MessageId originalId = new MessageId(originalIdBytes);
+
+		// signature
+		// Taken from the original comment
+		byte[] signature = body.getRaw(4);
+		checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
+
+		// parent_current_id
+		// The ID of a post, comment, wrapped post or wrapped comment in this
+		// group, which had the ID parent_original_id in the group
+		// where it was originally posted
+		byte[] currentIdBytes = body.getRaw(5);
+		checkLength(currentIdBytes, MessageId.LENGTH);
+		MessageId currentId = new MessageId(currentIdBytes);
+
+		// Get and Validate the Wrapped Comment
+		Group wGroup = groupFactory
+				.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
+		BdfList wBodyList =	BdfList.of(comment, originalId, signature,
+				currentId);
+		byte[] wBody = clientHelper.toByteArray(wBodyList);
+		Message wMessage =
+				messageFactory.createMessage(wGroup.getId(), wTimestamp, wBody);
+		BdfMessageContext c = validateComment(wMessage, wGroup, wBodyList);
+
+		// Return the metadata and dependencies
+		Collection<MessageId> dependencies = Collections.singleton(currentId);
+		BdfDictionary meta = new BdfDictionary();
+		meta.put(KEY_ORIGINAL_MSG_ID, wMessage.getId());
+		meta.put(KEY_CURRENT_MSG_ID, currentId);
+		meta.put(KEY_TIMESTAMP, wTimestamp);
+		if (comment != null) meta.put(KEY_COMMENT, comment);
+		meta.put(KEY_AUTHOR, c.getDictionary().getDictionary(KEY_AUTHOR));
+		return new BdfMessageContext(meta, dependencies);
+	}
+
+	private void verifySignature(byte[] sig, byte[] publicKey, BdfList signed)
+			throws InvalidMessageException {
 		try {
-			// Get the blog author
-			Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
-			a = b.getAuthor();
 			// Parse the public key
 			KeyParser keyParser = crypto.getSignatureKeyParser();
-			PublicKey key = keyParser.parsePublicKey(a.getPublicKey());
-			// Serialise the data to be signed
-			BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), content);
+			PublicKey key = keyParser.parsePublicKey(publicKey);
 			// Verify the signature
 			Signature signature = crypto.getSignature();
 			signature.initVerify(key);
@@ -107,26 +298,23 @@ class BlogPostValidator extends BdfMessageValidator {
 			}
 		} catch (GeneralSecurityException e) {
 			throw new InvalidMessageException("Invalid public key");
+		} catch (FormatException e) {
+			throw new InvalidMessageException(e);
 		}
+	}
 
-		// Return the metadata and dependencies
-		BdfDictionary meta = new BdfDictionary();
-		Collection<MessageId> dependencies = null;
-		if (title != null) meta.put(KEY_TITLE, title);
-		BdfDictionary author = BdfDictionary.of(
+	static BdfDictionary authorToBdfDictionary(Author a) {
+		return BdfDictionary.of(
 				new BdfEntry(KEY_AUTHOR_ID, a.getId()),
 				new BdfEntry(KEY_AUTHOR_NAME, a.getName()),
 				new BdfEntry(KEY_PUBLIC_KEY, a.getPublicKey())
 		);
-		meta.put(KEY_AUTHOR, author);
-		meta.put(KEY_TIMESTAMP, m.getTimestamp());
-		meta.put(KEY_TIME_RECEIVED, clock.currentTimeMillis());
-		if (parent != null) {
-			meta.put(KEY_PARENT, parent);
-			dependencies = Collections.singletonList(new MessageId(parent));
-		}
-		meta.put(KEY_CONTENT_TYPE, contentType);
-		meta.put(KEY_READ, false);
-		return new BdfMessageContext(meta, dependencies);
 	}
+
+	private void addMessageMetadata(BdfMessageContext c, long time) {
+		c.getDictionary().put(KEY_TIMESTAMP, time);
+		c.getDictionary().put(KEY_TIME_RECEIVED, clock.currentTimeMillis());
+		c.getDictionary().put(KEY_READ, false);
+	}
+
 }
diff --git a/briar-core/src/org/briarproject/blogs/BlogsModule.java b/briar-core/src/org/briarproject/blogs/BlogsModule.java
index 055328ad9cbb664f7e027bb6bb46ce70a5309f8b..a93d39aa4534f14cd5f56807776432677426a87d 100644
--- a/briar-core/src/org/briarproject/blogs/BlogsModule.java
+++ b/briar-core/src/org/briarproject/blogs/BlogsModule.java
@@ -11,6 +11,7 @@ import org.briarproject.api.identity.AuthorFactory;
 import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.lifecycle.LifecycleManager;
 import org.briarproject.api.sync.GroupFactory;
+import org.briarproject.api.sync.MessageFactory;
 import org.briarproject.api.sync.ValidationManager;
 import org.briarproject.api.system.Clock;
 
@@ -64,11 +65,13 @@ public class BlogsModule {
 	@Singleton
 	BlogPostValidator provideBlogPostValidator(
 			ValidationManager validationManager, CryptoComponent crypto,
+			GroupFactory groupFactory, MessageFactory messageFactory,
 			BlogFactory blogFactory, ClientHelper clientHelper,
 			MetadataEncoder metadataEncoder, Clock clock) {
 
 		BlogPostValidator validator = new BlogPostValidator(crypto,
-				blogFactory, clientHelper, metadataEncoder, clock);
+				groupFactory, messageFactory, blogFactory, clientHelper,
+				metadataEncoder, clock);
 		validationManager.registerMessageValidator(CLIENT_ID, validator);
 
 		return validator;
diff --git a/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java b/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java
index e70fce8afbffc46c5bb17fd6bb8b69a55c7097c9..e64f29ba6b65ce2ad4fb262d80d1e449a04d5ff3 100644
--- a/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java
+++ b/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java
@@ -18,9 +18,11 @@ import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.sync.ClientId;
 import org.briarproject.api.sync.Group;
+import org.briarproject.api.sync.GroupFactory;
 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;
 import org.briarproject.api.system.Clock;
 import org.briarproject.system.SystemClock;
@@ -34,10 +36,17 @@ import java.security.GeneralSecurityException;
 import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
 import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
 import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
+import static org.briarproject.api.blogs.BlogConstants.KEY_COMMENT;
 import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
+import static org.briarproject.api.blogs.BlogConstants.KEY_CURRENT_MSG_ID;
+import static org.briarproject.api.blogs.BlogConstants.KEY_ORIGINAL_MSG_ID;
 import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
 import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
 import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
+import static org.briarproject.api.blogs.MessageType.COMMENT;
+import static org.briarproject.api.blogs.MessageType.POST;
+import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT;
+import static org.briarproject.api.blogs.MessageType.WRAPPED_POST;
 import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -47,11 +56,17 @@ public class BlogPostValidatorTest extends BriarTestCase {
 	private final Mockery context = new Mockery();
 	private final Blog blog;
 	private final Author author;
+	private final BdfDictionary authorDict;
+	private final ClientId clientId;
+	private final byte[] descriptor;
 	private final Group group;
 	private final Message message;
 	private final BlogPostValidator validator;
 	private final CryptoComponent cryptoComponent =
 			context.mock(CryptoComponent.class);
+	private final GroupFactory groupFactory = context.mock(GroupFactory.class);
+	private final MessageFactory messageFactory =
+			context.mock(MessageFactory.class);
 	private final BlogFactory blogFactory = context.mock(BlogFactory.class);
 	private final ClientHelper clientHelper = context.mock(ClientHelper.class);
 	private final Clock clock = new SystemClock();
@@ -61,12 +76,18 @@ public class BlogPostValidatorTest extends BriarTestCase {
 
 	public BlogPostValidatorTest() {
 		GroupId groupId = new GroupId(TestUtils.getRandomId());
-		ClientId clientId = new ClientId(TestUtils.getRandomId());
-		byte[] descriptor = TestUtils.getRandomBytes(12);
+		clientId = BlogManagerImpl.CLIENT_ID;
+		descriptor = TestUtils.getRandomBytes(42);
 		group = new Group(groupId, clientId, descriptor);
-		AuthorId authorId = new AuthorId(TestUtils.getRandomBytes(AuthorId.LENGTH));
+		AuthorId authorId =
+				new AuthorId(TestUtils.getRandomBytes(AuthorId.LENGTH));
 		byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
 		author = new Author(authorId, "Author", publicKey);
+		authorDict = BdfDictionary.of(
+				new BdfEntry(KEY_AUTHOR_ID, author.getId()),
+				new BdfEntry(KEY_AUTHOR_NAME, author.getName()),
+				new BdfEntry(KEY_PUBLIC_KEY, author.getPublicKey())
+		);
 		blog = new Blog(group, "Test Blog", "", author);
 
 		MessageId messageId = new MessageId(TestUtils.getRandomId());
@@ -75,29 +96,27 @@ public class BlogPostValidatorTest extends BriarTestCase {
 		message = new Message(messageId, group.getId(), timestamp, raw);
 
 		MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class);
-		validator = new BlogPostValidator(cryptoComponent, blogFactory,
-				clientHelper, metadataEncoder, clock);
+		validator = new BlogPostValidator(cryptoComponent, groupFactory,
+				messageFactory, blogFactory, clientHelper, metadataEncoder,
+				clock);
 		context.assertIsSatisfied();
 	}
 
 	@Test
 	public void testValidateProperBlogPost()
 			throws IOException, GeneralSecurityException {
-		// Parent ID, content type, title (optional), post body, attachments
+		// content type, title (optional), post body, attachments
 		BdfList content = BdfList.of(null, contentType, null, body, null);
 		final byte[] sigBytes = TestUtils.getRandomBytes(42);
-		BdfList m = BdfList.of(content, sigBytes);
+		BdfList m = BdfList.of(POST.getInt(), content, sigBytes);
 
-		expectCrypto(m, true);
+		BdfList signed =
+				BdfList.of(blog.getId(), message.getTimestamp(), content);
+		expectCrypto(signed, sigBytes, true);
 		final BdfDictionary result =
 				validator.validateMessage(message, group, m).getDictionary();
 
 		assertEquals(contentType, result.getString(KEY_CONTENT_TYPE));
-		BdfDictionary authorDict = BdfDictionary.of(
-				new BdfEntry(KEY_AUTHOR_ID, author.getId()),
-				new BdfEntry(KEY_AUTHOR_NAME, author.getName()),
-				new BdfEntry(KEY_PUBLIC_KEY, author.getPublicKey())
-		);
 		assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
 		assertFalse(result.getBoolean(KEY_READ));
 		context.assertIsSatisfied();
@@ -107,7 +126,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 	public void testValidateBlogPostWithoutAttachments()
 			throws IOException, GeneralSecurityException {
 		BdfList content = BdfList.of(null, contentType, null, body);
-		BdfList m = BdfList.of(content, null);
+		BdfList m = BdfList.of(POST.getInt(), content, null);
 
 		validator.validateMessage(message, group, m).getDictionary();
 	}
@@ -116,7 +135,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 	public void testValidateBlogPostWithoutSignature()
 			throws IOException, GeneralSecurityException {
 		BdfList content = BdfList.of(null, contentType, null, body, null);
-		BdfList m = BdfList.of(content, null);
+		BdfList m = BdfList.of(POST.getInt(), content, null);
 
 		validator.validateMessage(message, group, m).getDictionary();
 	}
@@ -124,26 +143,148 @@ public class BlogPostValidatorTest extends BriarTestCase {
 	@Test(expected = InvalidMessageException.class)
 	public void testValidateBlogPostWithBadSignature()
 			throws IOException, GeneralSecurityException {
-		// Parent ID, content type, title (optional), post body, attachments
+		// content type, title (optional), post body, attachments
 		BdfList content = BdfList.of(null, contentType, null, body, null);
 		final byte[] sigBytes = TestUtils.getRandomBytes(42);
-		BdfList m = BdfList.of(content, sigBytes);
+		BdfList m = BdfList.of(POST.getInt(), content, sigBytes);
 
-		expectCrypto(m, false);
+		BdfList signed =
+				BdfList.of(blog.getId(), message.getTimestamp(), content);
+		expectCrypto(signed, sigBytes, false);
 		validator.validateMessage(message, group, m).getDictionary();
 	}
 
-	private void expectCrypto(BdfList m, final boolean pass)
+	@Test
+	public void testValidateProperBlogComment()
 			throws IOException, GeneralSecurityException {
-		final Signature signature = context.mock(Signature.class);
-		final KeyParser keyParser = context.mock(KeyParser.class);
-		final PublicKey publicKey = context.mock(PublicKey.class);
+		// comment, parent_original_id, signature, parent_current_id
+		String comment = "This is a blog comment";
+		MessageId originalId = new MessageId(TestUtils.getRandomId());
+		byte[] currentId = TestUtils.getRandomId();
+		final byte[] sigBytes = TestUtils.getRandomBytes(42);
+		BdfList m = BdfList.of(COMMENT.getInt(), comment, originalId,
+				sigBytes, currentId);
 
-		final BdfList content = m.getList(0);
-		final byte[] sigBytes = m.getRaw(1);
+		BdfList signed =
+				BdfList.of(blog.getId(), message.getTimestamp(), comment,
+						originalId);
+		expectCrypto(signed, sigBytes, true);
+		final BdfDictionary result =
+				validator.validateMessage(message, group, m).getDictionary();
 
-		final BdfList signed =
+		assertEquals(comment, result.getString(KEY_COMMENT));
+		assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
+		assertEquals(originalId.getBytes(), result.getRaw(KEY_ORIGINAL_MSG_ID));
+		assertEquals(currentId, result.getRaw(KEY_CURRENT_MSG_ID));
+		assertFalse(result.getBoolean(KEY_READ));
+		context.assertIsSatisfied();
+	}
+
+	@Test
+	public void testValidateProperEmptyBlogComment()
+			throws IOException, GeneralSecurityException {
+		// comment, parent_original_id, signature, parent_current_id
+		MessageId originalId = new MessageId(TestUtils.getRandomId());
+		byte[] currentId = TestUtils.getRandomId();
+		final byte[] sigBytes = TestUtils.getRandomBytes(42);
+		BdfList m = BdfList.of(COMMENT.getInt(), null, originalId, sigBytes,
+				currentId);
+
+		BdfList signed =
+				BdfList.of(blog.getId(), message.getTimestamp(), null,
+						originalId);
+		expectCrypto(signed, sigBytes, true);
+		final BdfDictionary result =
+				validator.validateMessage(message, group, m).getDictionary();
+
+		assertFalse(result.containsKey(KEY_COMMENT));
+		context.assertIsSatisfied();
+	}
+
+	@Test
+	public void testValidateProperWrappedPost()
+			throws IOException, GeneralSecurityException {
+		// group descriptor, timestamp, content, signature
+		BdfList content = BdfList.of(null, contentType, null, body, null);
+		final byte[] sigBytes = TestUtils.getRandomBytes(42);
+		BdfList m =
+				BdfList.of(WRAPPED_POST.getInt(), descriptor,
+						message.getTimestamp(), content, sigBytes);
+
+		BdfList signed =
 				BdfList.of(blog.getId(), message.getTimestamp(), content);
+		expectCrypto(signed, sigBytes, true);
+
+		final BdfList originalList = BdfList.of(content, sigBytes);
+		final byte[] originalBody = TestUtils.getRandomBytes(42);
+
+		context.checking(new Expectations() {{
+			oneOf(groupFactory).createGroup(clientId, descriptor);
+			will(returnValue(blog.getGroup()));
+			oneOf(clientHelper).toByteArray(originalList);
+			will(returnValue(originalBody));
+			oneOf(messageFactory)
+					.createMessage(group.getId(), message.getTimestamp(),
+							originalBody);
+			will(returnValue(message));
+		}});
+
+		final BdfDictionary result =
+				validator.validateMessage(message, group, m).getDictionary();
+
+		assertEquals(contentType, result.getString(KEY_CONTENT_TYPE));
+		assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
+		context.assertIsSatisfied();
+	}
+
+	@Test
+	public void testValidateProperWrappedComment()
+			throws IOException, GeneralSecurityException {
+		// group descriptor, timestamp, comment, parent_original_id, signature,
+		// parent_current_id
+		String comment = "This is another comment";
+		MessageId originalId = new MessageId(TestUtils.getRandomId());
+		final byte[] sigBytes = TestUtils.getRandomBytes(42);
+		MessageId currentId = new MessageId(TestUtils.getRandomId());
+		BdfList m = BdfList.of(WRAPPED_COMMENT.getInt(), descriptor,
+				message.getTimestamp(), comment, originalId, sigBytes,
+				currentId);
+
+		BdfList signed = BdfList.of(blog.getId(), message.getTimestamp(),
+				comment, originalId);
+		expectCrypto(signed, sigBytes, true);
+
+		final BdfList originalList = BdfList.of(comment, originalId, sigBytes,
+				currentId);
+		final byte[] originalBody = TestUtils.getRandomBytes(42);
+
+		context.checking(new Expectations() {{
+			oneOf(groupFactory).createGroup(clientId, descriptor);
+			will(returnValue(blog.getGroup()));
+			oneOf(clientHelper).toByteArray(originalList);
+			will(returnValue(originalBody));
+			oneOf(messageFactory)
+					.createMessage(group.getId(), message.getTimestamp(),
+							originalBody);
+			will(returnValue(message));
+		}});
+
+		final BdfDictionary result =
+				validator.validateMessage(message, group, m).getDictionary();
+
+		assertEquals(comment, result.getString(KEY_COMMENT));
+		assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
+		assertEquals(
+				message.getId().getBytes(), result.getRaw(KEY_ORIGINAL_MSG_ID));
+		assertEquals(currentId.getBytes(), result.getRaw(KEY_CURRENT_MSG_ID));
+		context.assertIsSatisfied();
+	}
+
+	private void expectCrypto(final BdfList signed, final byte[] sig,
+			final boolean pass) throws IOException, GeneralSecurityException {
+		final Signature signature = context.mock(Signature.class);
+		final KeyParser keyParser = context.mock(KeyParser.class);
+		final PublicKey publicKey = context.mock(PublicKey.class);
 
 		context.checking(new Expectations() {{
 			oneOf(blogFactory).parseBlog(group, "");
@@ -156,9 +297,9 @@ public class BlogPostValidatorTest extends BriarTestCase {
 			will(returnValue(signature));
 			oneOf(signature).initVerify(publicKey);
 			oneOf(clientHelper).toByteArray(signed);
-			will(returnValue(sigBytes));
-			oneOf(signature).update(sigBytes);
-			oneOf(signature).verify(sigBytes);
+			will(returnValue(sig));
+			oneOf(signature).update(sig);
+			oneOf(signature).verify(sig);
 			will(returnValue(pass));
 		}});
 	}