Plan B: Remove error correction and reliability layers and the

consequent distinction between segments and frames.
This commit is contained in:
akwizgran
2012-02-06 16:03:09 +00:00
parent 899ec5e19e
commit 6da30ca486
84 changed files with 296 additions and 2674 deletions

View File

@@ -56,16 +56,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.FrameWindowImplTest'/>
<test name='net.sf.briar.transport.IncomingEncryptionLayerImplTest'/>
<test name='net.sf.briar.transport.IncomingErrorCorrectionLayerImplTest'/>
<test name='net.sf.briar.transport.IncomingReliabilityLayerImplTest'/>
<test name='net.sf.briar.transport.OutgoingEncryptionLayerImplTest'/>
<test name='net.sf.briar.transport.SegmentedIncomingEncryptionLayerTest'/>
<test name='net.sf.briar.transport.SegmentedOutgoingEncryptionLayerTest'/>
<test name='net.sf.briar.transport.XorErasureCodeTest'/>
<test name='net.sf.briar.transport.XorErasureDecoderTest'/>
<test name='net.sf.briar.transport.XorErasureEncoderTest'/>
<test name='net.sf.briar.util.ByteUtilsTest'/>
<test name='net.sf.briar.util.FileUtilsTest'/>
<test name='net.sf.briar.util.StringUtilsTest'/>

View File

@@ -189,7 +189,7 @@ public class ProtocolIntegrationTest extends BriarTestCase {
byte[] tag = new byte[TAG_LENGTH];
assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH));
ConnectionReader conn = connectionReaderFactory.createConnectionReader(
in, secret.clone(), tag);
in, secret.clone(), true);
InputStream in1 = conn.getInputStream();
ProtocolReader reader = protocolReaderFactory.createProtocolReader(in1);

View File

