From cf49a28c95ae83247c94e012208c9992b5914d13 Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Tue, 15 Nov 2011 11:11:31 +0000
Subject: [PATCH] Replaced SHA-256 with SHAd-256 to prevent length extension
 attacks.

---
 .../sf/briar/api/crypto/CryptoComponent.java  |  1 -
 .../sf/briar/api/crypto/MessageDigest.java    | 29 +++++++++
 .../sf/briar/api/serial}/CopyingConsumer.java |  8 +--
 .../briar/api/serial}/CountingConsumer.java   |  9 ++-
 .../briar/api/serial}/DigestingConsumer.java  | 10 ++--
 .../sf/briar/api/serial}/SigningConsumer.java |  8 +--
 .../sf/briar/crypto/CryptoComponentImpl.java  |  5 +-
 .../net/sf/briar/crypto/DoubleDigest.java     | 59 +++++++++++++++++++
 .../net/sf/briar/protocol/AckReader.java      |  1 +
 .../sf/briar/protocol/AuthorFactoryImpl.java  |  2 +-
 .../net/sf/briar/protocol/AuthorReader.java   |  3 +-
 .../net/sf/briar/protocol/BatchReader.java    |  4 +-
 .../sf/briar/protocol/GroupFactoryImpl.java   |  2 +-
 .../net/sf/briar/protocol/GroupReader.java    |  3 +-
 .../sf/briar/protocol/MessageEncoderImpl.java |  5 +-
 .../net/sf/briar/protocol/MessageReader.java  |  4 +-
 .../net/sf/briar/protocol/OfferReader.java    |  1 +
 .../net/sf/briar/protocol/RequestReader.java  |  1 +
 .../protocol/SubscriptionUpdateReader.java    |  1 +
 .../briar/protocol/TransportUpdateReader.java |  1 +
 .../protocol/writers/BatchWriterImpl.java     | 12 ++--
 .../writers/ProtocolWriterFactoryImpl.java    |  2 +-
 .../sf/briar/protocol/BatchReaderTest.java    |  2 +-
 test/net/sf/briar/protocol/ConsumersTest.java |  5 +-
 24 files changed, 140 insertions(+), 38 deletions(-)
 create mode 100644 api/net/sf/briar/api/crypto/MessageDigest.java
 rename {components/net/sf/briar/protocol => api/net/sf/briar/api/serial}/CopyingConsumer.java (74%)
 rename {components/net/sf/briar/protocol => api/net/sf/briar/api/serial}/CountingConsumer.java (76%)
 rename {components/net/sf/briar/protocol => api/net/sf/briar/api/serial}/DigestingConsumer.java (59%)
 rename {components/net/sf/briar/protocol => api/net/sf/briar/api/serial}/SigningConsumer.java (79%)
 create mode 100644 components/net/sf/briar/crypto/DoubleDigest.java

diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java
index 6b98dfad89..75c6b726f9 100644
--- a/api/net/sf/briar/api/crypto/CryptoComponent.java
+++ b/api/net/sf/briar/api/crypto/CryptoComponent.java
@@ -1,7 +1,6 @@
 package net.sf.briar.api.crypto;
 
 import java.security.KeyPair;
-import java.security.MessageDigest;
 import java.security.SecureRandom;
 import java.security.Signature;
 
diff --git a/api/net/sf/briar/api/crypto/MessageDigest.java b/api/net/sf/briar/api/crypto/MessageDigest.java
new file mode 100644
index 0000000000..7162e107e2
--- /dev/null
+++ b/api/net/sf/briar/api/crypto/MessageDigest.java
@@ -0,0 +1,29 @@
+package net.sf.briar.api.crypto;
+
+/** An interface that allows a java.security.MessageDigest to be wrapped. */
+public interface MessageDigest {
+
+    /** @see {@link java.security.MessageDigest#digest()} */
+	byte[] digest();
+
+	/** @see {@link java.security.MessageDigest#digest(byte[])} */
+	byte[] digest(byte[] input);
+
+	/** @see {@link java.security.MessageDigest#digest(byte[], int, int)} */
+	int digest(byte[] buf, int offset, int len);
+
+	/** @see {@link java.security.MessageDigest#getDigestLength()} */
+	int getDigestLength();
+
+	/** @see {@link java.security.MessageDigest#reset()} */
+	void reset();
+
+	/** @see {@link java.security.MessageDigest#update(byte)} */
+	void update(byte input);
+
+	/** @see {@link java.security.MessageDigest#update(byte[])} */
+	void update(byte[] input);
+
+	/** @see {@link java.security.MessageDigest#update(byte[], int, int)} */
+	void update(byte[] input, int offset, int len);
+}
diff --git a/components/net/sf/briar/protocol/CopyingConsumer.java b/api/net/sf/briar/api/serial/CopyingConsumer.java
similarity index 74%
rename from components/net/sf/briar/protocol/CopyingConsumer.java
rename to api/net/sf/briar/api/serial/CopyingConsumer.java
index 97129ab327..3274c6a994 100644
--- a/components/net/sf/briar/protocol/CopyingConsumer.java
+++ b/api/net/sf/briar/api/serial/CopyingConsumer.java
@@ -1,16 +1,14 @@
-package net.sf.briar.protocol;
+package net.sf.briar.api.serial;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
-import net.sf.briar.api.serial.Consumer;
-
 /** A consumer that makes a copy of the bytes consumed. */
