diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/client/BdfMessageValidator.java b/bramble-api/src/main/java/org/briarproject/bramble/api/client/BdfMessageValidator.java
index 63f34185704b6a2f4dab643304ed0bcde7610aeb..b64ec9b1c108bf6fee6dcd3ee8d8ead06f45325a 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/client/BdfMessageValidator.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/client/BdfMessageValidator.java
@@ -16,7 +16,6 @@ import java.util.logging.Logger;
 
 import javax.annotation.concurrent.Immutable;
 
-import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
 import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
 
 @Immutable
@@ -49,14 +48,13 @@ public abstract class BdfMessageValidator implements MessageValidator {
 			throw new InvalidMessageException(
 					"Timestamp is too far in the future");
 		}
-		byte[] raw = m.getRaw();
-		if (raw.length <= MESSAGE_HEADER_LENGTH) {
+		byte[] body = m.getBody();
+		if (body.length == 0) {
 			throw new InvalidMessageException("Message is too short");
 		}
 		try {
-			BdfList body = clientHelper.toList(raw, MESSAGE_HEADER_LENGTH,
-					raw.length - MESSAGE_HEADER_LENGTH);
-			BdfMessageContext result = validateMessage(m, g, body);
+			BdfList bodyList = clientHelper.toList(body);
+			BdfMessageContext result = validateMessage(m, g, bodyList);
 			Metadata meta = metadataEncoder.encode(result.getDictionary());
 			return new MessageContext(meta, result.getDependencies());
 		} catch (FormatException e) {
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/Message.java b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/Message.java
index c032e744ed35baf784392c2bf456b11e7f504985..7704d03fc4a23cc4e4f53dd60d18905a78f73347 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/Message.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/Message.java
@@ -1,6 +1,6 @@
 package org.briarproject.bramble.api.sync;
 
-import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
+import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
 import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
 
 public class Message {
@@ -13,17 +13,15 @@ public class Message {
 	private final MessageId id;
 	private final GroupId groupId;
 	private final long timestamp;
-	private final byte[] raw;
+	private final byte[] body;
 
-	public Message(MessageId id, GroupId groupId, long timestamp, byte[] raw) {
-		if (raw.length <= MESSAGE_HEADER_LENGTH)
-			throw new IllegalArgumentException();
-		if (raw.length > MAX_MESSAGE_LENGTH)
+	public Message(MessageId id, GroupId groupId, long timestamp, byte[] body) {
+		if (body.length > MAX_MESSAGE_BODY_LENGTH)
 			throw new IllegalArgumentException();
 		this.id = id;
 		this.groupId = groupId;
 		this.timestamp = timestamp;
-		this.raw = raw;
+		this.body = body;
 	}
 
 	/**
@@ -51,14 +49,14 @@ public class Message {
 	 * Returns the length of the raw message in bytes.
 	 */
 	public int getRawLength() {
-		return raw.length;
+		return MESSAGE_HEADER_LENGTH + body.length;
 	}
 
 	/**
-	 * Returns the raw message.
+	 * Returns the message body.
 	 */
-	public byte[] getRaw() {
-		return raw;
+	public byte[] getBody() {
+		return body;
 	}
 
 	@Override
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/MessageFactory.java b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/MessageFactory.java
index a31e7737000a80d1511c5e9e57fa3bb1c5880bcd..cab854381dd7cbc7f29ca39c0c194d53ec4aeea1 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/MessageFactory.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/MessageFactory.java
@@ -9,5 +9,5 @@ public interface MessageFactory {
 
 	Message createMessage(byte[] raw);
 
-	Message createMessage(MessageId m, byte[] raw);
+	byte[] getRawMessage(Message m);
 }
diff --git a/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java b/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java
index 8b6a8177db2db8b430abf6b39e1fddd1636e1508..d33304dd05581e34693a244413392721a296c79f 100644
--- a/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java
+++ b/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java
@@ -33,7 +33,6 @@ import static org.briarproject.bramble.api.properties.TransportPropertyConstants
 import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
 import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
 import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
-import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
 import static org.briarproject.bramble.util.StringUtils.getRandomString;
 
 public class TestUtils {
@@ -132,13 +131,13 @@ public class TestUtils {
 
 	public static Message getMessage(GroupId groupId) {
 		int bodyLength = 1 + random.nextInt(MAX_MESSAGE_BODY_LENGTH);
-		return getMessage(groupId, MESSAGE_HEADER_LENGTH + bodyLength);
+		return getMessage(groupId, bodyLength);
 	}
 
-	public static Message getMessage(GroupId groupId, int rawLength) {
+	public static Message getMessage(GroupId groupId, int bodyLength) {
 		MessageId id = new MessageId(getRandomId());
-		byte[] raw = getRandomBytes(rawLength);
-		return new Message(id, groupId, timestamp, raw);
+		byte[] body = getRandomBytes(bodyLength);
+		return new Message(id, groupId, timestamp, body);
 	}
 
 	public static double getMedian(Collection<? extends Number> samples) {
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/client/ClientHelperImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/client/ClientHelperImpl.java
index 62ff284dd0ef82f8be259860d46151c5cb0bbc7e..0a66aacf648193453a25f6149b5e1b8c3165c24e 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/client/ClientHelperImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/client/ClientHelperImpl.java
@@ -41,7 +41,6 @@ import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_N
 import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
 import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
 import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
-import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
 import static org.briarproject.bramble.util.ValidationUtils.checkLength;
 import static org.briarproject.bramble.util.ValidationUtils.checkSize;
 
@@ -147,9 +146,7 @@ class ClientHelperImpl implements ClientHelper {
 	@Override
 	public BdfList getMessageAsList(Transaction txn, MessageId m)
 			throws DbException, FormatException {
-		byte[] raw = db.getMessage(txn, m).getRaw();
-		return toList(raw, MESSAGE_HEADER_LENGTH,
-				raw.length - MESSAGE_HEADER_LENGTH);
+		return toList(db.getMessage(txn, m).getBody());
 	}
 
 	@Override
@@ -361,9 +358,7 @@ class ClientHelperImpl implements ClientHelper {
 
 	@Override
 	public BdfList toList(Message m) throws FormatException {
-		byte[] raw = m.getRaw();
-		return toList(raw, MESSAGE_HEADER_LENGTH,
-				raw.length - MESSAGE_HEADER_LENGTH);
+		return toList(m.getBody());
 	}
 
 	@Override
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseModule.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseModule.java
index 24922528d9328e96a2011d52a68d65e366ffc326..3982365832e9b5b78fc8ba4bdc93c0e3e6c66657 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseModule.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseModule.java
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
 import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.api.event.EventBus;
 import org.briarproject.bramble.api.lifecycle.ShutdownManager;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 
 import java.sql.Connection;
@@ -18,8 +19,9 @@ public class DatabaseModule {
 
 	@Provides
 	@Singleton
-	Database<Connection> provideDatabase(DatabaseConfig config, Clock clock) {
-		return new H2Database(config, clock);
+	Database<Connection> provideDatabase(DatabaseConfig config,
+			MessageFactory messageFactory, Clock clock) {
+		return new H2Database(config, messageFactory, clock);
 	}
 
 	@Provides
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/H2Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/H2Database.java
index f400d1ce984c7c485fb50a8828fb3c273fa482aa..028312d81d31e9d46d0f74080f057b607f8096f5 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/H2Database.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/H2Database.java
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.db.MigrationListener;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 import org.briarproject.bramble.util.StringUtils;
 
@@ -36,9 +37,10 @@ class H2Database extends JdbcDatabase {
 	private volatile SecretKey key = null;
 
 	@Inject
-	H2Database(DatabaseConfig config, Clock clock) {
+	H2Database(DatabaseConfig config, MessageFactory messageFactory,
+			Clock clock) {
 		super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
-				clock);
+				messageFactory, clock);
 		this.config = config;
 		File dir = config.getDatabaseDirectory();
 		String path = new File(dir, "db").getAbsolutePath();
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/HyperSqlDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/HyperSqlDatabase.java
index 447537d8c8098a730e3c7ea217f1e7f001dd5c63..f5419f7cb4cf5769f38e358335451bffb40e4d65 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/HyperSqlDatabase.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/HyperSqlDatabase.java
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.db.MigrationListener;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 import org.briarproject.bramble.util.StringUtils;
 
@@ -37,9 +38,10 @@ class HyperSqlDatabase extends JdbcDatabase {
 	private volatile SecretKey key = null;
 
 	@Inject
-	HyperSqlDatabase(DatabaseConfig config, Clock clock) {
+	HyperSqlDatabase(DatabaseConfig config, MessageFactory messageFactory,
+			Clock clock) {
 		super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
-				clock);
+				messageFactory, clock);
 		this.config = config;
 		File dir = config.getDatabaseDirectory();
 		String path = new File(dir, "db").getAbsolutePath();
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
index 865db95b5cf52bca8c12b4ba307044e122892958..76eb96c4795be71b63c959aa03c166d4997caee8 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
@@ -21,6 +21,7 @@ import org.briarproject.bramble.api.sync.Group;
 import org.briarproject.bramble.api.sync.Group.Visibility;
 import org.briarproject.bramble.api.sync.GroupId;
 import org.briarproject.bramble.api.sync.Message;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.sync.MessageId;
 import org.briarproject.bramble.api.sync.MessageStatus;
 import org.briarproject.bramble.api.sync.ValidationManager.State;
@@ -61,6 +62,7 @@ import static org.briarproject.bramble.api.db.Metadata.REMOVE;
 import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
 import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
 import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
+import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
 import static org.briarproject.bramble.api.sync.ValidationManager.State.DELIVERED;
 import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
 import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
@@ -305,6 +307,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 	// Different database libraries use different names for certain types
 	private final String hashType, secretType, binaryType;
 	private final String counterType, stringType;
+	private final MessageFactory messageFactory;
 	private final Clock clock;
 
 	// Locking: connectionsLock
@@ -320,12 +323,14 @@ abstract class JdbcDatabase implements Database<Connection> {
 	private final Condition connectionsChanged = connectionsLock.newCondition();
 
 	JdbcDatabase(String hashType, String secretType, String binaryType,
-			String counterType, String stringType, Clock clock) {
+			String counterType, String stringType,
+			MessageFactory messageFactory, Clock clock) {
 		this.hashType = hashType;
 		this.secretType = secretType;
 		this.binaryType = binaryType;
 		this.counterType = counterType;
 		this.stringType = stringType;
+		this.messageFactory = messageFactory;
 		this.clock = clock;
 	}
 
@@ -727,7 +732,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 			ps.setLong(3, m.getTimestamp());
 			ps.setInt(4, state.getValue());
 			ps.setBoolean(5, messageShared);
-			byte[] raw = m.getRaw();
+			byte[] raw = messageFactory.getRawMessage(m);
 			ps.setInt(6, raw.length);
 			ps.setBytes(7, raw);
 			int affected = ps.executeUpdate();
@@ -741,7 +746,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 				boolean offered = removeOfferedMessage(txn, c, m.getId());
 				boolean seen = offered || (sender != null && c.equals(sender));
 				addStatus(txn, m.getId(), c, m.getGroupId(), m.getTimestamp(),
-						m.getRawLength(), state, e.getValue(), messageShared,
+						raw.length, state, e.getValue(), messageShared,
 						false, seen);
 			}
 			// Update denormalised column in messageDependencies if dependency
@@ -1501,7 +1506,10 @@ abstract class JdbcDatabase implements Database<Connection> {
 			rs.close();
 			ps.close();
 			if (raw == null) throw new MessageDeletedException();
-			return new Message(m, g, timestamp, raw);
+			if (raw.length < MESSAGE_HEADER_LENGTH) throw new AssertionError();
+			byte[] body = new byte[raw.length - MESSAGE_HEADER_LENGTH];
+			System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
+			return new Message(m, g, timestamp, body);
 		} catch (SQLException e) {
 			tryToClose(rs);
 			tryToClose(ps);
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/MessageFactoryImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/MessageFactoryImpl.java
index 754f0edac58ed70f648ac2983ae70e21e35bf051..ce22dc80d3ddff13a8e48a3ee6a390889aa89214 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/sync/MessageFactoryImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/MessageFactoryImpl.java
@@ -39,11 +39,7 @@ class MessageFactoryImpl implements MessageFactory {
 		if (body.length > MAX_MESSAGE_BODY_LENGTH)
 			throw new IllegalArgumentException();
 		MessageId id = getMessageId(g, timestamp, body);
-		byte[] raw = new byte[MESSAGE_HEADER_LENGTH + body.length];
-		System.arraycopy(g.getBytes(), 0, raw, 0, UniqueId.LENGTH);
-		ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH);
-		System.arraycopy(body, 0, raw, MESSAGE_HEADER_LENGTH, body.length);
-		return new Message(id, g, timestamp, raw);
+		return new Message(id, g, timestamp, body);
 	}
 
 	private MessageId getMessageId(GroupId g, long timestamp, byte[] body) {
@@ -69,18 +65,16 @@ class MessageFactoryImpl implements MessageFactory {
 		byte[] body = new byte[raw.length - MESSAGE_HEADER_LENGTH];
 		System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
 		MessageId id = getMessageId(g, timestamp, body);
-		return new Message(id, g, timestamp, raw);
+		return new Message(id, g, timestamp, body);
 	}
 
 	@Override
-	public Message createMessage(MessageId m, byte[] raw) {
-		if (raw.length < MESSAGE_HEADER_LENGTH)
-			throw new IllegalArgumentException();
-		if (raw.length > MAX_MESSAGE_LENGTH)
-			throw new IllegalArgumentException();
-		byte[] groupId = new byte[UniqueId.LENGTH];
-		System.arraycopy(raw, 0, groupId, 0, UniqueId.LENGTH);
-		long timestamp = ByteUtils.readUint64(raw, UniqueId.LENGTH);
-		return new Message(m, new GroupId(groupId), timestamp, raw);
+	public byte[] getRawMessage(Message m) {
+		byte[] body = m.getBody();
+		byte[] raw = new byte[MESSAGE_HEADER_LENGTH + body.length];
+		System.arraycopy(m.getGroupId().getBytes(), 0, raw, 0, UniqueId.LENGTH);
+		ByteUtils.writeUint64(m.getTimestamp(), raw, UniqueId.LENGTH);
+		System.arraycopy(body, 0, raw, MESSAGE_HEADER_LENGTH, body.length);
+		return raw;
 	}
 }
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordWriterFactoryImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordWriterFactoryImpl.java
index 0d6a762863e3b75e0a2b28525765e75fe2c4ae18..d83bda1a8b731e4dd90be54d42ee9b40ae7f8f42 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordWriterFactoryImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordWriterFactoryImpl.java
@@ -3,6 +3,7 @@ package org.briarproject.bramble.sync;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.record.RecordWriter;
 import org.briarproject.bramble.api.record.RecordWriterFactory;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.sync.SyncRecordWriter;
 import org.briarproject.bramble.api.sync.SyncRecordWriterFactory;
 
@@ -13,16 +14,19 @@ import javax.inject.Inject;
 @NotNullByDefault
 class SyncRecordWriterFactoryImpl implements SyncRecordWriterFactory {
 
+	private final MessageFactory messageFactory;
 	private final RecordWriterFactory recordWriterFactory;
 
 	@Inject
-	SyncRecordWriterFactoryImpl(RecordWriterFactory recordWriterFactory) {
+	SyncRecordWriterFactoryImpl(MessageFactory messageFactory,
+			RecordWriterFactory recordWriterFactory) {
+		this.messageFactory = messageFactory;
 		this.recordWriterFactory = recordWriterFactory;
 	}
 
 	@Override
 	public SyncRecordWriter createRecordWriter(OutputStream out) {
 		RecordWriter writer = recordWriterFactory.createRecordWriter(out);
-		return new SyncRecordWriterImpl(writer);
+		return new SyncRecordWriterImpl(messageFactory, writer);
 	}
 }
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordWriterImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordWriterImpl.java
index 3714df6d2d0491de3914bea79783f3fab697c87f..583f4eb626a90c2c24892efd0c236565c15a06bb 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordWriterImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/SyncRecordWriterImpl.java
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.record.Record;
 import org.briarproject.bramble.api.record.RecordWriter;
 import org.briarproject.bramble.api.sync.Ack;
 import org.briarproject.bramble.api.sync.Message;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.sync.MessageId;
 import org.briarproject.bramble.api.sync.Offer;
 import org.briarproject.bramble.api.sync.Request;
@@ -25,10 +26,12 @@ import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
 @NotNullByDefault
 class SyncRecordWriterImpl implements SyncRecordWriter {
 
+	private final MessageFactory messageFactory;
 	private final RecordWriter writer;
 	private final ByteArrayOutputStream payload = new ByteArrayOutputStream();
 
-	SyncRecordWriterImpl(RecordWriter writer) {
+	SyncRecordWriterImpl(MessageFactory messageFactory, RecordWriter writer) {
+		this.messageFactory = messageFactory;
 		this.writer = writer;
 	}
 
@@ -46,7 +49,8 @@ class SyncRecordWriterImpl implements SyncRecordWriter {
 
 	@Override
 	public void writeMessage(Message m) throws IOException {
-		writer.writeRecord(new Record(PROTOCOL_VERSION, MESSAGE, m.getRaw()));
+		byte[] raw = messageFactory.getRawMessage(m);
+		writer.writeRecord(new Record(PROTOCOL_VERSION, MESSAGE, raw));
 	}
 
 	@Override
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/client/BdfMessageValidatorTest.java b/bramble-core/src/test/java/org/briarproject/bramble/client/BdfMessageValidatorTest.java
index 0845586eaa11ae8fca1a6a7e572440fd71614957..72b3869f8b7be5471f4ac69b48fc321b0c174743 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/client/BdfMessageValidatorTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/client/BdfMessageValidatorTest.java
@@ -16,10 +16,8 @@ import org.jmock.Expectations;
 import org.jmock.lib.legacy.ClassImposteriser;
 import org.junit.Test;
 
-import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
 import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
 import static org.briarproject.bramble.test.TestUtils.getMessage;
-import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 
@@ -58,8 +56,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(clock).currentTimeMillis();
 			will(returnValue(timestamp - MAX_CLOCK_DIFFERENCE));
-			oneOf(clientHelper).toList(raw, MESSAGE_HEADER_LENGTH,
-					raw.length - MESSAGE_HEADER_LENGTH);
+			oneOf(clientHelper).toList(message.getBody());
 			will(returnValue(body));
 			oneOf(metadataEncoder).encode(dictionary);
 			will(returnValue(meta));
@@ -84,18 +81,11 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
 
 	@Test(expected = InvalidMessageException.class)
 	public void testRejectsTooShortMessage() throws Exception {
-		byte[] invalidRaw = getRandomBytes(MESSAGE_HEADER_LENGTH);
-		// Use a mock message so the length of the raw message can be invalid
-		Message invalidMessage = context.mock(Message.class);
+		Message invalidMessage = getMessage(groupId, 0);
 
 		context.checking(new Expectations() {{
-			//noinspection ResultOfMethodCallIgnored
-			oneOf(invalidMessage).getTimestamp();
-			will(returnValue(timestamp));
 			oneOf(clock).currentTimeMillis();
 			will(returnValue(timestamp));
-			oneOf(invalidMessage).getRaw();
-			will(returnValue(invalidRaw));
 		}});
 
 		failIfSubclassIsCalled.validateMessage(invalidMessage, group);
@@ -103,13 +93,12 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
 
 	@Test
 	public void testAcceptsMinLengthMessage() throws Exception {
-		Message shortMessage = getMessage(groupId, MESSAGE_HEADER_LENGTH + 1);
+		Message shortMessage = getMessage(groupId, 1);
 
 		context.checking(new Expectations() {{
 			oneOf(clock).currentTimeMillis();
 			will(returnValue(timestamp));
-			oneOf(clientHelper).toList(shortMessage.getRaw(),
-					MESSAGE_HEADER_LENGTH, 1);
+			oneOf(clientHelper).toList(shortMessage.getBody());
 			will(returnValue(body));
 			oneOf(metadataEncoder).encode(dictionary);
 			will(returnValue(meta));
@@ -137,8 +126,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(clock).currentTimeMillis();
 			will(returnValue(timestamp));
-			oneOf(clientHelper).toList(raw, MESSAGE_HEADER_LENGTH,
-					raw.length - MESSAGE_HEADER_LENGTH);
+			oneOf(clientHelper).toList(message.getBody());
 			will(throwException(new FormatException()));
 		}});
 
@@ -150,8 +138,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(clock).currentTimeMillis();
 			will(returnValue(timestamp));
-			oneOf(clientHelper).toList(raw, MESSAGE_HEADER_LENGTH,
-					raw.length - MESSAGE_HEADER_LENGTH);
+			oneOf(clientHelper).toList(message.getBody());
 			will(returnValue(body));
 		}});
 
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseMigrationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseMigrationTest.java
index 0740bdfc2301bff10330aba7dc99114936f0ace3..8b84ea2aa478279f8eb1a7195f26113509e8aea4 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseMigrationTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseMigrationTest.java
@@ -7,10 +7,12 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.settings.Settings;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 import org.briarproject.bramble.system.SystemClock;
 import org.briarproject.bramble.test.BrambleMockTestCase;
 import org.briarproject.bramble.test.TestDatabaseConfig;
+import org.briarproject.bramble.test.TestMessageFactory;
 import org.briarproject.bramble.test.TestUtils;
 import org.jmock.Expectations;
 import org.junit.After;
@@ -45,6 +47,7 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
 
 	protected final DatabaseConfig config =
 			new TestDatabaseConfig(testDir, 1024 * 1024);
+	protected final MessageFactory messageFactory = new TestMessageFactory();
 	protected final SecretKey key = getSecretKey();
 	protected final Clock clock = new SystemClock();
 
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabasePerformanceComparisonTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabasePerformanceComparisonTest.java
index 3f5657d11d03a9b7b616163a9a7a5eb952e87189..86be2afdbbb0ba9b42b8065744baf9d431668c93 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabasePerformanceComparisonTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabasePerformanceComparisonTest.java
@@ -3,9 +3,11 @@ package org.briarproject.bramble.db;
 import org.briarproject.bramble.api.crypto.SecretKey;
 import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 import org.briarproject.bramble.system.SystemClock;
 import org.briarproject.bramble.test.TestDatabaseConfig;
+import org.briarproject.bramble.test.TestMessageFactory;
 import org.briarproject.bramble.test.UTest;
 
 import java.io.IOException;
@@ -30,7 +32,8 @@ public abstract class DatabasePerformanceComparisonTest
 	private SecretKey databaseKey = getSecretKey();
 
 	abstract Database<Connection> createDatabase(boolean conditionA,
-			DatabaseConfig databaseConfig, Clock clock);
+			DatabaseConfig databaseConfig, MessageFactory messageFactory,
+			Clock clock);
 
 	@Override
 	protected void benchmark(String name,
@@ -73,7 +76,8 @@ public abstract class DatabasePerformanceComparisonTest
 	private Database<Connection> openDatabase(boolean conditionA)
 			throws DbException {
 		Database<Connection> db = createDatabase(conditionA,
-				new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
+				new TestDatabaseConfig(testDir, MAX_SIZE),
+				new TestMessageFactory(), new SystemClock());
 		db.open(databaseKey, null);
 		return db;
 	}
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseTraceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseTraceTest.java
index cf90b7fd09ef9c673631f706b57677edc92eda93..466b1d7c5e90251b01b02d933dbf7248d16d1fc7 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseTraceTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseTraceTest.java
@@ -3,9 +3,11 @@ package org.briarproject.bramble.db;
 import org.briarproject.bramble.api.crypto.SecretKey;
 import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 import org.briarproject.bramble.system.SystemClock;
 import org.briarproject.bramble.test.TestDatabaseConfig;
+import org.briarproject.bramble.test.TestMessageFactory;
 import org.briarproject.bramble.util.IoUtils;
 
 import java.io.File;
@@ -24,7 +26,7 @@ public abstract class DatabaseTraceTest extends DatabasePerformanceTest {
 	private SecretKey databaseKey = getSecretKey();
 
 	abstract Database<Connection> createDatabase(DatabaseConfig databaseConfig,
-			Clock clock);
+			MessageFactory messageFactory, Clock clock);
 
 	@Nullable
 	protected abstract File getTraceFile();
@@ -46,7 +48,8 @@ public abstract class DatabaseTraceTest extends DatabasePerformanceTest {
 
 	private Database<Connection> openDatabase() throws DbException {
 		Database<Connection> db = createDatabase(
-				new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
+				new TestDatabaseConfig(testDir, MAX_SIZE),
+				new TestMessageFactory(), new SystemClock());
 		db.open(databaseKey, null);
 		return db;
 	}
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabasePerformanceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabasePerformanceTest.java
index fdac3e58c93f61f0b072929762a4d210ec6b4ce2..f682997f7b926e0f0819bbd141b7122d6382decb 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabasePerformanceTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabasePerformanceTest.java
@@ -1,6 +1,7 @@
 package org.briarproject.bramble.db;
 
 import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 import org.junit.Ignore;
 
@@ -13,7 +14,8 @@ public class H2DatabasePerformanceTest extends SingleDatabasePerformanceTest {
 	}
 
 	@Override
-	protected JdbcDatabase createDatabase(DatabaseConfig config, Clock clock) {
-		return new H2Database(config, clock);
+	protected JdbcDatabase createDatabase(DatabaseConfig config,
+			MessageFactory messageFactory, Clock clock) {
+		return new H2Database(config, messageFactory, clock);
 	}
 }
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabaseTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabaseTest.java
index fb0f6e4a3071be2ec9e50c0d6f6147cbf6ed8399..19822113d449383461c2915668156ed07ef79b42 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabaseTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabaseTest.java
@@ -1,16 +1,14 @@
 package org.briarproject.bramble.db;
 
 import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 
 public class H2DatabaseTest extends JdbcDatabaseTest {
 
-	public H2DatabaseTest() throws Exception {
-		super();
-	}
-
 	@Override
-	protected JdbcDatabase createDatabase(DatabaseConfig config, Clock clock) {
-		return new H2Database(config, clock);
+	protected JdbcDatabase createDatabase(DatabaseConfig config,
+			MessageFactory messageFactory, Clock clock) {
+		return new H2Database(config, messageFactory, clock);
 	}
 }
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabaseTraceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabaseTraceTest.java
index 2b12f88b03ff148e2f6a9556de786fb612608152..b87f3992a6e0cdd2b6ae23d00543d3b1e14b1932 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabaseTraceTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabaseTraceTest.java
@@ -1,6 +1,7 @@
 package org.briarproject.bramble.db;
 
 import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 import org.junit.Ignore;
 
@@ -14,8 +15,8 @@ public class H2DatabaseTraceTest extends DatabaseTraceTest {
 
 	@Override
 	Database<Connection> createDatabase(DatabaseConfig databaseConfig,
-			Clock clock) {
-		return new H2Database(databaseConfig, clock) {
+			MessageFactory messageFactory, Clock clock) {
+		return new H2Database(databaseConfig, messageFactory, clock) {
 			@Override
 			@Nonnull
 			String getUrl() {
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/H2HyperSqlDatabasePerformanceComparisonTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/H2HyperSqlDatabasePerformanceComparisonTest.java
index b51cca72a8a04c38a8d01df3f24af1435cd1c7e9..8fd8d35c887c5e6b0412eba3455a9fc7b928b326 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/H2HyperSqlDatabasePerformanceComparisonTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/H2HyperSqlDatabasePerformanceComparisonTest.java
@@ -1,6 +1,7 @@
 package org.briarproject.bramble.db;
 
 import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 import org.junit.Ignore;
 
@@ -12,9 +13,11 @@ public class H2HyperSqlDatabasePerformanceComparisonTest
 
 	@Override
 	Database<Connection> createDatabase(boolean conditionA,
-			DatabaseConfig databaseConfig, Clock clock) {
-		if (conditionA) return new H2Database(databaseConfig, clock);
-		else return new HyperSqlDatabase(databaseConfig, clock);
+			DatabaseConfig databaseConfig, MessageFactory messageFactory,
+			Clock clock) {
+		if (conditionA)
+			return new H2Database(databaseConfig, messageFactory, clock);
+		else return new HyperSqlDatabase(databaseConfig, messageFactory, clock);
 	}
 
 	@Override
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/H2MigrationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/H2MigrationTest.java
index 29bdbfa5f5b962a427ffdeeef8f9f94429be8ca4..4646a05b4c6d84904f4b70e67175068643fd7236 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/H2MigrationTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/H2MigrationTest.java
@@ -11,7 +11,7 @@ public class H2MigrationTest extends DatabaseMigrationTest {
 	@Override
 	Database<Connection> createDatabase(
 			List<Migration<Connection>> migrations) {
-		return new H2Database(config, clock) {
+		return new H2Database(config, messageFactory, clock) {
 			@Override
 			List<Migration<Connection>> getMigrations() {
 				return migrations;
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/H2SelfDatabasePerformanceComparisonTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/H2SelfDatabasePerformanceComparisonTest.java
index 4fb686043fbd4d6e3887d163983f1a2b6dba4a6d..5af317910e952f8443d66d48ab2e45a6d6932315 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/H2SelfDatabasePerformanceComparisonTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/H2SelfDatabasePerformanceComparisonTest.java
@@ -1,6 +1,7 @@
 package org.briarproject.bramble.db;
 
 import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 import org.junit.Ignore;
 
@@ -17,8 +18,9 @@ public class H2SelfDatabasePerformanceComparisonTest
 
 	@Override
 	Database<Connection> createDatabase(boolean conditionA,
-			DatabaseConfig databaseConfig, Clock clock) {
-		return new H2Database(databaseConfig, clock);
+			DatabaseConfig databaseConfig, MessageFactory messageFactory,
+			Clock clock) {
+		return new H2Database(databaseConfig, messageFactory, clock);
 	}
 
 	@Override
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/H2SleepDatabasePerformanceComparisonTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/H2SleepDatabasePerformanceComparisonTest.java
index 73d382bc5192956da0a420b1d66fd04a3b4d9473..641f20805957a152cbf49824f7eb3923cb28ee7d 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/H2SleepDatabasePerformanceComparisonTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/H2SleepDatabasePerformanceComparisonTest.java
@@ -3,6 +3,7 @@ package org.briarproject.bramble.db;
 import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 import org.junit.Ignore;
 
@@ -19,11 +20,12 @@ public class H2SleepDatabasePerformanceComparisonTest
 
 	@Override
 	Database<Connection> createDatabase(boolean conditionA,
-			DatabaseConfig databaseConfig, Clock clock) {
+			DatabaseConfig databaseConfig, MessageFactory messageFactory,
+			Clock clock) {
 		if (conditionA) {
-			return new H2Database(databaseConfig, clock);
+			return new H2Database(databaseConfig, messageFactory, clock);
 		} else {
-			return new H2Database(databaseConfig, clock) {
+			return new H2Database(databaseConfig, messageFactory, clock) {
 				@Override
 				@NotNullByDefault
 				public void commitTransaction(Connection txn)
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlDatabasePerformanceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlDatabasePerformanceTest.java
index 39c69f3d795c0dd1a0c63275b868f250352021c2..27b39e9246aee7372be3dad9ff97f6a2a1d41236 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlDatabasePerformanceTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlDatabasePerformanceTest.java
@@ -1,6 +1,7 @@
 package org.briarproject.bramble.db;
 
 import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 import org.junit.Ignore;
 
@@ -14,7 +15,8 @@ public class HyperSqlDatabasePerformanceTest
 	}
 
 	@Override
-	protected JdbcDatabase createDatabase(DatabaseConfig config, Clock clock) {
-		return new HyperSqlDatabase(config, clock);
+	protected JdbcDatabase createDatabase(DatabaseConfig config,
+			MessageFactory messageFactory, Clock clock) {
+		return new HyperSqlDatabase(config, messageFactory, clock);
 	}
 }
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlDatabaseTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlDatabaseTest.java
index 64f666fc426bae11cd8534b635adc331f03de07d..bf785e31e356f5005aa1a558222628fff2992437 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlDatabaseTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlDatabaseTest.java
@@ -1,16 +1,14 @@
 package org.briarproject.bramble.db;
 
 import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 
 public class HyperSqlDatabaseTest extends JdbcDatabaseTest {
 
-	public HyperSqlDatabaseTest() throws Exception {
-		super();
-	}
-
 	@Override
-	protected JdbcDatabase createDatabase(DatabaseConfig config, Clock clock) {
-		return new HyperSqlDatabase(config, clock);
+	protected JdbcDatabase createDatabase(DatabaseConfig config,
+			MessageFactory messageFactory, Clock clock) {
+		return new HyperSqlDatabase(config, messageFactory ,clock);
 	}
 }
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlMigrationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlMigrationTest.java
index 70383ec27f6168cdb680a4fe85b8604d3625344a..f4468728f0697496ad2965a6936bf3badec3b6b0 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlMigrationTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlMigrationTest.java
@@ -9,9 +9,9 @@ import java.util.List;
 public class HyperSqlMigrationTest extends DatabaseMigrationTest {
 
 	@Override
-	Database<Connection> createDatabase(List<Migration<Connection>> migrations)
-			throws Exception {
-		return new HyperSqlDatabase(config, clock) {
+	Database<Connection> createDatabase(
+			List<Migration<Connection>> migrations) {
+		return new HyperSqlDatabase(config, messageFactory, clock) {
 			@Override
 			List<Migration<Connection>> getMigrations() {
 				return migrations;
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
index 790a27cb05157d9f121432e5be5d15360fe7ff89..6e434f90c561eefeab84319024f6948b614a42c5 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.sync.ClientId;
 import org.briarproject.bramble.api.sync.Group;
 import org.briarproject.bramble.api.sync.GroupId;
 import org.briarproject.bramble.api.sync.Message;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.sync.MessageId;
 import org.briarproject.bramble.api.sync.MessageStatus;
 import org.briarproject.bramble.api.sync.ValidationManager.State;
@@ -27,6 +28,7 @@ import org.briarproject.bramble.api.transport.TransportKeys;
 import org.briarproject.bramble.system.SystemClock;
 import org.briarproject.bramble.test.BrambleTestCase;
 import org.briarproject.bramble.test.TestDatabaseConfig;
+import org.briarproject.bramble.test.TestMessageFactory;
 import org.briarproject.bramble.test.TestUtils;
 import org.junit.After;
 import org.junit.Before;
@@ -112,7 +114,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 	}
 
 	protected abstract JdbcDatabase createDatabase(DatabaseConfig config,
-			Clock clock);
+			MessageFactory messageFactory, Clock clock);
 
 	@Before
 	public void setUp() {
@@ -144,8 +146,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 		assertTrue(db.containsContact(txn, contactId));
 		assertTrue(db.containsGroup(txn, groupId));
 		assertTrue(db.containsMessage(txn, messageId));
-		assertArrayEquals(message.getRaw(),
-				db.getMessage(txn, messageId).getRaw());
+		assertArrayEquals(message.getBody(),
+				db.getMessage(txn, messageId).getBody());
 
 		// Delete the records
 		db.removeMessage(txn, messageId);
@@ -1644,7 +1646,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 		assertEquals(messageId, m.getId());
 		assertEquals(groupId, m.getGroupId());
 		assertEquals(message.getTimestamp(), m.getTimestamp());
-		assertArrayEquals(message.getRaw(), m.getRaw());
+		assertArrayEquals(message.getBody(), m.getBody());
 
 		// Delete the message
 		db.deleteMessage(txn, messageId);
@@ -1727,7 +1729,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 	@Test
 	public void testGetNextSendTime() throws Exception {
 		long now = System.currentTimeMillis();
-		Database<Connection> db = open(false, new StoppedClock(now));
+		Database<Connection> db = open(false, new TestMessageFactory(),
+				new StoppedClock(now));
 		Connection txn = db.startTransaction();
 
 		// Add a contact, a group and a message
@@ -1807,13 +1810,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
 	}
 
 	private Database<Connection> open(boolean resume) throws Exception {
-		return open(resume, new SystemClock());
+		return open(resume, new TestMessageFactory(), new SystemClock());
 	}
 
-	private Database<Connection> open(boolean resume, Clock clock)
-			throws Exception {
-		Database<Connection> db = createDatabase(
-				new TestDatabaseConfig(testDir, MAX_SIZE), clock);
+	private Database<Connection> open(boolean resume,
+			MessageFactory messageFactory, Clock clock) throws Exception {
+		Database<Connection> db =
+				createDatabase(new TestDatabaseConfig(testDir, MAX_SIZE),
+						messageFactory, clock);
 		if (!resume) TestUtils.deleteTestDirectory(testDir);
 		db.open(key, null);
 		return db;
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/SingleDatabasePerformanceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/SingleDatabasePerformanceTest.java
index 1b93b1f1e306f577cb36db29dcbb06bc2725a713..1984e0123fc42a1dfbdb92198c78fc138a3888b3 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/SingleDatabasePerformanceTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/SingleDatabasePerformanceTest.java
@@ -3,9 +3,11 @@ package org.briarproject.bramble.db;
 import org.briarproject.bramble.api.crypto.SecretKey;
 import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.sync.MessageFactory;
 import org.briarproject.bramble.api.system.Clock;
 import org.briarproject.bramble.system.SystemClock;
 import org.briarproject.bramble.test.TestDatabaseConfig;
+import org.briarproject.bramble.test.TestMessageFactory;
 
 import java.io.IOException;
 import java.sql.Connection;
@@ -21,7 +23,7 @@ public abstract class SingleDatabasePerformanceTest
 		extends DatabasePerformanceTest {
 
 	abstract Database<Connection> createDatabase(DatabaseConfig databaseConfig,
-			Clock clock);
+			MessageFactory messageFactory, Clock clock);
 
 	private SecretKey databaseKey = getSecretKey();
 
@@ -43,7 +45,8 @@ public abstract class SingleDatabasePerformanceTest
 
 	private Database<Connection> openDatabase() throws DbException {
 		Database<Connection> db = createDatabase(
-				new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
+				new TestDatabaseConfig(testDir, MAX_SIZE),
+				new TestMessageFactory(), new SystemClock());
 		db.open(databaseKey, null);
 		return db;
 	}
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncIntegrationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncIntegrationTest.java
index ef106c97b26a1fcbd5599347394810b79a23e6d6..6c9f97f2a1ac01fd04cfce0ab55b492c6b521ed0 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncIntegrationTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/sync/SyncIntegrationTest.java
@@ -170,6 +170,6 @@ public class SyncIntegrationTest extends BrambleTestCase {
 				m2.getGroupId().getBytes());
 		assertEquals(m1.getTimestamp(), m2.getTimestamp());
 		assertEquals(m1.getRawLength(), m2.getRawLength());
-		assertArrayEquals(m1.getRaw(), m2.getRaw());
+		assertArrayEquals(m1.getBody(), m2.getBody());
 	}
 }
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/TestMessageFactory.java b/bramble-core/src/test/java/org/briarproject/bramble/test/TestMessageFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a7f090952eaec0d50878c65937209bccd036f71
--- /dev/null
+++ b/bramble-core/src/test/java/org/briarproject/bramble/test/TestMessageFactory.java
@@ -0,0 +1,30 @@
+package org.briarproject.bramble.test;
+
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.sync.GroupId;
+import org.briarproject.bramble.api.sync.Message;
+import org.briarproject.bramble.api.sync.MessageFactory;
+
+import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
+
+@NotNullByDefault
+public class TestMessageFactory implements MessageFactory {
+
+	@Override
+	public Message createMessage(GroupId g, long timestamp, byte[] body) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public Message createMessage(byte[] raw) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public byte[] getRawMessage(Message m) {
+		byte[] body = m.getBody();
+		byte[] raw = new byte[MESSAGE_HEADER_LENGTH + body.length];
+		System.arraycopy(body, 0, raw, MESSAGE_HEADER_LENGTH, body.length);
+		return raw;
+	}
+}
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/ValidatorTestCase.java b/bramble-core/src/test/java/org/briarproject/bramble/test/ValidatorTestCase.java
index 9958573de8d1fd2565df261deda486111aaa4dde..de4b985695a6f22a928a272a7fb2e49dd3f778d9 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/test/ValidatorTestCase.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/test/ValidatorTestCase.java
@@ -30,7 +30,6 @@ public abstract class ValidatorTestCase extends BrambleMockTestCase {
 	protected final Message message = getMessage(groupId);
 	protected final MessageId messageId = message.getId();
 	protected final long timestamp = message.getTimestamp();
-	protected final byte[] raw = message.getRaw();
 	protected final Author author = getAuthor();
 	protected final BdfList authorList = BdfList.of(
 			author.getFormatVersion(),
diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/MessageEncoderTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/MessageEncoderTest.java
index 155bd6bb1b71ee4a721c1ee92e9eb0234a32827c..6f1af6f746167146797a2ed38ec384944d3117a8 100644
--- a/briar-core/src/test/java/org/briarproject/briar/introduction/MessageEncoderTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/introduction/MessageEncoderTest.java
@@ -12,10 +12,8 @@ import org.jmock.Expectations;
 import org.junit.Test;
 
 import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
-import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
 import static org.briarproject.bramble.test.TestUtils.getAuthor;
 import static org.briarproject.bramble.test.TestUtils.getMessage;
-import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
 import static org.briarproject.bramble.test.TestUtils.getRandomId;
 import static org.briarproject.bramble.util.StringUtils.getRandomString;
 import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_REQUEST_MESSAGE_LENGTH;
@@ -30,9 +28,10 @@ public class MessageEncoderTest extends BrambleMockTestCase {
 			new MessageEncoderImpl(clientHelper, messageFactory);
 
 	private final GroupId groupId = new GroupId(getRandomId());
-	private final Message message = getMessage(groupId, MAX_MESSAGE_LENGTH);
+	private final Message message =
+			getMessage(groupId, MAX_MESSAGE_BODY_LENGTH);
 	private final long timestamp = message.getTimestamp();
-	private final byte[] body = getRandomBytes(MAX_MESSAGE_BODY_LENGTH);
+	private final byte[] body = message.getBody();
 	private final Author author = getAuthor();
 	private final BdfList authorList = new BdfList();
 	private final String text = getRandomString(MAX_REQUEST_MESSAGE_LENGTH);