@@ -27,8 +27,8 @@ public class KeyDerivationTest extends BriarTestCase {
@Test
public void testSixKeysAreDistinct() {
List<ErasableKey> keys = new ArrayList<ErasableKey>();
keys.add(crypto.deriveSegmentKey(secret, true));
keys.add(crypto.deriveSegmentKey(secret, false));
keys.add(crypto.deriveFrameKey(secret, true));
keys.add(crypto.deriveFrameKey(secret, false));
keys.add(crypto.deriveTagKey(secret, true));
keys.add(crypto.deriveTagKey(secret, false));
keys.add(crypto.deriveMacKey(secret, true));

View File

@@ -174,7 +174,7 @@ public class SimplexConnectionReadWriteTest extends BriarTestCase {
IncomingSimplexConnection batchIn = new IncomingSimplexConnection(
new ImmediateExecutor(), new ImmediateExecutor(), db,
connRegistry, connFactory, protoFactory, ctx, transportId,
transport, tag);
transport);
// No messages should have been added yet
assertFalse(listener.messagesAdded);
// Read whatever needs to be read

View File

@@ -15,7 +15,6 @@ import net.sf.briar.api.transport.ConnectionReader;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.junit.Test;
// FIXME: This test covers too many classes
public class ConnectionReaderImplTest extends TransportTest {
public ConnectionReaderImplTest() throws Exception {
@@ -217,14 +216,9 @@ public class ConnectionReaderImplTest extends TransportTest {
}
private ConnectionReader createConnectionReader(InputStream in) {
IncomingEncryptionLayer encryption =
new NullIncomingEncryptionLayer(in);
IncomingErrorCorrectionLayer correction =
new NullIncomingErrorCorrectionLayer(encryption);
IncomingAuthenticationLayer authentication =
new IncomingAuthenticationLayerImpl(correction, mac, macKey, false);
IncomingReliabilityLayer reliability =
new NullIncomingReliabilityLayer(authentication);
return new ConnectionReaderImpl(reliability, false, false);
FrameReader encryption = new NullIncomingEncryptionLayer(in);
FrameReader authentication = new IncomingAuthenticationLayerImpl(
encryption, mac, macKey);
return new ConnectionReaderImpl(authentication);
}
}

View File

@@ -618,7 +618,7 @@ public class ConnectionRecogniserImplTest extends BriarTestCase {
ErasableKey tagKey = crypto.deriveTagKey(secret, true);
Cipher tagCipher = crypto.getTagCipher();
byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
TagEncoder.encodeTag(tag, tagCipher, tagKey);
return tag;
}
}

View File

@@ -12,7 +12,6 @@ import net.sf.briar.api.transport.ConnectionWriter;
import org.junit.Test;
// FIXME: This test covers too many classes
public class ConnectionWriterImplTest extends TransportTest {
public ConnectionWriterImplTest() throws Exception {
@@ -107,14 +106,9 @@ public class ConnectionWriterImplTest extends TransportTest {
}
private ConnectionWriter createConnectionWriter(OutputStream out) {
OutgoingEncryptionLayer encryption =
new NullOutgoingEncryptionLayer(out);
OutgoingErrorCorrectionLayer correction =
new NullOutgoingErrorCorrectionLayer(encryption);
OutgoingAuthenticationLayer authentication =
new OutgoingAuthenticationLayerImpl(correction, mac, macKey);
OutgoingReliabilityLayer reliability =
new NullOutgoingReliabilityLayer(authentication);
return new ConnectionWriterImpl(reliability, false);
FrameWriter encryption = new NullOutgoingEncryptionLayer(out);
FrameWriter authentication =
new OutgoingAuthenticationLayerImpl(encryption, mac, macKey);
return new ConnectionWriterImpl(authentication);
}
}

View File

@@ -27,25 +27,25 @@ import com.google.inject.Injector;
public class FrameReadWriteTest extends BriarTestCase {
private final CryptoComponent crypto;
private final Cipher tagCipher, segCipher;
private final Cipher tagCipher, frameCipher;
private final Mac mac;
private final Random random;
private final byte[] outSecret;
private final ErasableKey tagKey, segKey, macKey;
private final ErasableKey tagKey, frameKey, macKey;
public FrameReadWriteTest() {
super();
Injector i = Guice.createInjector(new CryptoModule());
crypto = i.getInstance(CryptoComponent.class);
tagCipher = crypto.getTagCipher();
segCipher = crypto.getSegmentCipher();
frameCipher = crypto.getFrameCipher();
mac = crypto.getMac();
random = new Random();
// Since we're sending frames to ourselves, we only need outgoing keys
outSecret = new byte[32];
random.nextBytes(outSecret);
tagKey = crypto.deriveTagKey(outSecret, true);
segKey = crypto.deriveSegmentKey(outSecret, true);
frameKey = crypto.deriveFrameKey(outSecret, true);
macKey = crypto.deriveMacKey(outSecret, true);
}
@@ -62,7 +62,7 @@ public class FrameReadWriteTest extends BriarTestCase {
private void testWriteAndRead(boolean initiator) throws Exception {
// Encode the tag
byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
TagEncoder.encodeTag(tag, tagCipher, tagKey);
// Generate two random frames
byte[] frame = new byte[12345];
random.nextBytes(frame);
@@ -70,21 +70,15 @@ public class FrameReadWriteTest extends BriarTestCase {
random.nextBytes(frame1);
// Copy the keys - the copies will be erased
ErasableKey tagCopy = tagKey.copy();
ErasableKey segCopy = segKey.copy();
ErasableKey frameCopy = frameKey.copy();
ErasableKey macCopy = macKey.copy();
// Write the frames
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutgoingEncryptionLayer encryptionOut = new OutgoingEncryptionLayerImpl(
out, Long.MAX_VALUE, tagCipher, segCipher, tagCopy, segCopy,
false);
OutgoingErrorCorrectionLayer correctionOut =
new NullOutgoingErrorCorrectionLayer(encryptionOut);
OutgoingAuthenticationLayer authenticationOut =
new OutgoingAuthenticationLayerImpl(correctionOut, mac, macCopy);
OutgoingReliabilityLayer reliabilityOut =
new NullOutgoingReliabilityLayer(authenticationOut);
ConnectionWriter writer = new ConnectionWriterImpl(reliabilityOut,
false);
FrameWriter encryptionOut = new OutgoingEncryptionLayerImpl(out,
Long.MAX_VALUE, tagCipher, frameCipher, tagCopy, frameCopy);
FrameWriter authenticationOut = new OutgoingAuthenticationLayerImpl(
encryptionOut, mac, macCopy);
ConnectionWriter writer = new ConnectionWriterImpl(authenticationOut);
OutputStream out1 = writer.getOutputStream();
out1.write(frame);
out1.flush();
@@ -95,20 +89,13 @@ public class FrameReadWriteTest extends BriarTestCase {
byte[] recoveredTag = new byte[TAG_LENGTH];
assertEquals(TAG_LENGTH, in.read(recoveredTag));
assertArrayEquals(tag, recoveredTag);
assertEquals(0L, TagEncoder.decodeTag(tag, tagCipher, tagKey));
assertTrue(TagEncoder.decodeTag(tag, tagCipher, tagKey));
// Read the frames back
IncomingEncryptionLayer encryptionIn = new IncomingEncryptionLayerImpl(
in, tagCipher, segCipher, tagKey, segKey, false, false,
recoveredTag);
IncomingErrorCorrectionLayer correctionIn =
new NullIncomingErrorCorrectionLayer(encryptionIn);
IncomingAuthenticationLayer authenticationIn =
new IncomingAuthenticationLayerImpl(correctionIn, mac, macKey,
false);
IncomingReliabilityLayer reliabilityIn =
new NullIncomingReliabilityLayer(authenticationIn);
ConnectionReader reader = new ConnectionReaderImpl(reliabilityIn, false,
false);
FrameReader encryptionIn = new IncomingEncryptionLayerImpl(in,
tagCipher, frameCipher, tagKey, frameKey, false);
FrameReader authenticationIn = new IncomingAuthenticationLayerImpl(
encryptionIn, mac, macKey);
ConnectionReader reader = new ConnectionReaderImpl(authenticationIn);
InputStream in1 = reader.getInputStream();
byte[] recovered = new byte[frame.length];
int offset = 0;

View File

@@ -1,82 +0,0 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_WINDOW_SIZE;
import net.sf.briar.BriarTestCase;
import org.junit.Test;
public class FrameWindowImplTest extends BriarTestCase {
@Test
public void testWindowSliding() {
FrameWindow w = new FrameWindowImpl();
for(int i = 0; i < 100; i++) {
assertTrue(w.contains(i));
assertTrue(w.remove(i));
assertFalse(w.contains(i));
}
for(int i = 100; i < 100 + FRAME_WINDOW_SIZE; i++) {
assertTrue(w.contains(i));
assertFalse(w.isTooHigh(i));
}
assertFalse(w.contains(100 + FRAME_WINDOW_SIZE));
assertTrue(w.isTooHigh(100 + FRAME_WINDOW_SIZE));
}
@Test
public void testWindowJumping() {
FrameWindow w = new FrameWindowImpl();
// Base of the window is 0
for(int i = 0; i < FRAME_WINDOW_SIZE; i++) assertTrue(w.contains(i));
assertFalse(w.contains(FRAME_WINDOW_SIZE));
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE - 1));
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE));
// Remove all numbers except 0 and 5
for(int i = 1; i < 5; i++) assertTrue(w.remove(i));
for(int i = 6; i < FRAME_WINDOW_SIZE; i++) assertTrue(w.remove(i));
// Base of the window should still be 0
assertTrue(w.contains(0));
for(int i = 1; i < 5; i++) assertFalse(w.contains(i));
assertTrue(w.contains(5));
for(int i = 6; i < FRAME_WINDOW_SIZE; i++) assertFalse(w.contains(i));
assertFalse(w.contains(FRAME_WINDOW_SIZE));
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE - 1));
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE));
// Remove 0
assertTrue(w.remove(0));
// Base of the window should now be 5
for(int i = 0; i < 5; i++) assertFalse(w.contains(i));
assertTrue(w.contains(5));
for(int i = 6; i < FRAME_WINDOW_SIZE; i++) assertFalse(w.contains(i));
for(int i = FRAME_WINDOW_SIZE; i < FRAME_WINDOW_SIZE + 5; i++) {
assertTrue(w.contains(i));
}
assertFalse(w.contains(FRAME_WINDOW_SIZE + 5));
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE + 4));
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE + 5));
// Remove all numbers except 5
for(int i = FRAME_WINDOW_SIZE; i < FRAME_WINDOW_SIZE + 5; i++) {
assertTrue(w.remove(i));
}
// Base of the window should still be 5
assertTrue(w.contains(5));
for(int i = 6; i < FRAME_WINDOW_SIZE + 5; i++) {
assertFalse(w.contains(i));
}
assertFalse(w.contains(FRAME_WINDOW_SIZE + 5));
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE + 4));
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE + 5));
// Remove 5
assertTrue(w.remove(5));
// Base of the window should now be FRAME_WINDOW_SIZE + 5
for(int i = 0; i < FRAME_WINDOW_SIZE + 5; i++) {
assertFalse(w.contains(i));
}
for(int i = FRAME_WINDOW_SIZE + 5; i < FRAME_WINDOW_SIZE * 2 + 5; i++) {
assertTrue(w.contains(i));
}
assertFalse(w.contains(FRAME_WINDOW_SIZE * 2 + 5));
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE * 2 + 4));
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE * 2 + 5));
}
}

