diff --git a/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedTransportConnection.java b/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedTransportConnection.java index 3adad239866e3d75ad4c54838ec3284d12a2ba12..728f3e60b4302849db500d0776bae9bffe685132 100644 --- a/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedTransportConnection.java +++ b/api/net/sf/briar/api/plugins/duplex/DuplexSegmentedTransportConnection.java @@ -11,6 +11,9 @@ import net.sf.briar.api.plugins.FrameSource; public interface DuplexSegmentedTransportConnection extends FrameSource, FrameSink { + /** Returns the maximum length of a segment in bytes. */ + int getMaximumSegmentLength(); + /** * Returns true if the output stream should be flushed after each packet. */ diff --git a/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedTransportWriter.java b/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedTransportWriter.java index ff909847eba576272a27e41e09c9709ed7ba6f70..2a8cb810bccf6163de87fa7f2521c9492d8ce648 100644 --- a/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedTransportWriter.java +++ b/api/net/sf/briar/api/plugins/simplex/SimplexSegmentedTransportWriter.java @@ -1,14 +1,19 @@ package net.sf.briar.api.plugins.simplex; +import net.sf.briar.api.plugins.FrameSink; + /** * An interface for writing data to a simplex segmented transport. The writer is * not responsible for authenticating or encrypting the data before writing it. */ -public interface SimplexSegmentedTransportWriter { +public interface SimplexSegmentedTransportWriter extends FrameSink { /** Returns the capacity of the transport in bytes. */ long getCapacity(); + /** Returns the maximum length of a segment in bytes. */ + int getMaximumSegmentLength(); + /** * Returns true if the output stream should be flushed after each packet. */ diff --git a/components/net/sf/briar/transport/SegmentedConnectionDecrypter.java b/components/net/sf/briar/transport/SegmentedConnectionDecrypter.java index 54d285d5ea2667d77497bd2f849a696343e5172e..385d9671209d948597c0f54addafb4addede318a 100644 --- a/components/net/sf/briar/transport/SegmentedConnectionDecrypter.java +++ b/components/net/sf/briar/transport/SegmentedConnectionDecrypter.java @@ -56,7 +56,7 @@ class SegmentedConnectionDecrypter implements FrameSource { throw new FormatException(); // Decrypt the frame try { - int decrypted = frameCipher.update(b, 0, length, b); + int decrypted = frameCipher.doFinal(b, 0, length, b); assert decrypted == length; } catch(GeneralSecurityException badCipher) { throw new RuntimeException(badCipher); diff --git a/test/build.xml b/test/build.xml index 904f92fb1308403bffc6618c51d6848e484ae081..8c643549dee314b3ff7bb973c9ba3d643dd31745 100644 --- a/test/build.xml +++ b/test/build.xml @@ -58,6 +58,8 @@ <test name='net.sf.briar.transport.ConnectionWriterImplTest'/> <test name='net.sf.briar.transport.ConnectionWriterTest'/> <test name='net.sf.briar.transport.FrameReadWriteTest'/> + <test name='net.sf.briar.transport.SegmentedConnectionDecrypterTest'/> + <test name='net.sf.briar.transport.SegmentedConnectionEncrypterTest'/> <test name='net.sf.briar.util.ByteUtilsTest'/> <test name='net.sf.briar.util.FileUtilsTest'/> <test name='net.sf.briar.util.StringUtilsTest'/> diff --git a/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java b/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java index c721d8b59b7150feb0b5a91813f2e30b39b16a4c..f8d09593a49b3ea3be86965291ff9091eb5127e4 100644 --- a/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java +++ b/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java @@ -36,32 +36,22 @@ public class ConnectionDecrypterImplTest extends BriarTestCase { } @Test - public void testInitiatorDecryption() throws Exception { - testDecryption(true); - } - - @Test - public void testResponderDecryption() throws Exception { - testDecryption(false); - } - - private void testDecryption(boolean initiator) throws Exception { + public void testDecryption() throws Exception { // Calculate the ciphertext for the first frame byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH]; HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0); byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize()); IvParameterSpec ivSpec = new IvParameterSpec(iv); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); - byte[] ciphertext = new byte[plaintext.length]; - frameCipher.doFinal(plaintext, 0, plaintext.length, ciphertext); + byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length); // Calculate the ciphertext for the second frame byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH]; HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0); IvEncoder.updateIv(iv, 1L); ivSpec = new IvParameterSpec(iv); frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); - byte[] ciphertext1 = new byte[plaintext1.length]; - frameCipher.doFinal(plaintext1, 0, plaintext1.length, ciphertext1); + byte[] ciphertext1 = frameCipher.doFinal(plaintext1, 0, + plaintext1.length); // Concatenate the ciphertexts ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(ciphertext); diff --git a/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java b/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java index 9b25a30f217a1955564877fa4dbda187b5d005bd..17016265e33992e07afc056e83677c7a18f7cebc 100644 --- a/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java +++ b/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java @@ -35,16 +35,7 @@ public class ConnectionEncrypterImplTest extends BriarTestCase { } @Test - public void testInitiatorEncryption() throws Exception { - testEncryption(true); - } - - @Test - public void testResponderEncryption() throws Exception { - testEncryption(false); - } - - private void testEncryption(boolean initiator) throws Exception { + public void testEncryption() throws Exception { // Calculate the expected tag byte[] tag = TagEncoder.encodeTag(0, tagCipher, tagKey); // Calculate the expected ciphertext for the first frame diff --git a/test/net/sf/briar/transport/SegmentedConnectionDecrypterTest.java b/test/net/sf/briar/transport/SegmentedConnectionDecrypterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a987ede8f8c86c26f04645fc4abc65eba9469e05 --- /dev/null +++ b/test/net/sf/briar/transport/SegmentedConnectionDecrypterTest.java @@ -0,0 +1,88 @@ +package net.sf.briar.transport; + +import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH; + +import java.io.IOException; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.crypto.ErasableKey; +import net.sf.briar.api.plugins.FrameSource; +import net.sf.briar.crypto.CryptoModule; + +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class SegmentedConnectionDecrypterTest extends BriarTestCase { + + private static final int MAC_LENGTH = 32; + + private final Cipher frameCipher; + private final ErasableKey frameKey; + + public SegmentedConnectionDecrypterTest() { + super(); + Injector i = Guice.createInjector(new CryptoModule()); + CryptoComponent crypto = i.getInstance(CryptoComponent.class); + frameCipher = crypto.getFrameCipher(); + frameKey = crypto.generateTestKey(); + } + + @Test + public void testDecryption() throws Exception { + // Calculate the ciphertext for the first frame + byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH]; + HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0); + byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize()); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); + byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length); + // Calculate the ciphertext for the second frame + byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH]; + HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0); + IvEncoder.updateIv(iv, 1L); + ivSpec = new IvParameterSpec(iv); + frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); + byte[] ciphertext1 = frameCipher.doFinal(plaintext1, 0, + plaintext1.length); + // Use a connection decrypter to decrypt the ciphertext + byte[][] frames = new byte[][] { ciphertext, ciphertext1 }; + FrameSource in = new ByteArrayFrameSource(frames); + FrameSource decrypter = new SegmentedConnectionDecrypter(in, + frameCipher, frameKey, MAC_LENGTH); + // First frame + byte[] decrypted = new byte[MAX_FRAME_LENGTH]; + assertEquals(plaintext.length, decrypter.readFrame(decrypted)); + for(int i = 0; i < plaintext.length; i++) { + assertEquals(plaintext[i], decrypted[i]); + } + // Second frame + assertEquals(plaintext1.length, decrypter.readFrame(decrypted)); + for(int i = 0; i < plaintext1.length; i++) { + assertEquals(plaintext1[i], decrypted[i]); + } + } + + private static class ByteArrayFrameSource implements FrameSource { + + private final byte[][] frames; + + private int frame = 0; + + private ByteArrayFrameSource(byte[][] frames) { + this.frames = frames; + } + + public int readFrame(byte[] b) throws IOException { + byte[] src = frames[frame++]; + System.arraycopy(src, 0, b, 0, src.length); + return src.length; + } + } +} diff --git a/test/net/sf/briar/transport/SegmentedConnectionEncrypterTest.java b/test/net/sf/briar/transport/SegmentedConnectionEncrypterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9c4cf62dc6833b1d0cbab6bec69949162ba4bf20 --- /dev/null +++ b/test/net/sf/briar/transport/SegmentedConnectionEncrypterTest.java @@ -0,0 +1,84 @@ +package net.sf.briar.transport; + +import static org.junit.Assert.assertArrayEquals; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.crypto.ErasableKey; +import net.sf.briar.api.plugins.FrameSink; +import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; +import net.sf.briar.crypto.CryptoModule; + +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class SegmentedConnectionEncrypterTest extends BriarTestCase { + + private static final int MAC_LENGTH = 32; + + private final Cipher tagCipher, frameCipher; + private final ErasableKey tagKey, frameKey; + + public SegmentedConnectionEncrypterTest() { + super(); + Injector i = Guice.createInjector(new CryptoModule()); + CryptoComponent crypto = i.getInstance(CryptoComponent.class); + tagCipher = crypto.getTagCipher(); + frameCipher = crypto.getFrameCipher(); + tagKey = crypto.generateTestKey(); + frameKey = crypto.generateTestKey(); + } + + @Test + public void testEncryption() throws Exception { + // Calculate the expected tag + byte[] tag = TagEncoder.encodeTag(0, tagCipher, tagKey); + // Calculate the expected ciphertext for the first frame + byte[] iv = new byte[frameCipher.getBlockSize()]; + byte[] plaintext = new byte[123 + MAC_LENGTH]; + IvParameterSpec ivSpec = new IvParameterSpec(iv); + frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); + byte[] ciphertext = frameCipher.doFinal(plaintext); + // Calculate the expected ciphertext for the second frame + byte[] plaintext1 = new byte[1234 + MAC_LENGTH]; + IvEncoder.updateIv(iv, 1L); + ivSpec = new IvParameterSpec(iv); + frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); + byte[] ciphertext1 = frameCipher.doFinal(plaintext1); + // Concatenate the ciphertexts + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(tag); + out.write(ciphertext); + out.write(ciphertext1); + byte[] expected = out.toByteArray(); + // Use a connection encrypter to encrypt the plaintext + ByteArrayFrameSink sink = new ByteArrayFrameSink(); + ConnectionEncrypter e = new SegmentedConnectionEncrypter(sink, + Long.MAX_VALUE, tagCipher, frameCipher, tagKey, frameKey); + // The first frame's buffer must have enough space for the tag + byte[] b = new byte[TAG_LENGTH + plaintext.length]; + System.arraycopy(plaintext, 0, b, 0, plaintext.length); + e.writeFrame(b, plaintext.length); + e.writeFrame(plaintext1, plaintext1.length); + byte[] actual = out.toByteArray(); + // Check that the actual ciphertext matches the expected ciphertext + assertArrayEquals(expected, actual); + assertEquals(Long.MAX_VALUE - actual.length, e.getRemainingCapacity()); + } + + private static class ByteArrayFrameSink extends ByteArrayOutputStream + implements FrameSink { + + public void writeFrame(byte[] b, int len) throws IOException { + write(b, 0, len); + } + } +}