diff --git a/briar-api/src/org/briarproject/api/sync/PrivateGroupFactory.java b/briar-api/src/org/briarproject/api/sync/PrivateGroupFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..0de62ebc96b5d5807b44a89b5f0bb1999353d21d
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/sync/PrivateGroupFactory.java
@@ -0,0 +1,9 @@
+package org.briarproject.api.sync;
+
+import org.briarproject.api.contact.Contact;
+
+public interface PrivateGroupFactory {
+
+	/** Creates a group for the given client to share with the given contact. */
+	Group createPrivateGroup(ClientId clientId, Contact contact);
+}
diff --git a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java
index 49c4a357c813ffccb24f2ffcbc42a9832577cb82..90c8f426e2f6e7b680991e07f659e8fde2d8f98d 100644
--- a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java
+++ b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java
@@ -3,7 +3,6 @@ package org.briarproject.messaging;
 import com.google.inject.Inject;
 
 import org.briarproject.api.FormatException;
-import org.briarproject.api.UniqueId;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager.AddContactHook;
@@ -11,28 +10,24 @@ import org.briarproject.api.contact.ContactManager.RemoveContactHook;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfReader;
 import org.briarproject.api.data.BdfReaderFactory;
-import org.briarproject.api.data.BdfWriter;
-import org.briarproject.api.data.BdfWriterFactory;
 import org.briarproject.api.data.MetadataEncoder;
 import org.briarproject.api.data.MetadataParser;
 import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Metadata;
 import org.briarproject.api.db.NoSuchContactException;
-import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.messaging.MessagingManager;
 import org.briarproject.api.messaging.PrivateMessage;
 import org.briarproject.api.messaging.PrivateMessageHeader;
 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.MessageId;
 import org.briarproject.api.sync.MessageStatus;
+import org.briarproject.api.sync.PrivateGroupFactory;
 import org.briarproject.util.StringUtils;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -55,21 +50,20 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
 			Logger.getLogger(MessagingManagerImpl.class.getName());
 
 	private final DatabaseComponent db;
-	private final GroupFactory groupFactory;
+	private final PrivateGroupFactory privateGroupFactory;
 	private final BdfReaderFactory bdfReaderFactory;
-	private final BdfWriterFactory bdfWriterFactory;
 	private final MetadataEncoder metadataEncoder;
 	private final MetadataParser metadataParser;
 
 	@Inject
-	MessagingManagerImpl(DatabaseComponent db, GroupFactory groupFactory,
+	MessagingManagerImpl(DatabaseComponent db,
+			PrivateGroupFactory privateGroupFactory,
 			BdfReaderFactory bdfReaderFactory,
-			BdfWriterFactory bdfWriterFactory, MetadataEncoder metadataEncoder,
+			MetadataEncoder metadataEncoder,
 			MetadataParser metadataParser) {
 		this.db = db;
-		this.groupFactory = groupFactory;
+		this.privateGroupFactory = privateGroupFactory;
 		this.bdfReaderFactory = bdfReaderFactory;
-		this.bdfWriterFactory = bdfWriterFactory;
 		this.metadataEncoder = metadataEncoder;
 		this.metadataParser = metadataParser;
 	}
@@ -95,30 +89,7 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
 	}
 
 	private Group getConversationGroup(Contact c) {
-		AuthorId local = c.getLocalAuthorId();
-		AuthorId remote = c.getAuthor().getId();
-		byte[] descriptor = createGroupDescriptor(local, remote);
-		return groupFactory.createGroup(CLIENT_ID, descriptor);
-	}
-
-	private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		BdfWriter w = bdfWriterFactory.createWriter(out);
-		try {
-			w.writeListStart();
-			if (UniqueId.IdComparator.INSTANCE.compare(local, remote) < 0) {
-				w.writeRaw(local.getBytes());
-				w.writeRaw(remote.getBytes());
-			} else {
-				w.writeRaw(remote.getBytes());
-				w.writeRaw(local.getBytes());
-			}
-			w.writeListEnd();
-		} catch (IOException e) {
-			// Shouldn't happen with ByteArrayOutputStream
-			throw new RuntimeException(e);
-		}
-		return out.toByteArray();
+		return privateGroupFactory.createPrivateGroup(CLIENT_ID, c);
 	}
 
 	@Override
diff --git a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java
index 036dd125be6314d7ec602c875e4b90132c826b58..10328e3c26c3bc23b39131a88f368fbe79c9da89 100644
--- a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java
+++ b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java
@@ -5,7 +5,6 @@ import com.google.inject.Inject;
 import org.briarproject.api.DeviceId;
 import org.briarproject.api.FormatException;
 import org.briarproject.api.TransportId;
-import org.briarproject.api.UniqueId;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager.AddContactHook;
@@ -21,7 +20,6 @@ import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Metadata;
 import org.briarproject.api.db.NoSuchSubscriptionException;
-import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.properties.TransportProperties;
 import org.briarproject.api.properties.TransportPropertyManager;
 import org.briarproject.api.sync.ClientId;
@@ -31,6 +29,7 @@ import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageFactory;
 import org.briarproject.api.sync.MessageId;
+import org.briarproject.api.sync.PrivateGroupFactory;
 import org.briarproject.api.system.Clock;
 import org.briarproject.util.StringUtils;
 
@@ -61,7 +60,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
 			Logger.getLogger(TransportPropertyManagerImpl.class.getName());
 
 	private final DatabaseComponent db;
-	private final GroupFactory groupFactory;
+	private final PrivateGroupFactory privateGroupFactory;
 	private final MessageFactory messageFactory;
 	private final BdfReaderFactory bdfReaderFactory;
 	private final BdfWriterFactory bdfWriterFactory;