View File

@@ -12,7 +12,6 @@ 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.transport.Segment;
import net.sf.briar.crypto.CryptoModule;
import org.apache.commons.io.output.ByteArrayOutputStream;
@@ -23,112 +22,106 @@ import com.google.inject.Injector;
public class IncomingEncryptionLayerImplTest extends BriarTestCase {
private final Cipher tagCipher, segCipher;
private final ErasableKey tagKey, segKey;
private final Cipher tagCipher, frameCipher;
private final ErasableKey tagKey, frameKey;
public IncomingEncryptionLayerImplTest() {
super();
Injector i = Guice.createInjector(new CryptoModule());
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
tagCipher = crypto.getTagCipher();
segCipher = crypto.getSegmentCipher();
frameCipher = crypto.getFrameCipher();
tagKey = crypto.generateTestKey();
segKey = crypto.generateTestKey();
frameKey = crypto.generateTestKey();
}
@Test
public void testDecryptionWithFirstSegmentTagged() throws Exception {
// Calculate the tag for the first segment
public void testDecryptionWithTag() throws Exception {
// Calculate the tag
byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
// Calculate the ciphertext for the first segment
TagEncoder.encodeTag(tag, tagCipher, tagKey);
// 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, segCipher.getBlockSize());
byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
IvParameterSpec ivSpec = new IvParameterSpec(iv);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext = segCipher.doFinal(plaintext, 0, plaintext.length);
// Calculate the ciphertext for the second segment
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);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext1 = segCipher.doFinal(plaintext1, 0,
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext1 = frameCipher.doFinal(plaintext1, 0,
plaintext1.length);
// Concatenate the ciphertexts, excluding the first tag
// Concatenate the ciphertexts, including the tag
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(tag);
out.write(ciphertext);
out.write(ciphertext1);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// Use the encryption layer to decrypt the ciphertext
IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
tagCipher, segCipher, tagKey, segKey, false, false, tag);
// First segment
Segment s = new SegmentImpl();
assertTrue(decrypter.readSegment(s));
assertEquals(plaintext.length, s.getLength());
assertEquals(0L, s.getSegmentNumber());
byte[] decrypted = s.getBuffer();
FrameReader decrypter = new IncomingEncryptionLayerImpl(in, tagCipher,
frameCipher, tagKey, frameKey, true);
// First frame
Frame f = new Frame();
assertTrue(decrypter.readFrame(f));
assertEquals(plaintext.length, f.getLength());
byte[] decrypted = f.getBuffer();
assertEquals(0L, HeaderEncoder.getFrameNumber(decrypted));
for(int i = 0; i < plaintext.length; i++) {
assertEquals(plaintext[i], decrypted[i]);
}
// Second segment
assertTrue(decrypter.readSegment(s));
assertEquals(plaintext1.length, s.getLength());
assertEquals(1L, s.getSegmentNumber());
decrypted = s.getBuffer();
// Second frame
assertTrue(decrypter.readFrame(f));
assertEquals(plaintext1.length, f.getLength());
decrypted = f.getBuffer();
assertEquals(1L, HeaderEncoder.getFrameNumber(decrypted));
for(int i = 0; i < plaintext1.length; i++) {
assertEquals(plaintext1[i], decrypted[i]);
}
}
@Test
public void testDecryptionWithEverySegmentTagged() throws Exception {
// Calculate the tag for the first segment
byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
// Calculate the ciphertext for the first segment
public void testDecryptionWithoutTag() 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, segCipher.getBlockSize());
byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
IvParameterSpec ivSpec = new IvParameterSpec(iv);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext = segCipher.doFinal(plaintext, 0, plaintext.length);
// Calculate the tag for the second segment
byte[] tag1 = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag1, 1L, tagCipher, tagKey);
// Calculate the ciphertext for the second segment
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);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext1 = segCipher.doFinal(plaintext1, 0,
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext1 = frameCipher.doFinal(plaintext1, 0,
plaintext1.length);
// Concatenate the ciphertexts, excluding the first tag
// Concatenate the ciphertexts, excluding the tag
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(ciphertext);
out.write(tag1);
out.write(ciphertext1);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// Use the encryption layer to decrypt the ciphertext
IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
tagCipher, segCipher, tagKey, segKey, true, false, tag);
// First segment
Segment s = new SegmentImpl();
assertTrue(decrypter.readSegment(s));
assertEquals(plaintext.length, s.getLength());
assertEquals(0L, s.getSegmentNumber());
byte[] decrypted = s.getBuffer();
FrameReader decrypter = new IncomingEncryptionLayerImpl(in, tagCipher,
frameCipher, tagKey, frameKey, false);
// First frame
Frame f = new Frame();
assertTrue(decrypter.readFrame(f));
assertEquals(plaintext.length, f.getLength());
byte[] decrypted = f.getBuffer();
assertEquals(0L, HeaderEncoder.getFrameNumber(decrypted));
for(int i = 0; i < plaintext.length; i++) {
assertEquals(plaintext[i], decrypted[i]);
}
// Second segment
assertTrue(decrypter.readSegment(s));
assertEquals(plaintext1.length, s.getLength());
assertEquals(1L, s.getSegmentNumber());
decrypted = s.getBuffer();
// Second frame
assertTrue(decrypter.readFrame(f));
assertEquals(plaintext1.length, f.getLength());
assertEquals(1L, HeaderEncoder.getFrameNumber(decrypted));
decrypted = f.getBuffer();
for(int i = 0; i < plaintext1.length; i++) {
assertEquals(plaintext1[i], decrypted[i]);
}

