diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java index 6b98dfad8..75c6b726f 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 000000000..7162e107e --- /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 97129ab32..3274c6a99 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 7722ce543..901bbd564 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 29a0c260e..843cf7185 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 c3274ba7f..4b0349ed8 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 352182701..78c7badbf 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 000000000..a3039845a --- /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, Practical Cryptography, chapter 6. + *

+ * "Let h be an interative hash function. The hash function hd is + * defined by hd := 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 af02456d4..870abefdf 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 7356a0414..d1fb362d5 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 4451197bb..eb18812bb 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 e77c711f5..04151a76a 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 6df7b1855..3aebebff0 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 9e5dc2d14..8eba20c68 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 5d5d68dd4..143c33783 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 54502aff0..3bba3525e 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 7d16c0b2f..f40389f71 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 b2edd24cb..f64fecf64 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 b5f37f01f..392a5c99c 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 5eae4d613..a3bed1a60 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 cbae50f1d..3c8d5fe93 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 ed6b55844..e977d2bd0 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 c6727e274..ec683095e 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 c2bc475fd..9605c1c40 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;