@@ -75,12 +74,12 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
 
 	@Inject
 	TransportPropertyManagerImpl(DatabaseComponent db,
-			GroupFactory groupFactory, MessageFactory messageFactory,
-			BdfReaderFactory bdfReaderFactory,
+			GroupFactory groupFactory, PrivateGroupFactory privateGroupFactory,
+			MessageFactory messageFactory, BdfReaderFactory bdfReaderFactory,
 			BdfWriterFactory bdfWriterFactory, MetadataEncoder metadataEncoder,
 			MetadataParser metadataParser, Clock clock) {
 		this.db = db;
-		this.groupFactory = groupFactory;
+		this.privateGroupFactory = privateGroupFactory;
 		this.messageFactory = messageFactory;
 		this.bdfReaderFactory = bdfReaderFactory;
 		this.bdfWriterFactory = bdfWriterFactory;
@@ -118,30 +117,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
 	}
 
 	private Group getContactGroup(Contact c) {
-		AuthorId local = c.getLocalAuthorId();
-		AuthorId remote = c.getAuthor().getId();
-		byte[] descriptor = encodeGroupDescriptor(local, remote);
-		return groupFactory.createGroup(CLIENT_ID, descriptor);
-	}
-
-	private byte[] encodeGroupDescriptor(AuthorId local, AuthorId remote) {
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		BdfWriter w = bdfWriterFactory.createWriter(out);
-		try {
-			w.writeListStart();
-			if (UniqueId.IdComparator.INSTANCE.compare(local, remote) < 0) {
-				w.writeRaw(local.getBytes());
-				w.writeRaw(remote.getBytes());
-			} else {
-				w.writeRaw(remote.getBytes());
-				w.writeRaw(local.getBytes());
-			}
-			w.writeListEnd();
-		} catch (IOException e) {
-			// Shouldn't happen with ByteArrayOutputStream
-			throw new RuntimeException(e);
-		}
-		return out.toByteArray();
+		return privateGroupFactory.createPrivateGroup(CLIENT_ID, c);
 	}
 
 	private void storeMessage(GroupId g, DeviceId dev, TransportId t,
diff --git a/briar-core/src/org/briarproject/sync/PrivateGroupFactoryImpl.java b/briar-core/src/org/briarproject/sync/PrivateGroupFactoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea5db99ae4c23cc52f783743bf088b510a4e3cde
--- /dev/null
+++ b/briar-core/src/org/briarproject/sync/PrivateGroupFactoryImpl.java
@@ -0,0 +1,54 @@
+package org.briarproject.sync;
+
+import org.briarproject.api.UniqueId;
+import org.briarproject.api.contact.Contact;
+import org.briarproject.api.data.BdfWriter;
+import org.briarproject.api.data.BdfWriterFactory;
+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.PrivateGroupFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+class PrivateGroupFactoryImpl implements PrivateGroupFactory {
+
+	private final GroupFactory groupFactory;
+	private final BdfWriterFactory bdfWriterFactory;
+
+	PrivateGroupFactoryImpl(GroupFactory groupFactory,
+			BdfWriterFactory bdfWriterFactory) {
+		this.groupFactory = groupFactory;
+		this.bdfWriterFactory = bdfWriterFactory;
+	}
+
+	@Override
+	public Group createPrivateGroup(ClientId clientId, Contact contact) {
+		AuthorId local = contact.getLocalAuthorId();
+		AuthorId remote = contact.getAuthor().getId();
+		byte[] descriptor = createGroupDescriptor(local, remote);
+		return groupFactory.createGroup(clientId, descriptor);
+	}
+
+	private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		BdfWriter w = bdfWriterFactory.createWriter(out);
+		try {
+			w.writeListStart();
+			if (UniqueId.IdComparator.INSTANCE.compare(local, remote) < 0) {
+				w.writeRaw(local.getBytes());
+				w.writeRaw(remote.getBytes());
+			} else {
+				w.writeRaw(remote.getBytes());
+				w.writeRaw(local.getBytes());
+			}
+			w.writeListEnd();
+		} catch (IOException e) {
+			// Shouldn't happen with ByteArrayOutputStream
+			throw new RuntimeException(e);
+		}
+		return out.toByteArray();
+	}
+}
diff --git a/briar-core/src/org/briarproject/sync/SyncModule.java b/briar-core/src/org/briarproject/sync/SyncModule.java
index f90c6c1ecc05642840ff48579ee27f4014c2505a..0b4a9a2c3b1bb8033d85925a00e2b64408fbc8a5 100644
--- a/briar-core/src/org/briarproject/sync/SyncModule.java
+++ b/briar-core/src/org/briarproject/sync/SyncModule.java
@@ -13,6 +13,7 @@ import org.briarproject.api.sync.GroupFactory;
 import org.briarproject.api.sync.MessageFactory;
 import org.briarproject.api.sync.PacketReaderFactory;
 import org.briarproject.api.sync.PacketWriterFactory;
+import org.briarproject.api.sync.PrivateGroupFactory;
 import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.SyncSessionFactory;
 import org.briarproject.api.sync.ValidationManager;
@@ -28,6 +29,7 @@ public class SyncModule extends AbstractModule {
 		bind(MessageFactory.class).to(MessageFactoryImpl.class);
 		bind(PacketReaderFactory.class).to(PacketReaderFactoryImpl.class);
 		bind(PacketWriterFactory.class).to(PacketWriterFactoryImpl.class);
+		bind(PrivateGroupFactory.class).to(PrivateGroupFactoryImpl.class);
 		bind(SyncSessionFactory.class).to(
 				SyncSessionFactoryImpl.class).in(Singleton.class);
 	}