View File

@@ -1,168 +0,0 @@
package net.sf.briar.transport;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Map;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.transport.Segment;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
public class IncomingErrorCorrectionLayerImplTest extends BriarTestCase {
@Test
public void testDiscardedSegmentsAreCounted() throws Exception {
LinkedList<Long> segmentNumbers = new LinkedList<Long>();
segmentNumbers.add(123L); // 123 / 3 = frame number 41
segmentNumbers.add(456L); // 456 / 3 = frame number 152
segmentNumbers.add(0L); // 0 / 3 = frame number 0
IncomingEncryptionLayer in = new TestIncomingEncryptionLayer(
segmentNumbers, 1234);
Mockery context = new Mockery();
final ErasureDecoder decoder = context.mock(ErasureDecoder.class);
final FrameWindow window = context.mock(FrameWindow.class);
context.checking(new Expectations() {{
// First segment
one(window).contains(41L);
will(returnValue(false));
one(window).isTooHigh(41L);
will(returnValue(true));
// Second segment
one(window).contains(152L);
will(returnValue(false));
one(window).isTooHigh(152L);
will(returnValue(true));
// Third segment
one(window).contains(0L);
will(returnValue(true));
one(decoder).decodeFrame(with(any(Frame.class)),
with(any(Segment[].class)));
will(returnValue(false));
}});
IncomingErrorCorrectionLayerImpl err =
new IncomingErrorCorrectionLayerImpl(in, decoder, 3, 2);
Frame f = new Frame();
assertFalse(err.readFrame(f, window));
Map<Long, Integer> discardCounts = err.getDiscardCounts();
assertEquals(2, discardCounts.size());
assertEquals(Integer.valueOf(1), discardCounts.get(41L));
assertEquals(Integer.valueOf(1), discardCounts.get(152L));
context.assertIsSatisfied();
}
@Test
public void testTooManyDiscardedSegmentsCauseException() throws Exception {
LinkedList<Long> segmentNumbers = new LinkedList<Long>();
segmentNumbers.add(123L); // 123 / 3 = frame number 41
segmentNumbers.add(124L); // 124 / 3 = frame number 41
IncomingEncryptionLayer in = new TestIncomingEncryptionLayer(
segmentNumbers, 1234);
Mockery context = new Mockery();
final ErasureDecoder decoder = context.mock(ErasureDecoder.class);
final FrameWindow window = context.mock(FrameWindow.class);
context.checking(new Expectations() {{
// First segment
one(window).contains(41L);
will(returnValue(false));
one(window).isTooHigh(41L);
will(returnValue(true));
// Second segment
one(window).contains(41L);
will(returnValue(false));
one(window).isTooHigh(41L);
will(returnValue(true));
}});
IncomingErrorCorrectionLayerImpl err =
new IncomingErrorCorrectionLayerImpl(in, decoder, 3, 2);
Frame f = new Frame();
try {
err.readFrame(f, window);
fail();
} catch(FormatException expected) {}
context.assertIsSatisfied();
}
@Test
public void testSetsAndDiscardedSegmentsAreFreed() throws Exception {
LinkedList<Long> segmentNumbers = new LinkedList<Long>();
segmentNumbers.add(96L); // 96 / 3 = frame number 32
segmentNumbers.add(0L); // 0 / 3 = frame number 0
segmentNumbers.add(1L); // 1 / 3 = frame number 0
IncomingEncryptionLayer in = new TestIncomingEncryptionLayer(
segmentNumbers, 1234);
Mockery context = new Mockery();
final ErasureDecoder decoder = context.mock(ErasureDecoder.class);
final FrameWindow window = context.mock(FrameWindow.class);
context.checking(new Expectations() {{
// First segment
one(window).contains(32L);
will(returnValue(false));
one(window).isTooHigh(32L);
will(returnValue(true));
// Second segment
one(window).contains(0L);
will(returnValue(true));
one(decoder).decodeFrame(with(any(Frame.class)),
with(any(Segment[].class)));
will(returnValue(false));
// Third segment
one(window).contains(0L);
will(returnValue(true));
one(decoder).decodeFrame(with(any(Frame.class)),
with(any(Segment[].class)));
will(returnValue(true));
// Second call, new window
one(window).contains(0L);
will(returnValue(false));
one(window).isTooHigh(32L);
will(returnValue(false));
}});
IncomingErrorCorrectionLayerImpl err =
new IncomingErrorCorrectionLayerImpl(in, decoder, 3, 2);
Frame f = new Frame();
// The first call discards one segment and decodes two
assertTrue(err.readFrame(f, window));
// The second call reaches EOF
assertFalse(err.readFrame(f, window));
// The segment set and discard count should have been freed
Map<Long, Segment[]> segmentSets = err.getSegmentSets();
assertTrue(segmentSets.isEmpty());
Map<Long, Integer> discardCounts = err.getDiscardCounts();
assertTrue(discardCounts.isEmpty());
context.assertIsSatisfied();
}
private static class TestIncomingEncryptionLayer
implements IncomingEncryptionLayer {
private final LinkedList<Long> segmentNumbers;
private final int length;
private TestIncomingEncryptionLayer(LinkedList<Long> segmentNumbers,
int length) {
this.segmentNumbers = segmentNumbers;
this.length = length;
}
public boolean readSegment(Segment s) throws IOException,
InvalidDataException {
Long segmentNumber = segmentNumbers.poll();
if(segmentNumber == null) return false;
s.setSegmentNumber(segmentNumber);
s.setLength(length);
return true;
}
public int getMaxSegmentLength() {
return length;
}
}
}

View File

