diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java
index b3e08351b7999254fbba1e47628fcb3b0108792d..fffeb2dec86318d2e4c15eaf7e70e46c59cdfa63 100644
--- a/api/net/sf/briar/api/db/DatabaseComponent.java
+++ b/api/net/sf/briar/api/db/DatabaseComponent.java
@@ -7,17 +7,17 @@ import java.util.Map;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.Rating;
 import net.sf.briar.api.protocol.Ack;
-import net.sf.briar.api.protocol.AckWriter;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.Batch;
-import net.sf.briar.api.protocol.BatchWriter;
 import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupId;
 import net.sf.briar.api.protocol.Message;
-import net.sf.briar.api.protocol.SubscriptionWriter;
 import net.sf.briar.api.protocol.Subscriptions;
-import net.sf.briar.api.protocol.TransportWriter;
 import net.sf.briar.api.protocol.Transports;
+import net.sf.briar.api.protocol.writers.AckWriter;
+import net.sf.briar.api.protocol.writers.BatchWriter;
+import net.sf.briar.api.protocol.writers.SubscriptionWriter;
+import net.sf.briar.api.protocol.writers.TransportWriter;
 
 /**
  * Encapsulates the database implementation and exposes high-level operations
diff --git a/api/net/sf/briar/api/protocol/AckWriter.java b/api/net/sf/briar/api/protocol/writers/AckWriter.java
similarity index 78%
rename from api/net/sf/briar/api/protocol/AckWriter.java
rename to api/net/sf/briar/api/protocol/writers/AckWriter.java
index 0d9a18a2978a48367d5a2f506147624458f5cc8c..846b36144c6f607fae040fbff02397b00f2392f9 100644
--- a/api/net/sf/briar/api/protocol/AckWriter.java
+++ b/api/net/sf/briar/api/protocol/writers/AckWriter.java
@@ -1,7 +1,9 @@
-package net.sf.briar.api.protocol;
+package net.sf.briar.api.protocol.writers;
 
 import java.io.IOException;
 
+import net.sf.briar.api.protocol.BatchId;
+
 /** An interface for creating an ack. */
 public interface AckWriter {
 
diff --git a/api/net/sf/briar/api/protocol/BatchWriter.java b/api/net/sf/briar/api/protocol/writers/BatchWriter.java
similarity index 84%
rename from api/net/sf/briar/api/protocol/BatchWriter.java
rename to api/net/sf/briar/api/protocol/writers/BatchWriter.java
index 87a5fd3df10e11ee0de23b0856494beff3536c53..4b6ecc0e1a5d5950cdb55b400a9b1f500780a46f 100644
--- a/api/net/sf/briar/api/protocol/BatchWriter.java
+++ b/api/net/sf/briar/api/protocol/writers/BatchWriter.java
@@ -1,7 +1,9 @@
-package net.sf.briar.api.protocol;
+package net.sf.briar.api.protocol.writers;
 
 import java.io.IOException;
 
+import net.sf.briar.api.protocol.BatchId;
+
 /** An interface for creating a batch of messages. */
 public interface BatchWriter {
 
diff --git a/api/net/sf/briar/api/protocol/writers/PacketWriterFactory.java b/api/net/sf/briar/api/protocol/writers/PacketWriterFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac4d3c292c2999127c1fe7de7fe68465cfd1a45d
--- /dev/null
+++ b/api/net/sf/briar/api/protocol/writers/PacketWriterFactory.java
@@ -0,0 +1,14 @@
+package net.sf.briar.api.protocol.writers;
+
+import java.io.OutputStream;
+
+public interface PacketWriterFactory {
+
+	AckWriter createAckWriter(OutputStream out);
+
+	BatchWriter createBatchWriter(OutputStream out);
+
+	SubscriptionWriter createSubscriptionWriter(OutputStream out);
+
+	TransportWriter createTransportWriter(OutputStream out);
+}
diff --git a/api/net/sf/briar/api/protocol/SubscriptionWriter.java b/api/net/sf/briar/api/protocol/writers/SubscriptionWriter.java
similarity index 73%
rename from api/net/sf/briar/api/protocol/SubscriptionWriter.java
rename to api/net/sf/briar/api/protocol/writers/SubscriptionWriter.java
index 0cddd424bf469414531e636aa5236044902a2001..7e7d340d0c6c13f55d44c7c933e39143f93c4f91 100644
--- a/api/net/sf/briar/api/protocol/SubscriptionWriter.java
+++ b/api/net/sf/briar/api/protocol/writers/SubscriptionWriter.java
@@ -1,7 +1,9 @@
-package net.sf.briar.api.protocol;
+package net.sf.briar.api.protocol.writers;
 
 import java.io.IOException;
 
+import net.sf.briar.api.protocol.Group;
+
 /** An interface for creating a subscription update. */
 public interface SubscriptionWriter {
 
diff --git a/api/net/sf/briar/api/protocol/TransportWriter.java b/api/net/sf/briar/api/protocol/writers/TransportWriter.java
similarity index 85%
rename from api/net/sf/briar/api/protocol/TransportWriter.java
rename to api/net/sf/briar/api/protocol/writers/TransportWriter.java
index 637017cfe7b5e574e10a06a5114a1bbb15fe7902..5caa756c420308675868dac1b6fac131720836e3 100644
--- a/api/net/sf/briar/api/protocol/TransportWriter.java
+++ b/api/net/sf/briar/api/protocol/writers/TransportWriter.java
@@ -1,4 +1,4 @@
-package net.sf.briar.api.protocol;
+package net.sf.briar.api.protocol.writers;
 
 import java.io.IOException;
 import java.util.Map;
diff --git a/api/net/sf/briar/api/serial/Writer.java b/api/net/sf/briar/api/serial/Writer.java
index b3b7942b329f449a02522bf3a02f6edf1adfedcd..6b53486e27003ffa595a707dc26ddd3884ef4dc9 100644
--- a/api/net/sf/briar/api/serial/Writer.java
+++ b/api/net/sf/briar/api/serial/Writer.java
@@ -6,7 +6,6 @@ import java.util.Map;
 
 public interface Writer {
 
-	// FIXME: Remove this method
 	long getBytesWritten();
 
 	void writeBoolean(boolean b) throws IOException;
diff --git a/components/net/sf/briar/crypto/CryptoModule.java b/components/net/sf/briar/crypto/CryptoModule.java
index 93ee654ddd321e84d57d2ffa670fb3b990d65c20..93240a4839b21abcd9ee60f8464a6cca91085165 100644
--- a/components/net/sf/briar/crypto/CryptoModule.java
+++ b/components/net/sf/briar/crypto/CryptoModule.java
@@ -1,22 +1,59 @@
 package net.sf.briar.crypto;
 
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
 
 import net.sf.briar.api.crypto.KeyParser;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
 
 public class CryptoModule extends AbstractModule {
 
-	public static final String DIGEST_ALGO = "SHA-256";
-	public static final String KEY_PAIR_ALGO = "RSA";
-	public static final String SIGNATURE_ALGO = "SHA256withRSA";
+	private static final String DIGEST_ALGO = "SHA-256";
+	private static final String KEY_PAIR_ALGO = "RSA";
+	private static final String SIGNATURE_ALGO = "SHA256withRSA";
 
 	@Override
 	protected void configure() {
 		try {
 			bind(KeyParser.class).toInstance(new KeyParserImpl(KEY_PAIR_ALGO));
 		} catch(NoSuchAlgorithmException e) {
+			// FIXME: Can modules throw?
+			throw new RuntimeException(e);
+		}
+	}
+
+	@Provides
+	MessageDigest getMessageDigest() {
+		try {
+			return MessageDigest.getInstance(DIGEST_ALGO);
+		} catch(NoSuchAlgorithmException e) {
+			// FIXME: Providers should not throw
+			throw new RuntimeException(e);
+		}
+	}
+
+	@Provides
+	Signature getSignature() {
+		try {
+			return Signature.getInstance(SIGNATURE_ALGO);
+		} catch(NoSuchAlgorithmException e) {
+			// FIXME: Providers should not throw
+			throw new RuntimeException(e);
+		}
+	}
+
+	@Provides
+	KeyPair generateKeyPair() {
+		try {
+			KeyPairGenerator gen = KeyPairGenerator.getInstance(KEY_PAIR_ALGO);
+			return gen.generateKeyPair();
+		} catch(NoSuchAlgorithmException e) {
+			// FIXME: Providers should not throw
 			throw new RuntimeException(e);
 		}
 	}
diff --git a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java
index 0d8ae615248dbe4bfb6d2827cec21296293f0dec..15cee668766e12d2e258649a4fe2925d2d807707 100644
--- a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java
+++ b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java
@@ -16,19 +16,19 @@ import net.sf.briar.api.Rating;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.NoSuchContactException;
 import net.sf.briar.api.protocol.Ack;
-import net.sf.briar.api.protocol.AckWriter;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.Batch;
 import net.sf.briar.api.protocol.BatchId;
-import net.sf.briar.api.protocol.BatchWriter;
 import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupId;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageId;
-import net.sf.briar.api.protocol.SubscriptionWriter;
 import net.sf.briar.api.protocol.Subscriptions;
-import net.sf.briar.api.protocol.TransportWriter;
 import net.sf.briar.api.protocol.Transports;
+import net.sf.briar.api.protocol.writers.AckWriter;
+import net.sf.briar.api.protocol.writers.BatchWriter;
+import net.sf.briar.api.protocol.writers.SubscriptionWriter;
+import net.sf.briar.api.protocol.writers.TransportWriter;
 
 import com.google.inject.Inject;
 
diff --git a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java
index 1de25c0bf435f7c848ab63dbaef54bf5930b429a..1377739517cc7e6cb664b3303241ae601b103bef 100644
--- a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java
+++ b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java
@@ -15,19 +15,19 @@ import net.sf.briar.api.Rating;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.NoSuchContactException;
 import net.sf.briar.api.protocol.Ack;
-import net.sf.briar.api.protocol.AckWriter;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.Batch;
 import net.sf.briar.api.protocol.BatchId;
-import net.sf.briar.api.protocol.BatchWriter;
 import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupId;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageId;
-import net.sf.briar.api.protocol.SubscriptionWriter;
 import net.sf.briar.api.protocol.Subscriptions;
-import net.sf.briar.api.protocol.TransportWriter;
 import net.sf.briar.api.protocol.Transports;
+import net.sf.briar.api.protocol.writers.AckWriter;
+import net.sf.briar.api.protocol.writers.BatchWriter;
+import net.sf.briar.api.protocol.writers.SubscriptionWriter;
+import net.sf.briar.api.protocol.writers.TransportWriter;
 
 import com.google.inject.Inject;
 
diff --git a/components/net/sf/briar/protocol/AckWriterImpl.java b/components/net/sf/briar/protocol/writers/AckWriterImpl.java
similarity index 92%
rename from components/net/sf/briar/protocol/AckWriterImpl.java
rename to components/net/sf/briar/protocol/writers/AckWriterImpl.java
index 1d5282a5051c20cd4f9c583257f134198bbeddd8..ebb6f9fe86d4945fabf1b119a83d2de2888c8f31 100644
--- a/components/net/sf/briar/protocol/AckWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/AckWriterImpl.java
@@ -1,12 +1,12 @@
-package net.sf.briar.protocol;
+package net.sf.briar.protocol.writers;
 
 import java.io.IOException;
 import java.io.OutputStream;
 
 import net.sf.briar.api.protocol.Ack;
-import net.sf.briar.api.protocol.AckWriter;
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.writers.AckWriter;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
 
diff --git a/components/net/sf/briar/protocol/BatchWriterImpl.java b/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
similarity index 94%
rename from components/net/sf/briar/protocol/BatchWriterImpl.java
rename to components/net/sf/briar/protocol/writers/BatchWriterImpl.java
index e19da4539671c2c721ee7344e17cb608e6a8ae64..99a06fcf754a9f529463151aacc22019d888a292 100644
--- a/components/net/sf/briar/protocol/BatchWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
@@ -1,4 +1,4 @@
-package net.sf.briar.protocol;
+package net.sf.briar.protocol.writers;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -7,8 +7,8 @@ import java.security.MessageDigest;
 
 import net.sf.briar.api.protocol.Batch;
 import net.sf.briar.api.protocol.BatchId;
-import net.sf.briar.api.protocol.BatchWriter;
 import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.writers.BatchWriter;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
 
diff --git a/components/net/sf/briar/protocol/writers/PacketWriterFactoryImpl.java b/components/net/sf/briar/protocol/writers/PacketWriterFactoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b995004861f57c23336bd89a63916a1dd71c42d
--- /dev/null
+++ b/components/net/sf/briar/protocol/writers/PacketWriterFactoryImpl.java
@@ -0,0 +1,44 @@
+package net.sf.briar.protocol.writers;
+
+import java.io.OutputStream;
+import java.security.MessageDigest;
+
+import net.sf.briar.api.protocol.writers.AckWriter;
+import net.sf.briar.api.protocol.writers.BatchWriter;
+import net.sf.briar.api.protocol.writers.PacketWriterFactory;
+import net.sf.briar.api.protocol.writers.SubscriptionWriter;
+import net.sf.briar.api.protocol.writers.TransportWriter;
+import net.sf.briar.api.serial.WriterFactory;
+
+import com.google.inject.Inject;
+
+class PacketWriterFactoryImpl implements PacketWriterFactory {
+
+	private final MessageDigest messageDigest;
+	private final WriterFactory writerFactory;
+
+	@Inject
+	PacketWriterFactoryImpl(MessageDigest messageDigest,
+			WriterFactory writerFactory) {
+		this.messageDigest = messageDigest;
+		this.writerFactory = writerFactory;
+	}
+
+	public AckWriter createAckWriter(OutputStream out) {
+		return new AckWriterImpl(out, writerFactory);
+	}
+
+	public BatchWriter createBatchWriter(OutputStream out) {
+		return new BatchWriterImpl(out, writerFactory, messageDigest);
+	}
+
+	public SubscriptionWriter createSubscriptionWriter(OutputStream out) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public TransportWriter createTransportWriter(OutputStream out) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+}
diff --git a/components/net/sf/briar/protocol/writers/WritersModule.java b/components/net/sf/briar/protocol/writers/WritersModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..abb7540ed56dd0098a7762e507ee252477ca483d
--- /dev/null
+++ b/components/net/sf/briar/protocol/writers/WritersModule.java
@@ -0,0 +1,14 @@
+package net.sf.briar.protocol.writers;
+
+
+import net.sf.briar.api.protocol.writers.PacketWriterFactory;
+
+import com.google.inject.AbstractModule;
+
+public class WritersModule extends AbstractModule {
+
+	@Override
+	protected void configure() {
+		bind(PacketWriterFactory.class).to(PacketWriterFactoryImpl.class);
+	}
+}
diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java
index 65ba87ad8d5aa3c685af4bb47a6d459f01710f08..c46107aad5db1493ec355a674593823f416d4319 100644
--- a/test/net/sf/briar/db/DatabaseComponentTest.java
+++ b/test/net/sf/briar/db/DatabaseComponentTest.java
@@ -14,13 +14,13 @@ import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.NoSuchContactException;
 import net.sf.briar.api.db.Status;
 import net.sf.briar.api.protocol.Ack;
-import net.sf.briar.api.protocol.AckWriter;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupId;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageId;
+import net.sf.briar.api.protocol.writers.AckWriter;
 
 import org.jmock.Expectations;
 import org.jmock.Mockery;
diff --git a/test/net/sf/briar/protocol/BatchReaderTest.java b/test/net/sf/briar/protocol/BatchReaderTest.java
index c7f18b0e571a2c91bbee890f4c2ba8698ee7b320..f53641057f458ed6798044accafe0abb5abda6b2 100644
--- a/test/net/sf/briar/protocol/BatchReaderTest.java
+++ b/test/net/sf/briar/protocol/BatchReaderTest.java
@@ -38,10 +38,11 @@ public class BatchReaderTest extends TestCase {
 
 	public BatchReaderTest() throws Exception {
 		super();
-		Injector i = Guice.createInjector(new SerialModule());
+		Injector i = Guice.createInjector(new SerialModule(),
+				new CryptoModule());
 		readerFactory = i.getInstance(ReaderFactory.class);
 		writerFactory = i.getInstance(WriterFactory.class);
-		messageDigest = MessageDigest.getInstance(CryptoModule.DIGEST_ALGO);
+		messageDigest = i.getInstance(MessageDigest.class);
 		context = new Mockery();
 		message = context.mock(Message.class);
 	}
diff --git a/test/net/sf/briar/protocol/ConsumersTest.java b/test/net/sf/briar/protocol/ConsumersTest.java
index 01af2ba690579e9b98729f19bf1c5126d6f6e33e..c2d16bf84f06f0dc6864b3097310c0660d3f5358 100644
--- a/test/net/sf/briar/protocol/ConsumersTest.java
+++ b/test/net/sf/briar/protocol/ConsumersTest.java
@@ -1,7 +1,6 @@
 package net.sf.briar.protocol;
 
 import java.security.KeyPair;
-import java.security.KeyPairGenerator;
 import java.security.MessageDigest;
 import java.security.Signature;
 import java.util.Arrays;
@@ -11,48 +10,59 @@ import junit.framework.TestCase;
 import net.sf.briar.api.serial.FormatException;
 import net.sf.briar.crypto.CryptoModule;
 
+import org.junit.Before;
 import org.junit.Test;
 
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
 public class ConsumersTest extends TestCase {
 
+	private Signature signature = null;
+	private KeyPair keyPair = null;
+	private MessageDigest messageDigest = null;
+
+	@Before
+	public void setUp() {
+		Injector i = Guice.createInjector(new CryptoModule());
+		signature = i.getInstance(Signature.class);
+		keyPair = i.getInstance(KeyPair.class);
+		messageDigest = i.getInstance(MessageDigest.class);
+	}
+		
 	@Test
 	public void testSigningConsumer() throws Exception {
-		Signature s = Signature.getInstance(CryptoModule.SIGNATURE_ALGO);
-		KeyPairGenerator gen =
-			KeyPairGenerator.getInstance(CryptoModule.KEY_PAIR_ALGO);
-		KeyPair k = gen.genKeyPair();
 		byte[] data = new byte[1234];
 		// Generate some random data and sign it
 		new Random().nextBytes(data);
-		s.initSign(k.getPrivate());
-		s.update(data);
-		byte[] sig = s.sign();
+		signature.initSign(keyPair.getPrivate());
+		signature.update(data);
+		byte[] sig = signature.sign();
 		// Check that feeding a SigningConsumer generates the same signature
-		s.initSign(k.getPrivate());
-		SigningConsumer sc = new SigningConsumer(s);
+		signature.initSign(keyPair.getPrivate());
+		SigningConsumer sc = new SigningConsumer(signature);
 		sc.write(data[0]);
 		sc.write(data, 1, data.length - 2);
 		sc.write(data[data.length - 1]);
-		byte[] sig1 = s.sign();
+		byte[] sig1 = signature.sign();
 		assertTrue(Arrays.equals(sig, sig1));
 	}
 
 	@Test
 	public void testDigestingConsumer() throws Exception {
-		MessageDigest m = MessageDigest.getInstance(CryptoModule.DIGEST_ALGO);
 		byte[] data = new byte[1234];
 		// Generate some random data and digest it
 		new Random().nextBytes(data);
-		m.reset();
-		m.update(data);
-		byte[] dig = m.digest();
+		messageDigest.reset();
+		messageDigest.update(data);
+		byte[] dig = messageDigest.digest();
 		// Check that feeding a DigestingConsumer generates the same digest
-		m.reset();
-		DigestingConsumer dc = new DigestingConsumer(m);
+		messageDigest.reset();
+		DigestingConsumer dc = new DigestingConsumer(messageDigest);
 		dc.write(data[0]);
 		dc.write(data, 1, data.length - 2);
 		dc.write(data[data.length - 1]);
-		byte[] dig1 = m.digest();
+		byte[] dig1 = messageDigest.digest();
 		assertTrue(Arrays.equals(dig, dig1));
 	}
 
diff --git a/test/net/sf/briar/protocol/FileReadWriteTest.java b/test/net/sf/briar/protocol/FileReadWriteTest.java
index 3d6b9c4b88d6f7b7f96c31f0a5972140a9d8cd66..f355d01e37e1821c6a4d59b677b22df72ad8a48c 100644
--- a/test/net/sf/briar/protocol/FileReadWriteTest.java
+++ b/test/net/sf/briar/protocol/FileReadWriteTest.java
@@ -4,7 +4,6 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.security.KeyPair;
-import java.security.KeyPairGenerator;
 import java.security.MessageDigest;
 import java.security.Signature;
 import java.util.Arrays;
@@ -15,20 +14,22 @@ import junit.framework.TestCase;
 import net.sf.briar.TestUtils;
 import net.sf.briar.api.crypto.KeyParser;
 import net.sf.briar.api.protocol.Ack;
-import net.sf.briar.api.protocol.AckWriter;
 import net.sf.briar.api.protocol.Batch;
 import net.sf.briar.api.protocol.BatchId;
-import net.sf.briar.api.protocol.BatchWriter;
 import net.sf.briar.api.protocol.GroupId;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageEncoder;
 import net.sf.briar.api.protocol.MessageId;
 import net.sf.briar.api.protocol.Tags;
 import net.sf.briar.api.protocol.UniqueId;
+import net.sf.briar.api.protocol.writers.AckWriter;
+import net.sf.briar.api.protocol.writers.BatchWriter;
+import net.sf.briar.api.protocol.writers.PacketWriterFactory;
 import net.sf.briar.api.serial.Reader;
 import net.sf.briar.api.serial.ReaderFactory;
 import net.sf.briar.api.serial.WriterFactory;
 import net.sf.briar.crypto.CryptoModule;
+import net.sf.briar.protocol.writers.WritersModule;
 import net.sf.briar.serial.SerialModule;
 
 import org.junit.After;
@@ -50,6 +51,7 @@ public class FileReadWriteTest extends TestCase {
 
 	private final ReaderFactory readerFactory;
 	private final WriterFactory writerFactory;
+	private final PacketWriterFactory packetWriterFactory;
 	private final Signature signature;
 	private final MessageDigest messageDigest, batchDigest;
 	private final KeyParser keyParser;
@@ -58,21 +60,20 @@ public class FileReadWriteTest extends TestCase {
 	public FileReadWriteTest() throws Exception {
 		super();
 		Injector i = Guice.createInjector(new SerialModule(),
-				new CryptoModule());
+				new CryptoModule(), new WritersModule());
 		readerFactory = i.getInstance(ReaderFactory.class);
 		writerFactory = i.getInstance(WriterFactory.class);
+		packetWriterFactory = i.getInstance(PacketWriterFactory.class);
 		keyParser = i.getInstance(KeyParser.class);
-		signature = Signature.getInstance(CryptoModule.SIGNATURE_ALGO);
-		messageDigest = MessageDigest.getInstance(CryptoModule.DIGEST_ALGO);
-		batchDigest = MessageDigest.getInstance(CryptoModule.DIGEST_ALGO);
+		signature = i.getInstance(Signature.class);
+		messageDigest = i.getInstance(MessageDigest.class);
+		batchDigest = i.getInstance(MessageDigest.class);
 		assertEquals(messageDigest.getDigestLength(), UniqueId.LENGTH);
 		assertEquals(batchDigest.getDigestLength(), UniqueId.LENGTH);
 		// Create and encode a test message
 		MessageEncoder messageEncoder = new MessageEncoderImpl(signature,
 				messageDigest, writerFactory);
-		KeyPairGenerator gen =
-			KeyPairGenerator.getInstance(CryptoModule.KEY_PAIR_ALGO);
-		KeyPair keyPair = gen.generateKeyPair();
+		KeyPair keyPair = i.getInstance(KeyPair.class);
 		message = messageEncoder.encodeMessage(MessageId.NONE, sub, nick,
 				keyPair, messageBody.getBytes("UTF-8"));
 	}
@@ -86,11 +87,11 @@ public class FileReadWriteTest extends TestCase {
 	public void testWriteFile() throws Exception {
 		FileOutputStream out = new FileOutputStream(file);
 
-		AckWriter a = new AckWriterImpl(out, writerFactory);
+		AckWriter a = packetWriterFactory.createAckWriter(out);
 		a.addBatchId(ack);
 		a.finish();
 
-		BatchWriter b = new BatchWriterImpl(out, writerFactory, batchDigest);
+		BatchWriter b = packetWriterFactory.createBatchWriter(out);
 		b.addMessage(message.getBytes());
 		b.finish();
 
diff --git a/test/net/sf/briar/protocol/SigningDigestingOutputStreamTest.java b/test/net/sf/briar/protocol/SigningDigestingOutputStreamTest.java
index f86f62114c83448327c81df92fe3ac5c2e11083b..a82f75e982b0067ae44b4cef445ac180081af5bf 100644
--- a/test/net/sf/briar/protocol/SigningDigestingOutputStreamTest.java
+++ b/test/net/sf/briar/protocol/SigningDigestingOutputStreamTest.java
@@ -3,7 +3,6 @@ package net.sf.briar.protocol;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.security.KeyPair;
-import java.security.KeyPairGenerator;
 import java.security.MessageDigest;
 import java.security.Signature;
 import java.util.Arrays;
@@ -15,19 +14,21 @@ import net.sf.briar.crypto.CryptoModule;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
 public class SigningDigestingOutputStreamTest extends TestCase {
 
+	private Signature signature = null;
 	private KeyPair keyPair = null;
-	private Signature sig = null;
-	private MessageDigest dig = null;
+	private MessageDigest messageDigest = null;
 
 	@Before
 	public void setUp() throws Exception {
-		KeyPairGenerator gen =
-			KeyPairGenerator.getInstance(CryptoModule.KEY_PAIR_ALGO);
-		keyPair = gen.generateKeyPair();
-		sig = Signature.getInstance(CryptoModule.SIGNATURE_ALGO);
-		dig = MessageDigest.getInstance(CryptoModule.DIGEST_ALGO);
+		Injector i = Guice.createInjector(new CryptoModule());
+		signature = i.getInstance(Signature.class);
+		keyPair = i.getInstance(KeyPair.class);
+		messageDigest = i.getInstance(MessageDigest.class);
 	}
 
 	@Test
@@ -36,9 +37,9 @@ public class SigningDigestingOutputStreamTest extends TestCase {
 		new Random().nextBytes(input);
 		ByteArrayOutputStream out = new ByteArrayOutputStream(input.length);
 		SigningDigestingOutputStream s =
-			new SigningDigestingOutputStream(out, sig, dig);
-		sig.initSign(keyPair.getPrivate());
-		dig.reset();
+			new SigningDigestingOutputStream(out, signature, messageDigest);
+		signature.initSign(keyPair.getPrivate());
+		messageDigest.reset();
 		// Sign the first 256 bytes, digest all but the last 256 bytes
 		s.setDigesting(true);
 		s.setSigning(true);
@@ -49,20 +50,20 @@ public class SigningDigestingOutputStreamTest extends TestCase {
 		s.write(input, 768, 256);
 		s.close();
 		// Get the signature and the digest
-		byte[] signature = sig.sign();
-		byte[] digest = dig.digest();
+		byte[] sig = signature.sign();
+		byte[] digest = messageDigest.digest();
 		// Check that the output matches the input
 		assertTrue(Arrays.equals(input, out.toByteArray()));
 		// Check that the signature matches a signature over the first 256 bytes
-		sig.initSign(keyPair.getPrivate());
-		sig.update(input, 0, 256);
-		byte[] directSignature = sig.sign();
-		assertTrue(Arrays.equals(directSignature, signature));
+		signature.initSign(keyPair.getPrivate());
+		signature.update(input, 0, 256);
+		byte[] directSig = signature.sign();
+		assertTrue(Arrays.equals(directSig, sig));
 		// Check that the digest matches a digest over all but the last 256
 		// bytes
-		dig.reset();
-		dig.update(input, 0, 768);
-		byte[] directDigest = dig.digest();
+		messageDigest.reset();
+		messageDigest.update(input, 0, 768);
+		byte[] directDigest = messageDigest.digest();
 		assertTrue(Arrays.equals(directDigest, digest));
 	}
 
@@ -70,7 +71,7 @@ public class SigningDigestingOutputStreamTest extends TestCase {
 	public void testSignatureExceptionThrowsIOException() throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		SigningDigestingOutputStream s =
-			new SigningDigestingOutputStream(out, sig, dig);
+			new SigningDigestingOutputStream(out, signature, messageDigest);
 		s.setSigning(true); // Signature hasn't been initialised yet
 		try {
 			s.write((byte) 0);