-class CopyingConsumer implements Consumer {
+public class CopyingConsumer implements Consumer {
 
 	private final ByteArrayOutputStream out = new ByteArrayOutputStream();
 
-	byte[] getCopy() {
+	public byte[] getCopy() {
 		return out.toByteArray();
 	}
 
diff --git a/components/net/sf/briar/protocol/CountingConsumer.java b/api/net/sf/briar/api/serial/CountingConsumer.java
similarity index 76%
rename from components/net/sf/briar/protocol/CountingConsumer.java
rename to api/net/sf/briar/api/serial/CountingConsumer.java
index 7722ce5437..901bbd5642 100644
--- a/components/net/sf/briar/protocol/CountingConsumer.java
+++ b/api/net/sf/briar/api/serial/CountingConsumer.java
@@ -1,24 +1,23 @@
-package net.sf.briar.protocol;
+package net.sf.briar.api.serial;
 
 import java.io.IOException;
 
 import net.sf.briar.api.FormatException;
-import net.sf.briar.api.serial.Consumer;
 
 /**
  * A consumer that counts the number of bytes consumed and throws a
  * FormatException if the count exceeds a given limit.
  */
-class CountingConsumer implements Consumer {
+public class CountingConsumer implements Consumer {
 
 	private final long limit;
 	private long count = 0L;
 
-	CountingConsumer(long limit) {
+	public CountingConsumer(long limit) {
 		this.limit = limit;
 	}
 
-	long getCount() {
+	public long getCount() {
 		return count;
 	}
 
diff --git a/components/net/sf/briar/protocol/DigestingConsumer.java b/api/net/sf/briar/api/serial/DigestingConsumer.java
similarity index 59%
rename from components/net/sf/briar/protocol/DigestingConsumer.java
rename to api/net/sf/briar/api/serial/DigestingConsumer.java
index 29a0c260ef..843cf7185e 100644
--- a/components/net/sf/briar/protocol/DigestingConsumer.java
+++ b/api/net/sf/briar/api/serial/DigestingConsumer.java
@@ -1,15 +1,13 @@
-package net.sf.briar.protocol;
+package net.sf.briar.api.serial;
 
-import java.security.MessageDigest;
-
-import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.crypto.MessageDigest;
 
 /** A consumer that passes its input through a message digest. */
-class DigestingConsumer implements Consumer {
+public class DigestingConsumer implements Consumer {
 
 	private final MessageDigest messageDigest;
 
-	DigestingConsumer(MessageDigest messageDigest) {
+	public DigestingConsumer(MessageDigest messageDigest) {
 		this.messageDigest = messageDigest;
 	}
 
diff --git a/components/net/sf/briar/protocol/SigningConsumer.java b/api/net/sf/briar/api/serial/SigningConsumer.java
similarity index 79%
rename from components/net/sf/briar/protocol/SigningConsumer.java
rename to api/net/sf/briar/api/serial/SigningConsumer.java
index c3274ba7ff..4b0349ed81 100644
--- a/components/net/sf/briar/protocol/SigningConsumer.java
+++ b/api/net/sf/briar/api/serial/SigningConsumer.java
@@ -1,17 +1,15 @@
-package net.sf.briar.protocol;
+package net.sf.briar.api.serial;
 
 import java.io.IOException;
 import java.security.Signature;
 import java.security.SignatureException;
 
-import net.sf.briar.api.serial.Consumer;
-
 /** A consumer that passes its input through a signature. */
-class SigningConsumer implements Consumer {
+public class SigningConsumer implements Consumer {
 
 	private final Signature signature;
 
-	SigningConsumer(Signature signature) {
+	public SigningConsumer(Signature signature) {
 		this.signature = signature;
 	}
 
diff --git a/components/net/sf/briar/crypto/CryptoComponentImpl.java b/components/net/sf/briar/crypto/CryptoComponentImpl.java
index 352182701a..78c7badbf3 100644
--- a/components/net/sf/briar/crypto/CryptoComponentImpl.java
+++ b/components/net/sf/briar/crypto/CryptoComponentImpl.java
@@ -5,7 +5,6 @@ import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
-import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.SecureRandom;
@@ -25,6 +24,7 @@ import javax.crypto.spec.SecretKeySpec;
 
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyParser;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.crypto.SecretStorageKey;
 
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
@@ -198,7 +198,8 @@ class CryptoComponentImpl implements CryptoComponent {
 
 	public MessageDigest getMessageDigest() {
 		try {
-			return MessageDigest.getInstance(DIGEST_ALGO, PROVIDER);
+			return new DoubleDigest(java.security.MessageDigest.getInstance(
+					DIGEST_ALGO, PROVIDER));
 		} catch(NoSuchAlgorithmException e) {
 			throw new RuntimeException(e);
 		} catch(NoSuchProviderException e) {
diff --git a/components/net/sf/briar/crypto/DoubleDigest.java b/components/net/sf/briar/crypto/DoubleDigest.java
new file mode 100644
index 0000000000..a3039845a8
--- /dev/null
+++ b/components/net/sf/briar/crypto/DoubleDigest.java
@@ -0,0 +1,59 @@
+package net.sf.briar.crypto;
+
+import net.sf.briar.api.crypto.MessageDigest;
+
+/**
+ * A message digest that prevents length extension attacks - see Ferguson and
+ * Schneier, <i>Practical Cryptography</i>, chapter 6.
+ * <p>
+ * "Let h be an interative hash function. The hash function h<sub>d</sub> is
+ * defined by h<sub>d</sub> := h(h(m)), and has a claimed security level of
+ * min(k, n/2) where k is the security level of h and n is the size of the hash
+ * result."
+ */
+class DoubleDigest implements MessageDigest {
+
+	private final java.security.MessageDigest delegate;
+
+	DoubleDigest(java.security.MessageDigest delegate) {
+		this.delegate = delegate;
+	}
+
+	public byte[] digest() {
+		byte[] digest = delegate.digest(); // h(m)
+		delegate.update(digest);
+		return delegate.digest(); // h(h(m))
+	}
+
+	public byte[] digest(byte[] input) {
+		delegate.update(input);
+		return digest();
+	}
+
+	public int digest(byte[] buf, int offset, int len) {
+		byte[] digest = digest();
+		len = Math.min(len, digest.length);
+		System.arraycopy(digest, 0, buf, offset, len);
+		return len;
+	}
+
+	public int getDigestLength() {
+		return delegate.getDigestLength();
+	}
+
+	public void reset() {
+		delegate.reset();
+	}
+
+	public void update(byte input) {
+		delegate.update(input);
+	}
+
+	public void update(byte[] input) {
+		delegate.update(input);
+	}
+
+	public void update(byte[] input, int offset, int len) {
+		delegate.update(input, offset, len);
+	}
+}
diff --git a/components/net/sf/briar/protocol/AckReader.java b/components/net/sf/briar/protocol/AckReader.java
index af02456d42..870abefdfd 100644
--- a/components/net/sf/briar/protocol/AckReader.java
+++ b/components/net/sf/briar/protocol/AckReader.java
@@ -8,6 +8,7 @@ import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/AuthorFactoryImpl.java b/components/net/sf/briar/protocol/AuthorFactoryImpl.java
index 7356a0414b..d1fb362d53 100644
--- a/components/net/sf/briar/protocol/AuthorFactoryImpl.java
+++ b/components/net/sf/briar/protocol/AuthorFactoryImpl.java
@@ -2,9 +2,9 @@ package net.sf.briar.protocol;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.security.MessageDigest;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Author;
 import net.sf.briar.api.protocol.AuthorFactory;
 import net.sf.briar.api.protocol.AuthorId;
diff --git a/components/net/sf/briar/protocol/AuthorReader.java b/components/net/sf/briar/protocol/AuthorReader.java
index 4451197bbe..eb18812bbe 100644
--- a/components/net/sf/briar/protocol/AuthorReader.java
+++ b/components/net/sf/briar/protocol/AuthorReader.java
@@ -1,14 +1,15 @@
 package net.sf.briar.protocol;
 
 import java.io.IOException;
-import java.security.MessageDigest;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Author;
 import net.sf.briar.api.protocol.AuthorFactory;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
+import net.sf.briar.api.serial.DigestingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/BatchReader.java b/components/net/sf/briar/protocol/BatchReader.java
index e77c711f58..04151a76a6 100644
--- a/components/net/sf/briar/protocol/BatchReader.java
+++ b/components/net/sf/briar/protocol/BatchReader.java
@@ -1,16 +1,18 @@
 package net.sf.briar.protocol;
 
 import java.io.IOException;
-import java.security.MessageDigest;
 import java.util.List;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Batch;
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
+import net.sf.briar.api.serial.DigestingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/GroupFactoryImpl.java b/components/net/sf/briar/protocol/GroupFactoryImpl.java
index 6df7b18553..3aebebff01 100644
--- a/components/net/sf/briar/protocol/GroupFactoryImpl.java
+++ b/components/net/sf/briar/protocol/GroupFactoryImpl.java
@@ -2,9 +2,9 @@ package net.sf.briar.protocol;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.security.MessageDigest;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupFactory;
 import net.sf.briar.api.protocol.GroupId;
diff --git a/components/net/sf/briar/protocol/GroupReader.java b/components/net/sf/briar/protocol/GroupReader.java
index 9e5dc2d14b..8eba20c68b 100644
--- a/components/net/sf/briar/protocol/GroupReader.java
+++ b/components/net/sf/briar/protocol/GroupReader.java
@@ -1,14 +1,15 @@
 package net.sf.briar.protocol;
 
 import java.io.IOException;
-import java.security.MessageDigest;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupFactory;
 import net.sf.briar.api.protocol.GroupId;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
+import net.sf.briar.api.serial.DigestingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/MessageEncoderImpl.java b/components/net/sf/briar/protocol/MessageEncoderImpl.java
index 5d5d68dd4f..143c337831 100644
--- a/components/net/sf/briar/protocol/MessageEncoderImpl.java
+++ b/components/net/sf/briar/protocol/MessageEncoderImpl.java
@@ -3,12 +3,12 @@ package net.sf.briar.protocol;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
 import java.security.PrivateKey;
 import java.security.SecureRandom;
 import java.security.Signature;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Author;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.Group;
@@ -19,6 +19,9 @@ import net.sf.briar.api.protocol.MessageId;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
+import net.sf.briar.api.serial.DigestingConsumer;
+import net.sf.briar.api.serial.SigningConsumer;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
 
diff --git a/components/net/sf/briar/protocol/MessageReader.java b/components/net/sf/briar/protocol/MessageReader.java
index 54502aff02..3bba3525ec 100644
--- a/components/net/sf/briar/protocol/MessageReader.java
+++ b/components/net/sf/briar/protocol/MessageReader.java
@@ -2,13 +2,13 @@ package net.sf.briar.protocol;
 
 import java.io.IOException;
 import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
 import java.security.PublicKey;
 import java.security.Signature;
 
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyParser;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Author;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.Group;
@@ -17,6 +17,8 @@ import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageId;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
+import net.sf.briar.api.serial.CopyingConsumer;
+import net.sf.briar.api.serial.CountingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/OfferReader.java b/components/net/sf/briar/protocol/OfferReader.java
index 7d16c0b2f3..f40389f71a 100644
--- a/components/net/sf/briar/protocol/OfferReader.java
+++ b/components/net/sf/briar/protocol/OfferReader.java
@@ -8,6 +8,7 @@ import net.sf.briar.api.protocol.Offer;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/RequestReader.java b/components/net/sf/briar/protocol/RequestReader.java
index b2edd24cb9..f64fecf64b 100644
--- a/components/net/sf/briar/protocol/RequestReader.java
+++ b/components/net/sf/briar/protocol/RequestReader.java
@@ -7,6 +7,7 @@ import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Request;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/SubscriptionUpdateReader.java b/components/net/sf/briar/protocol/SubscriptionUpdateReader.java
index b5f37f01f8..392a5c99cb 100644
--- a/components/net/sf/briar/protocol/SubscriptionUpdateReader.java
+++ b/components/net/sf/briar/protocol/SubscriptionUpdateReader.java
@@ -9,6 +9,7 @@ import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.SubscriptionUpdate;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/TransportUpdateReader.java b/components/net/sf/briar/protocol/TransportUpdateReader.java
index 5eae4d6138..a3bed1a60d 100644
--- a/components/net/sf/briar/protocol/TransportUpdateReader.java
+++ b/components/net/sf/briar/protocol/TransportUpdateReader.java
@@ -15,6 +15,7 @@ import net.sf.briar.api.protocol.TransportUpdate;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.UniqueId;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/writers/BatchWriterImpl.java b/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
index cbae50f1d5..3c8d5fe93a 100644
--- a/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
@@ -2,23 +2,24 @@ package net.sf.briar.protocol.writers;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.security.DigestOutputStream;
-import java.security.MessageDigest;
 
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.writers.BatchWriter;
+import net.sf.briar.api.serial.DigestingConsumer;
 import net.sf.briar.api.serial.SerialComponent;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
 
 class BatchWriterImpl implements BatchWriter {
 
-	private final DigestOutputStream out;
+	private final OutputStream out;
 	private final int headerLength, footerLength;
 	private final Writer w;
 	private final MessageDigest messageDigest;
+	private final DigestingConsumer digestingConsumer;
 
 	private boolean started = false;
 	private int capacity = ProtocolConstants.MAX_PACKET_LENGTH;
@@ -26,12 +27,13 @@ class BatchWriterImpl implements BatchWriter {
 
 	BatchWriterImpl(OutputStream out, SerialComponent serial,
 			WriterFactory writerFactory, MessageDigest messageDigest) {
-		this.out = new DigestOutputStream(out, messageDigest);
+		this.out = out;
 		headerLength = serial.getSerialisedUserDefinedIdLength(Types.BATCH)
 		+ serial.getSerialisedListStartLength();
 		footerLength = serial.getSerialisedListEndLength();
 		w = writerFactory.createWriter(this.out);
 		this.messageDigest = messageDigest;
+		digestingConsumer = new DigestingConsumer(messageDigest);
 	}
 
 	public int getCapacity() {
@@ -58,6 +60,7 @@ class BatchWriterImpl implements BatchWriter {
 	public BatchId finish() throws IOException {
 		if(!started) start();
 		w.writeListEnd();
+		w.removeConsumer(digestingConsumer);
 		out.flush();
 		remaining = capacity = ProtocolConstants.MAX_PACKET_LENGTH;
 		started = false;
@@ -66,6 +69,7 @@ class BatchWriterImpl implements BatchWriter {
 
 	private void start() throws IOException {
 		messageDigest.reset();
+		w.addConsumer(digestingConsumer);
 		w.writeUserDefinedId(Types.BATCH);
 		w.writeListStart();
 		remaining -= headerLength;
diff --git a/components/net/sf/briar/protocol/writers/ProtocolWriterFactoryImpl.java b/components/net/sf/briar/protocol/writers/ProtocolWriterFactoryImpl.java
index ed6b558448..e977d2bd01 100644
--- a/components/net/sf/briar/protocol/writers/ProtocolWriterFactoryImpl.java
+++ b/components/net/sf/briar/protocol/writers/ProtocolWriterFactoryImpl.java
@@ -1,9 +1,9 @@
 package net.sf.briar.protocol.writers;
 
 import java.io.OutputStream;
-import java.security.MessageDigest;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.writers.AckWriter;
 import net.sf.briar.api.protocol.writers.BatchWriter;
 import net.sf.briar.api.protocol.writers.OfferWriter;
diff --git a/test/net/sf/briar/protocol/BatchReaderTest.java b/test/net/sf/briar/protocol/BatchReaderTest.java
index c6727e2749..ec683095e6 100644
--- a/test/net/sf/briar/protocol/BatchReaderTest.java
+++ b/test/net/sf/briar/protocol/BatchReaderTest.java
@@ -3,12 +3,12 @@ package net.sf.briar.protocol;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.security.MessageDigest;
 import java.util.Collections;
 
 import junit.framework.TestCase;
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Batch;
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.Message;
diff --git a/test/net/sf/briar/protocol/ConsumersTest.java b/test/net/sf/briar/protocol/ConsumersTest.java
index c2bc475fd3..9605c1c403 100644
--- a/test/net/sf/briar/protocol/ConsumersTest.java
+++ b/test/net/sf/briar/protocol/ConsumersTest.java
@@ -2,12 +2,15 @@ package net.sf.briar.protocol;
 
 import static org.junit.Assert.assertArrayEquals;
 
-import java.security.MessageDigest;
 import java.util.Random;
 
 import junit.framework.TestCase;
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
+import net.sf.briar.api.serial.CopyingConsumer;
+import net.sf.briar.api.serial.CountingConsumer;
+import net.sf.briar.api.serial.DigestingConsumer;
 import net.sf.briar.crypto.CryptoModule;
 
 import org.junit.Before;
-- 
GitLab