@@ -1,95 +0,0 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.FRAME_WINDOW_SIZE;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.transport.ConnectionReader;
import org.junit.Test;
public class IncomingReliabilityLayerImplTest extends BriarTestCase {
@Test
public void testNoReordering() throws Exception {
List<Integer> frameNumbers = new ArrayList<Integer>();
// Receive FRAME_WINDOW_SIZE * 2 frames in the correct order
for(int i = 0; i < FRAME_WINDOW_SIZE * 2; i++) frameNumbers.add(i);
IncomingAuthenticationLayer authentication =
new TestIncomingAuthenticationLayer(frameNumbers);
IncomingReliabilityLayerImpl reliability =
new IncomingReliabilityLayerImpl(authentication);
ConnectionReader reader = new ConnectionReaderImpl(reliability, false,
false);
InputStream in = reader.getInputStream();
for(int i = 0; i < FRAME_WINDOW_SIZE * 2; i++) {
for(int j = 0; j < 100; j++) assertEquals(i, in.read());
}
assertEquals(-1, in.read());
// No free frames should be cached
assertEquals(0, reliability.getFreeFramesCount());
}
@Test
public void testReordering() throws Exception {
List<Integer> frameNumbers = new ArrayList<Integer>();
// Receive the first FRAME_WINDOW_SIZE frames in a random order
for(int i = 0; i < FRAME_WINDOW_SIZE; i++) frameNumbers.add(i);
Collections.shuffle(frameNumbers);
// Receive the next FRAME_WINDOW_SIZE frames in the correct order
for(int i = FRAME_WINDOW_SIZE; i < FRAME_WINDOW_SIZE * 2; i++) {
frameNumbers.add(i);
}
// The reliability layer should reorder the frames
IncomingAuthenticationLayer authentication =
new TestIncomingAuthenticationLayer(frameNumbers);
IncomingReliabilityLayerImpl reliability =
new IncomingReliabilityLayerImpl(authentication);
ConnectionReader reader = new ConnectionReaderImpl(reliability, false,
false);
InputStream in = reader.getInputStream();
for(int i = 0; i < FRAME_WINDOW_SIZE * 2; i++) {
for(int j = 0; j < 100; j++) assertEquals(i, in.read());
}
assertEquals(-1, in.read());
// Fewer than FRAME_WINDOW_SIZE free frames should be cached
assertTrue(reliability.getFreeFramesCount() < 32);
}
private static class TestIncomingAuthenticationLayer
implements IncomingAuthenticationLayer {
private final List<Integer> frameNumbers;
private int index;
private TestIncomingAuthenticationLayer(List<Integer> frameNumbers) {
this.frameNumbers = frameNumbers;
index = 0;
}
public boolean readFrame(Frame f, FrameWindow window) {
if(index >= frameNumbers.size()) return false;
int frameNumber = frameNumbers.get(index);
assertTrue(window.contains(frameNumber));
index++;
byte[] buf = f.getBuffer();
HeaderEncoder.encodeHeader(buf, frameNumber, 100, 0);
for(int i = 0; i < 100; i++) {
buf[FRAME_HEADER_LENGTH + i] = (byte) frameNumber;
}
f.setLength(FRAME_HEADER_LENGTH + 100 + MAC_LENGTH);
return true;
}
public int getMaxFrameLength() {
return FRAME_HEADER_LENGTH + 100 + MAC_LENGTH;
}
}
}

View File

@@ -3,29 +3,24 @@ package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.transport.Segment;
/** An encryption layer that performs no encryption. */
class NullIncomingEncryptionLayer implements IncomingEncryptionLayer {
class NullIncomingEncryptionLayer implements FrameReader {
private final InputStream in;
private long segmentNumber = 0L;
NullIncomingEncryptionLayer(InputStream in) {
this.in = in;
}
public boolean readSegment(Segment s) throws IOException {
byte[] buf = s.getBuffer();
public boolean readFrame(Frame f) throws IOException {
byte[] buf = f.getBuffer();
// Read the frame header
int offset = 0, length = FRAME_HEADER_LENGTH;
while(offset < length) {
@@ -47,12 +42,7 @@ class NullIncomingEncryptionLayer implements IncomingEncryptionLayer {
if(read == -1) throw new EOFException();
offset += read;
}
s.setLength(length);
s.setSegmentNumber(segmentNumber++);
f.setLength(length);
return true;
}
public int getMaxSegmentLength() {
return MAX_SEGMENT_LENGTH - TAG_LENGTH;
}
}

View File

@@ -1,15 +1,10 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import java.io.IOException;
import java.io.OutputStream;
import net.sf.briar.api.transport.Segment;
/** An encryption layer that performs no encryption. */
class NullOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
class NullOutgoingEncryptionLayer implements FrameWriter {
private final OutputStream out;
@@ -25,9 +20,9 @@ class NullOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
this.capacity = capacity;
}
public void writeSegment(Segment s) throws IOException {
out.write(s.getBuffer(), 0, s.getLength());
capacity -= s.getLength();
public void writeFrame(Frame f) throws IOException {
out.write(f.getBuffer(), 0, f.getLength());
capacity -= f.getLength();
}
public void flush() throws IOException {
@@ -37,8 +32,4 @@ class NullOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
public long getRemainingCapacity() {
return capacity;
}
public int getMaxSegmentLength() {
return MAX_SEGMENT_LENGTH - TAG_LENGTH;
}
}

View File

@@ -11,7 +11,6 @@ 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.transport.Segment;
import net.sf.briar.crypto.CryptoModule;
import org.junit.Test;
@@ -23,36 +22,36 @@ public class OutgoingEncryptionLayerImplTest extends BriarTestCase {
private static final int MAC_LENGTH = 32;
private final Cipher tagCipher, segCipher;
private final ErasableKey tagKey, segKey;
private final Cipher tagCipher, frameCipher;
private final ErasableKey tagKey, frameKey;
public OutgoingEncryptionLayerImplTest() {
super();
Injector i = Guice.createInjector(new CryptoModule());
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
tagCipher = crypto.getTagCipher();
segCipher = crypto.getSegmentCipher();
frameCipher = crypto.getFrameCipher();
tagKey = crypto.generateTestKey();
segKey = crypto.generateTestKey();
frameKey = crypto.generateTestKey();
}
@Test
public void testEncryptionWithFirstSegmentTagged() throws Exception {
public void testEncryptionWithTag() throws Exception {
// Calculate the expected tag
byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
// Calculate the expected ciphertext for the first segment
byte[] iv = new byte[segCipher.getBlockSize()];
TagEncoder.encodeTag(tag, 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);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext = segCipher.doFinal(plaintext);
// Calculate the expected ciphertext for the second segment
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);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext1 = segCipher.doFinal(plaintext1);
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
byte[] ciphertext1 = frameCipher.doFinal(plaintext1);
// Concatenate the ciphertexts
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(tag);
@@ -61,65 +60,15 @@ public class OutgoingEncryptionLayerImplTest extends BriarTestCase {
byte[] expected = out.toByteArray();
// Use the encryption layer to encrypt the plaintext
out.reset();
OutgoingEncryptionLayer encrypter = new OutgoingEncryptionLayerImpl(out,
Long.MAX_VALUE, tagCipher, segCipher, tagKey, segKey,
false);
Segment s = new SegmentImpl();
System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
s.setLength(plaintext.length);
s.setSegmentNumber(0L);
encrypter.writeSegment(s);
System.arraycopy(plaintext1, 0, s.getBuffer(), 0, plaintext1.length);
s.setLength(plaintext1.length);
s.setSegmentNumber(1L);
encrypter.writeSegment(s);
byte[] actual = out.toByteArray();
// Check that the actual ciphertext matches the expected ciphertext
assertArrayEquals(expected, actual);
assertEquals(Long.MAX_VALUE - actual.length,
encrypter.getRemainingCapacity());
}
@Test
public void testEncryptionWithEverySegmentTagged() throws Exception {
// Calculate the expected tag for the first segment
byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
// Calculate the expected ciphertext for the first segment
byte[] iv = new byte[segCipher.getBlockSize()];
byte[] plaintext = new byte[123 + MAC_LENGTH];
IvParameterSpec ivSpec = new IvParameterSpec(iv);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext = segCipher.doFinal(plaintext);
// Calculate the expected tag for the second segment
byte[] tag1 = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag1, 1L, tagCipher, tagKey);
// Calculate the expected ciphertext for the second segment
byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
IvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext1 = segCipher.doFinal(plaintext1);
// Concatenate the ciphertexts
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(tag);
out.write(ciphertext);
out.write(tag1);
out.write(ciphertext1);
byte[] expected = out.toByteArray();
// Use the encryption layer to encrypt the plaintext
out.reset();
OutgoingEncryptionLayer encrypter = new OutgoingEncryptionLayerImpl(out,
Long.MAX_VALUE, tagCipher, segCipher, tagKey, segKey, true);
Segment s = new SegmentImpl();
System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
s.setLength(plaintext.length);
s.setSegmentNumber(0L);
encrypter.writeSegment(s);
System.arraycopy(plaintext1, 0, s.getBuffer(), 0, plaintext1.length);
s.setLength(plaintext1.length);
s.setSegmentNumber(1L);
encrypter.writeSegment(s);
FrameWriter encrypter = new OutgoingEncryptionLayerImpl(out,
Long.MAX_VALUE, tagCipher, frameCipher, tagKey, frameKey);
Frame f = new Frame();
System.arraycopy(plaintext, 0, f.getBuffer(), 0, plaintext.length);
f.setLength(plaintext.length);
encrypter.writeFrame(f);
System.arraycopy(plaintext1, 0, f.getBuffer(), 0, plaintext1.length);
f.setLength(plaintext1.length);
encrypter.writeFrame(f);
byte[] actual = out.toByteArray();
// Check that the actual ciphertext matches the expected ciphertext
assertArrayEquals(expected, actual);

View File

@@ -1,158 +0,0 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_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.SegmentSource;
import net.sf.briar.api.transport.Segment;
import net.sf.briar.crypto.CryptoModule;
import org.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class SegmentedIncomingEncryptionLayerTest extends BriarTestCase {
private final Cipher tagCipher, segCipher;
private final ErasableKey tagKey, segKey;
public SegmentedIncomingEncryptionLayerTest() {
super();
Injector i = Guice.createInjector(new CryptoModule());
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
tagCipher = crypto.getTagCipher();
segCipher = crypto.getSegmentCipher();
tagKey = crypto.generateTestKey();
segKey = crypto.generateTestKey();
}
@Test
public void testDecryptionWithFirstSegmentTagged() throws Exception {
// Calculate the ciphertext for the first segment, including its tag
byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
byte[] ciphertext = new byte[TAG_LENGTH + plaintext.length];
TagEncoder.encodeTag(ciphertext, 0L, tagCipher, tagKey);
byte[] iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
IvParameterSpec ivSpec = new IvParameterSpec(iv);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
segCipher.doFinal(plaintext, 0, plaintext.length, ciphertext,
TAG_LENGTH);
// Calculate the ciphertext for the second segment
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
IvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext1 = segCipher.doFinal(plaintext1, 0,
plaintext1.length);
// Buffer the first segment and create a source for the second
Segment buffered = new SegmentImpl();
System.arraycopy(ciphertext, 0, buffered.getBuffer(), 0,
ciphertext.length);
buffered.setLength(ciphertext.length);
SegmentSource in = new ByteArraySegmentSource(ciphertext1);
// Use the encryption layer to decrypt the ciphertext
IncomingEncryptionLayer decrypter =
new SegmentedIncomingEncryptionLayer(in, tagCipher, segCipher,
tagKey, segKey, false, false, buffered);
// First segment
Segment s = new SegmentImpl();
assertTrue(decrypter.readSegment(s));
assertEquals(plaintext.length, s.getLength());
assertEquals(0L, s.getSegmentNumber());
byte[] decrypted = s.getBuffer();
for(int i = 0; i < plaintext.length; i++) {
assertEquals(plaintext[i], decrypted[i]);
}
// Second segment
assertTrue(decrypter.readSegment(s));
assertEquals(plaintext1.length, s.getLength());
assertEquals(1L, s.getSegmentNumber());
decrypted = s.getBuffer();
for(int i = 0; i < plaintext1.length; i++) {
assertEquals(plaintext1[i], decrypted[i]);
}
}
@Test
public void testDecryptionWithEverySegmentTagged() throws Exception {
// Calculate the ciphertext for the first segment, including its tag
byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
byte[] ciphertext = new byte[TAG_LENGTH + plaintext.length];
TagEncoder.encodeTag(ciphertext, 0L, tagCipher, tagKey);
byte[] iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
IvParameterSpec ivSpec = new IvParameterSpec(iv);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
segCipher.doFinal(plaintext, 0, plaintext.length, ciphertext,
TAG_LENGTH);
// Calculate the ciphertext for the second frame, including its tag
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
byte[] ciphertext1 = new byte[TAG_LENGTH + plaintext1.length];
TagEncoder.encodeTag(ciphertext1, 1L, tagCipher, tagKey);
IvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
segCipher.doFinal(plaintext1, 0, plaintext1.length, ciphertext1,
TAG_LENGTH);
// Buffer the first segment and create a source for the second
Segment buffered = new SegmentImpl();
System.arraycopy(ciphertext, 0, buffered.getBuffer(), 0,
ciphertext.length);
buffered.setLength(ciphertext.length);
SegmentSource in = new ByteArraySegmentSource(ciphertext1);
// Use the encryption layer to decrypt the ciphertext
IncomingEncryptionLayer decrypter =
new SegmentedIncomingEncryptionLayer(in, tagCipher, segCipher,
tagKey, segKey, true, false, buffered);
// First segment
Segment s = new SegmentImpl();
assertTrue(decrypter.readSegment(s));
assertEquals(plaintext.length, s.getLength());
assertEquals(0L, s.getSegmentNumber());
byte[] decrypted = s.getBuffer();
for(int i = 0; i < plaintext.length; i++) {
assertEquals(plaintext[i], decrypted[i]);
}
// Second segment
assertTrue(decrypter.readSegment(s));
assertEquals(plaintext1.length, s.getLength());
assertEquals(1L, s.getSegmentNumber());
decrypted = s.getBuffer();
for(int i = 0; i < plaintext1.length; i++) {
assertEquals(plaintext1[i], decrypted[i]);
}
}
private static class ByteArraySegmentSource implements SegmentSource {
private final byte[] segment;
private ByteArraySegmentSource(byte[] segment) {
this.segment = segment;
}
public boolean readSegment(Segment s) throws IOException {
System.arraycopy(segment, 0, s.getBuffer(), 0, segment.length);
s.setLength(segment.length);
return true;
}
public int getMaxSegmentLength() {
return MAX_SEGMENT_LENGTH;
}
}
}

View File

@@ -1,145 +0,0 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
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.SegmentSink;
import net.sf.briar.api.transport.Segment;
import net.sf.briar.crypto.CryptoModule;
import org.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class SegmentedOutgoingEncryptionLayerTest extends BriarTestCase {
private static final int MAC_LENGTH = 32;
private final Cipher tagCipher, segCipher;
private final ErasableKey tagKey, segKey;
public SegmentedOutgoingEncryptionLayerTest() {
super();
Injector i = Guice.createInjector(new CryptoModule());
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
tagCipher = crypto.getTagCipher();
segCipher = crypto.getSegmentCipher();
tagKey = crypto.generateTestKey();
segKey = crypto.generateTestKey();
}
@Test
public void testEncryptionWithFirstSegmentTagged() throws Exception {
// Calculate the expected tag
byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
// Calculate the expected ciphertext for the first segment
byte[] iv = new byte[segCipher.getBlockSize()];
byte[] plaintext = new byte[123 + MAC_LENGTH];
IvParameterSpec ivSpec = new IvParameterSpec(iv);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext = segCipher.doFinal(plaintext);
// Calculate the expected ciphertext for the second segment
byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
IvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext1 = segCipher.doFinal(plaintext1);
// Concatenate the ciphertexts
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(tag);
out.write(ciphertext);
out.write(ciphertext1);
byte[] expected = out.toByteArray();
// Use the encryption layer to encrypt the plaintext
ByteArraySegmentSink sink = new ByteArraySegmentSink();
OutgoingEncryptionLayer encrypter =
new SegmentedOutgoingEncryptionLayer(sink, Long.MAX_VALUE,
tagCipher, segCipher, tagKey, segKey, false, false);
Segment s = new SegmentImpl();
System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
s.setLength(plaintext.length);
s.setSegmentNumber(0L);
encrypter.writeSegment(s);
System.arraycopy(plaintext1, 0, s.getBuffer(), 0, plaintext1.length);
s.setLength(plaintext1.length);
s.setSegmentNumber(1L);
encrypter.writeSegment(s);
byte[] actual = out.toByteArray();
// Check that the actual ciphertext matches the expected ciphertext
assertArrayEquals(expected, actual);
assertEquals(Long.MAX_VALUE - actual.length,
encrypter.getRemainingCapacity());
}
@Test
public void testEncryptionWithEverySegmentTagged() throws Exception {
// Calculate the expected tag for the first segment
byte[] tag = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
// Calculate the expected ciphertext for the first segment
byte[] iv = new byte[segCipher.getBlockSize()];
byte[] plaintext = new byte[123 + MAC_LENGTH];
IvParameterSpec ivSpec = new IvParameterSpec(iv);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext = segCipher.doFinal(plaintext);
// Calculate the expected tag for the second segment
byte[] tag1 = new byte[TAG_LENGTH];
TagEncoder.encodeTag(tag1, 1L, tagCipher, tagKey);
// Calculate the expected ciphertext for the second segment
byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
IvEncoder.updateIv(iv, 1L);
ivSpec = new IvParameterSpec(iv);
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
byte[] ciphertext1 = segCipher.doFinal(plaintext1);
// Concatenate the ciphertexts
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(tag);
out.write(ciphertext);
out.write(tag1);
out.write(ciphertext1);
byte[] expected = out.toByteArray();
// Use the encryption layer to encrypt the plaintext
SegmentSink sink = new ByteArraySegmentSink();
OutgoingEncryptionLayer encrypter =
new SegmentedOutgoingEncryptionLayer(sink, Long.MAX_VALUE,
tagCipher, segCipher, tagKey, segKey, true, false);
Segment s = new SegmentImpl();
System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
s.setLength(plaintext.length);
s.setSegmentNumber(0L);
encrypter.writeSegment(s);
System.arraycopy(plaintext1, 0, s.getBuffer(), 0, plaintext1.length);
s.setLength(plaintext1.length);
s.setSegmentNumber(1L);
encrypter.writeSegment(s);
byte[] actual = out.toByteArray();
// Check that the actual ciphertext matches the expected ciphertext
assertArrayEquals(expected, actual);
assertEquals(Long.MAX_VALUE - actual.length,
encrypter.getRemainingCapacity());
}
private static class ByteArraySegmentSink extends ByteArrayOutputStream
implements SegmentSink {
public void writeSegment(Segment s) throws IOException {
write(s.getBuffer(), 0, s.getLength());
}
public int getMaxSegmentLength() {
return MAX_SEGMENT_LENGTH;
}
}
}

View File

@@ -1,50 +0,0 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import java.util.Random;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.transport.Segment;
import org.junit.Test;
public class XorErasureCodeTest extends BriarTestCase {
@Test
public void testEncodingAndDecodingWithAllSegments() throws Exception {
XorErasureEncoder e = new XorErasureEncoder(5);
XorErasureDecoder d = new XorErasureDecoder(5, false);
Frame f = new Frame(1234);
new Random().nextBytes(f.getBuffer());
int payload = 1234 - FRAME_HEADER_LENGTH - MAC_LENGTH;
HeaderEncoder.encodeHeader(f.getBuffer(), 0L, payload, 0);
f.setLength(1234);
Segment[] set = e.encodeFrame(f);
assertEquals(5, set.length);
Frame f1 = new Frame(1234);
assertTrue(d.decodeFrame(f1, set));
assertArrayEquals(f.getBuffer(), f1.getBuffer());
}
@Test
public void testEncodingAndDecodingWithMissingSegment() throws Exception {
XorErasureEncoder e = new XorErasureEncoder(5);
XorErasureDecoder d = new XorErasureDecoder(5, false);
Frame f = new Frame(1234);
new Random().nextBytes(f.getBuffer());
int payload = 1234 - FRAME_HEADER_LENGTH - MAC_LENGTH;
HeaderEncoder.encodeHeader(f.getBuffer(), 0L, payload, 0);
f.setLength(1234);
for(int i = 0; i < 5; i++) {
Segment[] set = e.encodeFrame(f);
assertEquals(5, set.length);
set[i] = null;
Frame f1 = new Frame(1234);
assertTrue(d.decodeFrame(f1, set));
assertArrayEquals(f.getBuffer(), f1.getBuffer());
}
}
}

View File

@@ -1,94 +0,0 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.transport.Segment;
import org.junit.Test;
public class XorErasureDecoderTest extends BriarTestCase {
@Test
public void testMaximumLength() throws Exception {
XorErasureDecoder d = new XorErasureDecoder(5, false);
// A frame of the maximum length should be decoded successfully
Segment[] set = encodeEmptyFrame(MAX_FRAME_LENGTH / 4, 5);
Frame f = new Frame();
assertTrue(d.decodeFrame(f, set));
checkFrame(f, MAX_FRAME_LENGTH);
// A frame larger than the maximum length should not be decoded
set = encodeEmptyFrame(MAX_FRAME_LENGTH / 4 + 1, 5);
f = new Frame();
try {
d.decodeFrame(f, set);
} catch(FormatException expected) {}
}
@Test
public void testMinimumLengthIsUsed() throws Exception {
Segment[] set = encodeEmptyFrame(250, 4);
// Replace one of the pieces with a longer piece
byte[] b = set[1].getBuffer();
assertArrayEquals(new byte[250], b);
set[1] = new SegmentImpl(251);
set[1].setLength(251);
// The frame should be decoded successfully
XorErasureDecoder d = new XorErasureDecoder(4, false);
Frame f = new Frame(750);
assertTrue(d.decodeFrame(f, set));
// The minimum of the segments' lengths should have been used
assertEquals(750, f.getLength());
}
@Test
public void testDecodingWithMissingSegment() throws Exception {
XorErasureDecoder d = new XorErasureDecoder(4, false);
for(int i = 0; i < 4; i++) {
Segment[] set = encodeEmptyFrame(250, 4);
set[i] = null;
// The frame should be decoded successfully
Frame f = new Frame(750);
assertTrue(d.decodeFrame(f, set));
checkFrame(f, 750);
}
}
@Test
public void testDecodingWithTwoMissingSegments() throws Exception {
XorErasureDecoder d = new XorErasureDecoder(4, false);
Segment[] set = encodeEmptyFrame(250, 4);
set[0] = null;
set[1] = null;
Frame f = new Frame(750);
assertFalse(d.decodeFrame(f, set));
}
private Segment[] encodeEmptyFrame(int length, int n) {
Segment[] set = new Segment[n];
for(int i = 0; i < n; i++) {
set[i] = new SegmentImpl(length);
set[i].setLength(length);
}
int payload = length * (n - 1) - FRAME_HEADER_LENGTH - MAC_LENGTH;
HeaderEncoder.encodeHeader(set[0].getBuffer(), 0L, payload, 0);
HeaderEncoder.encodeHeader(set[n - 1].getBuffer(), 0L, payload, 0);
return set;
}
private void checkFrame(Frame f, int length) {
byte[] b = f.getBuffer();
assertEquals(0L, HeaderEncoder.getFrameNumber(b));
int payload = length - FRAME_HEADER_LENGTH - MAC_LENGTH;
assertEquals(payload, HeaderEncoder.getPayloadLength(b));
assertEquals(0, HeaderEncoder.getPaddingLength(b));
// Check the body
assertEquals(length, f.getLength());
for(int i = FRAME_HEADER_LENGTH; i < length; i++) {
assertEquals("" + i, 0, b[i]);
}
}
}

View File

@@ -1,40 +0,0 @@
package net.sf.briar.transport;
import static org.junit.Assert.assertArrayEquals;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.transport.Segment;
import org.junit.Test;
public class XorErasureEncoderTest extends BriarTestCase {
@Test
public void testEncoding() {
// Create a frame
Frame f = new Frame();
f.setLength(200);
byte[] b = f.getBuffer();
for(int i = 0; i < 200; i++) b[i] = (byte) i;
// Encode the frame
XorErasureEncoder e = new XorErasureEncoder(4);
Segment[] set = e.encodeFrame(f);
// There should be four pieces of 67 bytes each
assertEquals(4, set.length);
for(int i = 0; i < 4; i++) assertEquals(67, set[i].getLength());
// The first three pieces should contain the data plus on padding byte
byte[] b1 = set[0].getBuffer();
for(int i = 0; i < 67; i++) assertEquals((byte) i, b1[i]);
byte[] b2 = set[1].getBuffer();
for(int i = 0; i < 67; i++) assertEquals((byte) (i + 67), b2[i]);
byte[] b3 = set[2].getBuffer();
for(int i = 0; i < 66; i++) assertEquals((byte) (i + 134), b3[i]);
assertEquals(0, b3[66]);
// The fourth piece should be the XOR of the other three
byte[] b4 = set[3].getBuffer();
byte[] expected = new byte[67];
for(int i = 0; i < 67; i++) {
expected[i] = (byte) (b1[i] ^ b2[i] ^ b3[i]);
}
assertArrayEquals(expected, b4);
}
}