mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Frame the encrypted data independently of inter-packet boundaries and
authenticate each frame before parsing its contents. Each connection starts with a tag, followed by any number of frames, each starting with the frame number (32 bits) and payload length (16 bits), and ending with a MAC (256 bits). Tags have the following format: 32 bits reserved, 16 bits for the transport ID, 32 bits for the connection number, 32 bits (set to zero in the tag) for the frame number, and 16 bits (set to zero in the tag) for the block number. The tag is encrypted with the tag key in ECB mode. Frame numbers for each connection must start from zero and must be contiguous and strictly increasing. Each frame is encrypted with the frame key in CTR mode, using the plaintext tag with the appropriate frame number to initialise the counter. The maximum frame size is 64 KiB, including header and footer. The maximum amount of data that can be sent over a connection is 2^32 frames - roughly 2^48 bytes, or 8 terabytes, with the maximum frame size of 64 KiB. If that isn't sufficient we can add another 16 bits to the frame counter.
This commit is contained in:
@@ -36,15 +36,14 @@
|
||||
<test name='net.sf.briar.serial.ReaderImplTest'/>
|
||||
<test name='net.sf.briar.serial.WriterImplTest'/>
|
||||
<test name='net.sf.briar.setup.SetupWorkerTest'/>
|
||||
<test name='net.sf.briar.transport.ConnectionDecrypterImplTest'/>
|
||||
<test name='net.sf.briar.transport.ConnectionEncrypterImplTest'/>
|
||||
<test name='net.sf.briar.transport.ConnectionReaderImplTest'/>
|
||||
<test name='net.sf.briar.transport.ConnectionRecogniserImplTest'/>
|
||||
<test name='net.sf.briar.transport.ConnectionWindowImplTest'/>
|
||||
<test name='net.sf.briar.transport.PacketDecrypterImplTest'/>
|
||||
<test name='net.sf.briar.transport.PacketEncrypterImplTest'/>
|
||||
<test name='net.sf.briar.transport.PacketReaderImplTest'/>
|
||||
<test name='net.sf.briar.transport.PacketReadWriteTest'/>
|
||||
<test name='net.sf.briar.transport.PacketWriterImplTest'/>
|
||||
<test name='net.sf.briar.transport.TagDecoderTest'/>
|
||||
<test name='net.sf.briar.transport.TagEncoderTest'/>
|
||||
<test name='net.sf.briar.transport.ConnectionWriterImplTest'/>
|
||||
<test name='net.sf.briar.transport.FrameReadWriteTest'/>
|
||||
<test name='net.sf.briar.util.ByteUtilsTest'/>
|
||||
<test name='net.sf.briar.util.FileUtilsTest'/>
|
||||
<test name='net.sf.briar.util.StringUtilsTest'/>
|
||||
<test name='net.sf.briar.util.ZipUtilsTest'/>
|
||||
|
||||
@@ -41,10 +41,10 @@ import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.writers.RequestWriter;
|
||||
import net.sf.briar.api.protocol.writers.SubscriptionWriter;
|
||||
import net.sf.briar.api.protocol.writers.TransportWriter;
|
||||
import net.sf.briar.api.transport.PacketReader;
|
||||
import net.sf.briar.api.transport.PacketReaderFactory;
|
||||
import net.sf.briar.api.transport.PacketWriter;
|
||||
import net.sf.briar.api.transport.PacketWriterFactory;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
import net.sf.briar.api.transport.ConnectionReaderFactory;
|
||||
import net.sf.briar.api.transport.ConnectionWriter;
|
||||
import net.sf.briar.api.transport.ConnectionWriterFactory;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.protocol.ProtocolModule;
|
||||
import net.sf.briar.protocol.writers.WritersModule;
|
||||
@@ -66,8 +66,8 @@ public class FileReadWriteTest extends TestCase {
|
||||
private final BatchId ack = new BatchId(TestUtils.getRandomId());
|
||||
private final long timestamp = System.currentTimeMillis();
|
||||
|
||||
private final PacketReaderFactory packetReaderFactory;
|
||||
private final PacketWriterFactory packetWriterFactory;
|
||||
private final ConnectionReaderFactory connectionReaderFactory;
|
||||
private final ConnectionWriterFactory connectionWriterFactory;
|
||||
private final ProtocolReaderFactory protocolReaderFactory;
|
||||
private final ProtocolWriterFactory protocolWriterFactory;
|
||||
private final CryptoComponent crypto;
|
||||
@@ -87,8 +87,8 @@ public class FileReadWriteTest extends TestCase {
|
||||
Injector i = Guice.createInjector(new CryptoModule(),
|
||||
new ProtocolModule(), new SerialModule(), new TransportModule(),
|
||||
new WritersModule());
|
||||
packetReaderFactory = i.getInstance(PacketReaderFactory.class);
|
||||
packetWriterFactory = i.getInstance(PacketWriterFactory.class);
|
||||
connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class);
|
||||
connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class);
|
||||
protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class);
|
||||
protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class);
|
||||
crypto = i.getInstance(CryptoComponent.class);
|
||||
@@ -134,14 +134,13 @@ public class FileReadWriteTest extends TestCase {
|
||||
public void testWriteFile() throws Exception {
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
// Use Alice's secret for writing
|
||||
PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out,
|
||||
ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out,
|
||||
transportId, connection, aliceSecret);
|
||||
out = packetWriter.getOutputStream();
|
||||
out = w.getOutputStream();
|
||||
|
||||
AckWriter a = protocolWriterFactory.createAckWriter(out);
|
||||
assertTrue(a.writeBatchId(ack));
|
||||
a.finish();
|
||||
packetWriter.finishPacket();
|
||||
|
||||
BatchWriter b = protocolWriterFactory.createBatchWriter(out);
|
||||
assertTrue(b.writeMessage(message.getBytes()));
|
||||
@@ -149,7 +148,6 @@ public class FileReadWriteTest extends TestCase {
|
||||
assertTrue(b.writeMessage(message2.getBytes()));
|
||||
assertTrue(b.writeMessage(message3.getBytes()));
|
||||
b.finish();
|
||||
packetWriter.finishPacket();
|
||||
|
||||
OfferWriter o = protocolWriterFactory.createOfferWriter(out);
|
||||
assertTrue(o.writeMessageId(message.getId()));
|
||||
@@ -157,14 +155,12 @@ public class FileReadWriteTest extends TestCase {
|
||||
assertTrue(o.writeMessageId(message2.getId()));
|
||||
assertTrue(o.writeMessageId(message3.getId()));
|
||||
o.finish();
|
||||
packetWriter.finishPacket();
|
||||
|
||||
RequestWriter r = protocolWriterFactory.createRequestWriter(out);
|
||||
BitSet requested = new BitSet(4);
|
||||
requested.set(1);
|
||||
requested.set(3);
|
||||
r.writeRequest(offerId, requested, 4);
|
||||
packetWriter.finishPacket();
|
||||
|
||||
SubscriptionWriter s =
|
||||
protocolWriterFactory.createSubscriptionWriter(out);
|
||||
@@ -173,14 +169,12 @@ public class FileReadWriteTest extends TestCase {
|
||||
subs.put(group, 0L);
|
||||
subs.put(group1, 0L);
|
||||
s.writeSubscriptions(subs, timestamp);
|
||||
packetWriter.finishPacket();
|
||||
|
||||
TransportWriter t = protocolWriterFactory.createTransportWriter(out);
|
||||
t.writeTransports(transports, timestamp);
|
||||
packetWriter.finishPacket();
|
||||
|
||||
out.flush();
|
||||
out.close();
|
||||
w.getOutputStream().flush();
|
||||
w.getOutputStream().close();
|
||||
assertTrue(file.exists());
|
||||
assertTrue(file.length() > message.getSize());
|
||||
}
|
||||
@@ -200,22 +194,20 @@ public class FileReadWriteTest extends TestCase {
|
||||
}
|
||||
assertEquals(16, offset);
|
||||
// Use Bob's secret for reading
|
||||
PacketReader packetReader = packetReaderFactory.createPacketReader(
|
||||
firstTag, in, transportId, connection, bobSecret);
|
||||
in = packetReader.getInputStream();
|
||||
ConnectionReader r = connectionReaderFactory.createConnectionReader(in,
|
||||
transportId, connection, bobSecret);
|
||||
in = r.getInputStream();
|
||||
ProtocolReader protocolReader =
|
||||
protocolReaderFactory.createProtocolReader(in);
|
||||
|
||||
// Read the ack
|
||||
assertTrue(protocolReader.hasAck());
|
||||
Ack a = protocolReader.readAck();
|
||||
packetReader.finishPacket();
|
||||
assertEquals(Collections.singletonList(ack), a.getBatchIds());
|
||||
|
||||
// Read the batch
|
||||
assertTrue(protocolReader.hasBatch());
|
||||
Batch b = protocolReader.readBatch();
|
||||
packetReader.finishPacket();
|
||||
Collection<Message> messages = b.getMessages();
|
||||
assertEquals(4, messages.size());
|
||||
Iterator<Message> it = messages.iterator();
|
||||
@@ -227,7 +219,6 @@ public class FileReadWriteTest extends TestCase {
|
||||
// Read the offer
|
||||
assertTrue(protocolReader.hasOffer());
|
||||
Offer o = protocolReader.readOffer();
|
||||
packetReader.finishPacket();
|
||||
Collection<MessageId> offered = o.getMessageIds();
|
||||
assertEquals(4, offered.size());
|
||||
Iterator<MessageId> it1 = offered.iterator();
|
||||
@@ -238,10 +229,9 @@ public class FileReadWriteTest extends TestCase {
|
||||
|
||||
// Read the request
|
||||
assertTrue(protocolReader.hasRequest());
|
||||
Request r = protocolReader.readRequest();
|
||||
packetReader.finishPacket();
|
||||
assertEquals(offerId, r.getOfferId());
|
||||
BitSet requested = r.getBitmap();
|
||||
Request req = protocolReader.readRequest();
|
||||
assertEquals(offerId, req.getOfferId());
|
||||
BitSet requested = req.getBitmap();
|
||||
assertFalse(requested.get(0));
|
||||
assertTrue(requested.get(1));
|
||||
assertFalse(requested.get(2));
|
||||
@@ -252,7 +242,6 @@ public class FileReadWriteTest extends TestCase {
|
||||
// Read the subscription update
|
||||
assertTrue(protocolReader.hasSubscriptionUpdate());
|
||||
SubscriptionUpdate s = protocolReader.readSubscriptionUpdate();
|
||||
packetReader.finishPacket();
|
||||
Map<Group, Long> subs = s.getSubscriptions();
|
||||
assertEquals(2, subs.size());
|
||||
assertEquals(Long.valueOf(0L), subs.get(group));
|
||||
@@ -262,7 +251,6 @@ public class FileReadWriteTest extends TestCase {
|
||||
// Read the transport update
|
||||
assertTrue(protocolReader.hasTransportUpdate());
|
||||
TransportUpdate t = protocolReader.readTransportUpdate();
|
||||
packetReader.finishPacket();
|
||||
assertEquals(transports, t.getTransports());
|
||||
assertTrue(t.getTimestamp() == timestamp);
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@ package net.sf.briar;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
|
||||
public class TestUtils {
|
||||
@@ -60,4 +62,14 @@ public class TestUtils {
|
||||
random.nextBytes(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
public static void readFully(InputStream in, byte[] b) throws IOException {
|
||||
int offset = 0;
|
||||
while(offset < b.length) {
|
||||
int read = in.read(b, offset, b.length - offset);
|
||||
if(read == -1) break;
|
||||
offset += read;
|
||||
}
|
||||
TestCase.assertEquals(b.length, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,31 +87,28 @@ public class CounterModeTest extends TestCase {
|
||||
@Test
|
||||
public void testLeastSignificantBitsUsedAsCounter()
|
||||
throws GeneralSecurityException {
|
||||
// Initialise the least significant 32 bits of the IV to zero and
|
||||
// Initialise the least significant 16 bits of the IV to zero and
|
||||
// encrypt ten blocks of zeroes
|
||||
byte[] plaintext = new byte[BLOCK_SIZE_BYTES * 10];
|
||||
byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
|
||||
random.nextBytes(ivBytes);
|
||||
for(int i = BLOCK_SIZE_BYTES - 4; i < BLOCK_SIZE_BYTES; i++) {
|
||||
ivBytes[i] = 0;
|
||||
}
|
||||
ivBytes[BLOCK_SIZE_BYTES - 2] = 0;
|
||||
ivBytes[BLOCK_SIZE_BYTES - 1] = 0;
|
||||
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||
byte[] ciphertext =
|
||||
new byte[cipher.getOutputSize(plaintext.length)];
|
||||
byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)];
|
||||
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0);
|
||||
// Initialise the least significant 32 bits of the IV to one and
|
||||
// Make sure the IV array hasn't been modified
|
||||
assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 2]);
|
||||
assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 1]);
|
||||
// Initialise the least significant 16 bits of the IV to one and
|
||||
// encrypt another ten blocks of zeroes
|
||||
for(int i = BLOCK_SIZE_BYTES - 4; i < BLOCK_SIZE_BYTES; i++) {
|
||||
assertEquals(0, ivBytes[i]);
|
||||
}
|
||||
ivBytes[BLOCK_SIZE_BYTES - 1] = 1;
|
||||
iv = new IvParameterSpec(ivBytes);
|
||||
cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||
byte[] ciphertext1 =
|
||||
new byte[cipher.getOutputSize(plaintext.length)];
|
||||
byte[] ciphertext1 = new byte[cipher.getOutputSize(plaintext.length)];
|
||||
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext1, 0);
|
||||
// The last nine blocks of the first ciphertext should be identical to
|
||||
// the first nine blocks of the second ciphertext
|
||||
@@ -121,38 +118,34 @@ public class CounterModeTest extends TestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCounterUsesMoreThan32Bits()
|
||||
public void testCounterUsesMoreThan16Bits()
|
||||
throws GeneralSecurityException {
|
||||
// Initialise the least significant bits of the IV to 2^32-1 and
|
||||
// Initialise the least significant bits of the IV to 2^16-1 and
|
||||
// encrypt ten blocks of zeroes
|
||||
byte[] plaintext = new byte[BLOCK_SIZE_BYTES * 10];
|
||||
byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
|
||||
random.nextBytes(ivBytes);
|
||||
ivBytes[BLOCK_SIZE_BYTES - 5] = 0;
|
||||
for(int i = BLOCK_SIZE_BYTES - 4; i < BLOCK_SIZE_BYTES; i++) {
|
||||
ivBytes[i] = (byte) 255;
|
||||
}
|
||||
ivBytes[BLOCK_SIZE_BYTES - 3] = 0;
|
||||
ivBytes[BLOCK_SIZE_BYTES - 2] = (byte) 255;
|
||||
ivBytes[BLOCK_SIZE_BYTES - 1] = (byte) 255;
|
||||
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||
byte[] ciphertext =
|
||||
new byte[cipher.getOutputSize(plaintext.length)];
|
||||
byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)];
|
||||
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0);
|
||||
// Initialise the least significant bits of the IV to 2^32 and
|
||||
// Make sure the IV array hasn't been modified
|
||||
assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 3]);
|
||||
assertEquals((byte) 255, ivBytes[BLOCK_SIZE_BYTES - 2]);
|
||||
assertEquals((byte) 255, ivBytes[BLOCK_SIZE_BYTES - 1]);
|
||||
// Initialise the least significant bits of the IV to 2^16 and
|
||||
// encrypt another ten blocks of zeroes
|
||||
assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 5]);
|
||||
for(int i = BLOCK_SIZE_BYTES - 4; i < BLOCK_SIZE_BYTES; i++) {
|
||||
assertEquals((byte) 255, ivBytes[i]);
|
||||
}
|
||||
ivBytes[BLOCK_SIZE_BYTES - 5] = 1;
|
||||
for(int i = BLOCK_SIZE_BYTES - 4; i < BLOCK_SIZE_BYTES; i++) {
|
||||
ivBytes[i] = 0;
|
||||
}
|
||||
ivBytes[BLOCK_SIZE_BYTES - 3] = 1;
|
||||
ivBytes[BLOCK_SIZE_BYTES - 2] = 0;
|
||||
ivBytes[BLOCK_SIZE_BYTES - 1] = 0;
|
||||
iv = new IvParameterSpec(ivBytes);
|
||||
cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||
byte[] ciphertext1 =
|
||||
new byte[cipher.getOutputSize(plaintext.length)];
|
||||
byte[] ciphertext1 = new byte[cipher.getOutputSize(plaintext.length)];
|
||||
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext1, 0);
|
||||
// The last nine blocks of the first ciphertext should be identical to
|
||||
// the first nine blocks of the second ciphertext
|
||||
|
||||
@@ -27,22 +27,22 @@ public class CryptoComponentTest extends TestCase {
|
||||
// Check that Alice's incoming keys match Bob's outgoing keys
|
||||
assertEquals(crypto.deriveIncomingMacKey(aliceSecret),
|
||||
crypto.deriveOutgoingMacKey(bobSecret));
|
||||
assertEquals(crypto.deriveIncomingPacketKey(aliceSecret),
|
||||
crypto.deriveOutgoingPacketKey(bobSecret));
|
||||
assertEquals(crypto.deriveIncomingFrameKey(aliceSecret),
|
||||
crypto.deriveOutgoingFrameKey(bobSecret));
|
||||
assertEquals(crypto.deriveIncomingTagKey(aliceSecret),
|
||||
crypto.deriveOutgoingTagKey(bobSecret));
|
||||
// Check that Alice's outgoing keys match Bob's incoming keys
|
||||
assertEquals(crypto.deriveOutgoingMacKey(aliceSecret),
|
||||
crypto.deriveIncomingMacKey(bobSecret));
|
||||
assertEquals(crypto.deriveOutgoingPacketKey(aliceSecret),
|
||||
crypto.deriveIncomingPacketKey(bobSecret));
|
||||
assertEquals(crypto.deriveOutgoingFrameKey(aliceSecret),
|
||||
crypto.deriveIncomingFrameKey(bobSecret));
|
||||
assertEquals(crypto.deriveOutgoingTagKey(aliceSecret),
|
||||
crypto.deriveIncomingTagKey(bobSecret));
|
||||
// Check that Alice's incoming and outgoing keys are different
|
||||
assertFalse(crypto.deriveIncomingMacKey(aliceSecret).equals(
|
||||
crypto.deriveOutgoingMacKey(aliceSecret)));
|
||||
assertFalse(crypto.deriveIncomingPacketKey(aliceSecret).equals(
|
||||
crypto.deriveOutgoingPacketKey(aliceSecret)));
|
||||
assertFalse(crypto.deriveIncomingFrameKey(aliceSecret).equals(
|
||||
crypto.deriveOutgoingFrameKey(aliceSecret)));
|
||||
assertFalse(crypto.deriveIncomingTagKey(aliceSecret).equals(
|
||||
crypto.deriveOutgoingTagKey(aliceSecret)));
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ import java.util.Collections;
|
||||
import java.util.Random;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.protocol.Ack;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.ProtocolConstants;
|
||||
import net.sf.briar.api.protocol.Tags;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
import net.sf.briar.api.serial.FormatException;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
|
||||
@@ -7,13 +7,13 @@ 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.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.Tags;
|
||||
import net.sf.briar.api.serial.FormatException;
|
||||
import net.sf.briar.api.serial.ObjectReader;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
|
||||
@@ -7,8 +7,8 @@ import java.util.Arrays;
|
||||
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.serial.FormatException;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
@@ -5,12 +5,12 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.util.BitSet;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.protocol.OfferId;
|
||||
import net.sf.briar.api.protocol.ProtocolConstants;
|
||||
import net.sf.briar.api.protocol.Request;
|
||||
import net.sf.briar.api.protocol.Tags;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
import net.sf.briar.api.serial.FormatException;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
|
||||
@@ -11,8 +11,8 @@ import java.util.Map.Entry;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.Bytes;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.serial.Consumer;
|
||||
import net.sf.briar.api.serial.FormatException;
|
||||
import net.sf.briar.api.serial.ObjectReader;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.util.StringUtils;
|
||||
|
||||
100
test/net/sf/briar/transport/ConnectionDecrypterImplTest.java
Normal file
100
test/net/sf/briar/transport/ConnectionDecrypterImplTest.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
|
||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class ConnectionDecrypterImplTest extends TestCase {
|
||||
|
||||
private static final int MAC_LENGTH = 32;
|
||||
|
||||
private final Cipher frameCipher;
|
||||
private final SecretKey frameKey;
|
||||
private final int transportId = 1234;
|
||||
private final long connection = 12345L;
|
||||
|
||||
public ConnectionDecrypterImplTest() {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||
frameCipher = crypto.getFrameCipher();
|
||||
frameKey = crypto.generateSecretKey();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleByteFrame() throws Exception {
|
||||
// Create a fake ciphertext frame: one byte plus a MAC
|
||||
byte[] ciphertext = new byte[1 + MAC_LENGTH];
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
|
||||
// Check that one byte plus a MAC can be read
|
||||
ConnectionDecrypter d = new ConnectionDecrypterImpl(in, transportId,
|
||||
connection, frameCipher, frameKey);
|
||||
assertFalse(d.getInputStream().read() == -1);
|
||||
d.readMac(new byte[MAC_LENGTH]);
|
||||
assertTrue(d.getInputStream().read() == -1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecryption() throws Exception {
|
||||
// Calculate the expected plaintext for the first frame
|
||||
byte[] ciphertext = new byte[123];
|
||||
byte[] ivBytes = new byte[TAG_LENGTH];
|
||||
TagEncoder.encodeTag(ivBytes, transportId, connection, 0L);
|
||||
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
||||
frameCipher.init(Cipher.DECRYPT_MODE, frameKey, iv);
|
||||
byte[] plaintext = frameCipher.doFinal(ciphertext);
|
||||
// Calculate the expected plaintext for the second frame
|
||||
byte[] ciphertext1 = new byte[1234];
|
||||
TagEncoder.encodeTag(ivBytes, transportId, connection, 1L);
|
||||
iv = new IvParameterSpec(ivBytes);
|
||||
frameCipher.init(Cipher.DECRYPT_MODE, frameKey, iv);
|
||||
byte[] plaintext1 = frameCipher.doFinal(ciphertext1);
|
||||
assertEquals(ciphertext1.length, plaintext1.length);
|
||||
// Concatenate the ciphertexts
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(ciphertext);
|
||||
out.write(ciphertext1);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
// Use a ConnectionDecrypter to decrypt the ciphertext
|
||||
ConnectionDecrypter d = new ConnectionDecrypterImpl(in, transportId,
|
||||
connection, frameCipher, frameKey);
|
||||
// First frame
|
||||
byte[] decrypted = new byte[plaintext.length - MAC_LENGTH];
|
||||
TestUtils.readFully(d.getInputStream(), decrypted);
|
||||
byte[] decryptedMac = new byte[MAC_LENGTH];
|
||||
d.readMac(decryptedMac);
|
||||
// Second frame
|
||||
byte[] decrypted1 = new byte[plaintext1.length - MAC_LENGTH];
|
||||
TestUtils.readFully(d.getInputStream(), decrypted1);
|
||||
byte[] decryptedMac1 = new byte[MAC_LENGTH];
|
||||
d.readMac(decryptedMac1);
|
||||
// Check that the actual plaintext matches the expected plaintext
|
||||
out.reset();
|
||||
out.write(plaintext);
|
||||
out.write(plaintext1);
|
||||
byte[] expected = out.toByteArray();
|
||||
out.reset();
|
||||
out.write(decrypted);
|
||||
out.write(decryptedMac);
|
||||
out.write(decrypted1);
|
||||
out.write(decryptedMac1);
|
||||
byte[] actual = out.toByteArray();
|
||||
assertTrue(Arrays.equals(expected, actual));
|
||||
}
|
||||
}
|
||||
98
test/net/sf/briar/transport/ConnectionEncrypterImplTest.java
Normal file
98
test/net/sf/briar/transport/ConnectionEncrypterImplTest.java
Normal file
@@ -0,0 +1,98 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class ConnectionEncrypterImplTest extends TestCase {
|
||||
|
||||
private static final int MAC_LENGTH = 32;
|
||||
|
||||
private final Cipher tagCipher, frameCipher;
|
||||
private final SecretKey tagKey, frameKey;
|
||||
private final int transportId = 1234;
|
||||
private final long connection = 12345L;
|
||||
|
||||
public ConnectionEncrypterImplTest() {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||
tagCipher = crypto.getTagCipher();
|
||||
frameCipher = crypto.getFrameCipher();
|
||||
tagKey = crypto.generateSecretKey();
|
||||
frameKey = crypto.generateSecretKey();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleByteFrame() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ConnectionEncrypter e = new ConnectionEncrypterImpl(out, transportId,
|
||||
connection, tagCipher, frameCipher, tagKey, frameKey);
|
||||
e.getOutputStream().write((byte) 0);
|
||||
e.writeMac(new byte[MAC_LENGTH]);
|
||||
assertEquals(TAG_LENGTH + 1 + MAC_LENGTH, out.toByteArray().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryption() throws Exception {
|
||||
// Calculate the expected ciphertext for the tag
|
||||
byte[] plaintextTag = TagEncoder.encodeTag(transportId, connection);
|
||||
assertEquals(TAG_LENGTH, plaintextTag.length);
|
||||
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
|
||||
byte[] tag = tagCipher.doFinal(plaintextTag);
|
||||
assertEquals(TAG_LENGTH, tag.length);
|
||||
// Calculate the expected ciphertext for the first frame
|
||||
byte[] plaintext = new byte[123];
|
||||
byte[] plaintextMac = new byte[MAC_LENGTH];
|
||||
byte[] ivBytes = new byte[TAG_LENGTH];
|
||||
TagEncoder.encodeTag(ivBytes, transportId, connection, 0L);
|
||||
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
||||
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, iv);
|
||||
byte[] ciphertext = new byte[plaintext.length + plaintextMac.length];
|
||||
int offset = frameCipher.update(plaintext, 0, plaintext.length,
|
||||
ciphertext);
|
||||
frameCipher.doFinal(plaintextMac, 0, plaintextMac.length, ciphertext,
|
||||
offset);
|
||||
// Calculate the expected ciphertext for the second frame
|
||||
byte[] plaintext1 = new byte[1234];
|
||||
TagEncoder.encodeTag(ivBytes, transportId, connection, 1L);
|
||||
iv = new IvParameterSpec(ivBytes);
|
||||
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, iv);
|
||||
byte[] ciphertext1 = new byte[plaintext1.length + plaintextMac.length];
|
||||
offset = frameCipher.update(plaintext1, 0, plaintext1.length,
|
||||
ciphertext1);
|
||||
frameCipher.doFinal(plaintextMac, 0, plaintextMac.length, ciphertext1,
|
||||
offset);
|
||||
// Concatenate the ciphertexts
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(tag);
|
||||
out.write(ciphertext);
|
||||
out.write(ciphertext1);
|
||||
byte[] expected = out.toByteArray();
|
||||
// Use a ConnectionEncrypter to encrypt the plaintext
|
||||
out.reset();
|
||||
ConnectionEncrypter e = new ConnectionEncrypterImpl(out, transportId,
|
||||
connection, tagCipher, frameCipher, tagKey, frameKey);
|
||||
e.getOutputStream().write(plaintext);
|
||||
e.writeMac(plaintextMac);
|
||||
e.getOutputStream().write(plaintext1);
|
||||
e.writeMac(plaintextMac);
|
||||
byte[] actual = out.toByteArray();
|
||||
// Check that the actual ciphertext matches the expected ciphertext
|
||||
assertTrue(Arrays.equals(expected, actual));
|
||||
}
|
||||
}
|
||||
106
test/net/sf/briar/transport/ConnectionReaderImplTest.java
Normal file
106
test/net/sf/briar/transport/ConnectionReaderImplTest.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class ConnectionReaderImplTest extends TestCase {
|
||||
|
||||
private final Mac mac;
|
||||
|
||||
public ConnectionReaderImplTest() throws Exception {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||
mac = crypto.getMac();
|
||||
mac.init(crypto.generateSecretKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleByteFrame() throws Exception {
|
||||
// Six bytes for the header, one for the payload
|
||||
byte[] frame = new byte[6 + 1 + mac.getMacLength()];
|
||||
ByteUtils.writeUint16(1, frame, 4); // Payload length = 1
|
||||
// Calculate the MAC
|
||||
mac.update(frame, 0, 6 + 1);
|
||||
mac.doFinal(frame, 6 + 1);
|
||||
// Read the frame
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(frame);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in);
|
||||
ConnectionReader r = new ConnectionReaderImpl(d, mac);
|
||||
// There should be one byte available before EOF
|
||||
assertEquals(0, r.getInputStream().read());
|
||||
assertEquals(-1, r.getInputStream().read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleFrames() throws Exception {
|
||||
// First frame: 123-byte payload
|
||||
byte[] frame = new byte[6 + 123 + mac.getMacLength()];
|
||||
ByteUtils.writeUint16(123, frame, 4);
|
||||
mac.update(frame, 0, 6 + 123);
|
||||
mac.doFinal(frame, 6 + 123);
|
||||
// Second frame: 1234-byte payload
|
||||
byte[] frame1 = new byte[6 + 1234 + mac.getMacLength()];
|
||||
ByteUtils.writeUint32(1, frame1, 0);
|
||||
ByteUtils.writeUint16(1234, frame1, 4);
|
||||
mac.update(frame1, 0, 6 + 1234);
|
||||
mac.doFinal(frame1, 6 + 1234);
|
||||
// Concatenate the frames
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(frame);
|
||||
out.write(frame1);
|
||||
// Read the frames
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in);
|
||||
ConnectionReader r = new ConnectionReaderImpl(d, mac);
|
||||
byte[] read = new byte[123];
|
||||
TestUtils.readFully(r.getInputStream(), read);
|
||||
assertTrue(Arrays.equals(new byte[123], read));
|
||||
byte[] read1 = new byte[1234];
|
||||
TestUtils.readFully(r.getInputStream(), read1);
|
||||
assertTrue(Arrays.equals(new byte[1234], read1));
|
||||
}
|
||||
|
||||
/** A ConnectionDecrypter that performs no decryption. */
|
||||
private static class NullConnectionDecrypter
|
||||
implements ConnectionDecrypter {
|
||||
|
||||
private final InputStream in;
|
||||
|
||||
private NullConnectionDecrypter(InputStream in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return in;
|
||||
}
|
||||
|
||||
public void readMac(byte[] mac) throws IOException {
|
||||
int offset = 0;
|
||||
while(offset < mac.length) {
|
||||
int read = in.read(mac, offset, mac.length - offset);
|
||||
if(read == -1) break;
|
||||
offset += read;
|
||||
}
|
||||
if(offset < mac.length) throw new EOFException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ public class ConnectionRecogniserImplTest extends TestCase {
|
||||
SecretKey tagKey = crypto.deriveIncomingTagKey(secret);
|
||||
Cipher tagCipher = crypto.getTagCipher();
|
||||
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
|
||||
byte[] tag = TagEncoder.encodeTag(transportId, 3L, 0);
|
||||
byte[] tag = TagEncoder.encodeTag(transportId, 3L);
|
||||
byte[] encryptedTag = tagCipher.doFinal(tag);
|
||||
|
||||
Mockery context = new Mockery();
|
||||
|
||||
98
test/net/sf/briar/transport/ConnectionWriterImplTest.java
Normal file
98
test/net/sf/briar/transport/ConnectionWriterImplTest.java
Normal file
@@ -0,0 +1,98 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.transport.ConnectionWriter;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class ConnectionWriterImplTest extends TestCase {
|
||||
|
||||
private final Mac mac;
|
||||
|
||||
public ConnectionWriterImplTest() throws Exception {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||
mac = crypto.getMac();
|
||||
mac.init(crypto.generateSecretKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleByteFrame() throws Exception {
|
||||
// Six bytes for the header, one for the payload
|
||||
byte[] frame = new byte[6 + 1 + mac.getMacLength()];
|
||||
ByteUtils.writeUint16(1, frame, 4); // Payload length = 1
|
||||
// Calculate the MAC
|
||||
mac.update(frame, 0, 6 + 1);
|
||||
mac.doFinal(frame, 6 + 1);
|
||||
// Check that the ConnectionWriter gets the same results
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ConnectionEncrypter e = new NullConnectionEncrypter(out);
|
||||
ConnectionWriter w = new ConnectionWriterImpl(e, mac);
|
||||
w.getOutputStream().write(0);
|
||||
w.getOutputStream().flush();
|
||||
assertTrue(Arrays.equals(frame, out.toByteArray()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleFrames() throws Exception {
|
||||
// First frame: 123-byte payload
|
||||
byte[] frame = new byte[6 + 123 + mac.getMacLength()];
|
||||
ByteUtils.writeUint16(123, frame, 4);
|
||||
mac.update(frame, 0, 6 + 123);
|
||||
mac.doFinal(frame, 6 + 123);
|
||||
// Second frame: 1234-byte payload
|
||||
byte[] frame1 = new byte[6 + 1234 + mac.getMacLength()];
|
||||
ByteUtils.writeUint32(1, frame1, 0);
|
||||
ByteUtils.writeUint16(1234, frame1, 4);
|
||||
mac.update(frame1, 0, 6 + 1234);
|
||||
mac.doFinal(frame1, 6 + 1234);
|
||||
// Concatenate the frames
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(frame);
|
||||
out.write(frame1);
|
||||
byte[] expected = out.toByteArray();
|
||||
// Check that the ConnectionWriter gets the same results
|
||||
out.reset();
|
||||
ConnectionEncrypter e = new NullConnectionEncrypter(out);
|
||||
ConnectionWriter w = new ConnectionWriterImpl(e, mac);
|
||||
w.getOutputStream().write(new byte[123]);
|
||||
w.getOutputStream().flush();
|
||||
w.getOutputStream().write(new byte[1234]);
|
||||
w.getOutputStream().flush();
|
||||
byte[] actual = out.toByteArray();
|
||||
assertTrue(Arrays.equals(expected, actual));
|
||||
}
|
||||
|
||||
/** A ConnectionEncrypter that performs no encryption. */
|
||||
private static class NullConnectionEncrypter
|
||||
implements ConnectionEncrypter {
|
||||
|
||||
private final OutputStream out;
|
||||
|
||||
private NullConnectionEncrypter(OutputStream out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return out;
|
||||
}
|
||||
|
||||
public void writeMac(byte[] mac) throws IOException {
|
||||
out.write(mac);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ import javax.crypto.SecretKey;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.transport.PacketReader;
|
||||
import net.sf.briar.api.transport.PacketWriter;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
import net.sf.briar.api.transport.ConnectionWriter;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -24,60 +24,66 @@ import org.junit.Test;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class PacketReadWriteTest extends TestCase {
|
||||
public class FrameReadWriteTest extends TestCase {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final Cipher tagCipher, packetCipher;
|
||||
private final SecretKey macKey, tagKey, packetKey;
|
||||
private final Cipher tagCipher, frameCipher;
|
||||
private final SecretKey macKey, tagKey, frameKey;
|
||||
private final Mac mac;
|
||||
private final Random random;
|
||||
private final byte[] secret = new byte[100];
|
||||
private final int transportId = 999;
|
||||
private final long connection = 1234L;
|
||||
|
||||
public PacketReadWriteTest() {
|
||||
public FrameReadWriteTest() {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
crypto = i.getInstance(CryptoComponent.class);
|
||||
tagCipher = crypto.getTagCipher();
|
||||
packetCipher = crypto.getPacketCipher();
|
||||
frameCipher = crypto.getFrameCipher();
|
||||
// Since we're sending packets to ourselves, we only need outgoing keys
|
||||
macKey = crypto.deriveOutgoingMacKey(secret);
|
||||
tagKey = crypto.deriveOutgoingTagKey(secret);
|
||||
packetKey = crypto.deriveOutgoingPacketKey(secret);
|
||||
frameKey = crypto.deriveOutgoingFrameKey(secret);
|
||||
mac = crypto.getMac();
|
||||
random = new Random();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteAndRead() throws Exception {
|
||||
// Generate two random packets
|
||||
byte[] packet = new byte[12345];
|
||||
random.nextBytes(packet);
|
||||
byte[] packet1 = new byte[321];
|
||||
random.nextBytes(packet1);
|
||||
// Write the packets
|
||||
// Calculate the expected ciphertext for the tag
|
||||
byte[] plaintextTag = TagEncoder.encodeTag(transportId, connection);
|
||||
assertEquals(TAG_LENGTH, plaintextTag.length);
|
||||
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
|
||||
byte[] tag = tagCipher.doFinal(plaintextTag);
|
||||
assertEquals(TAG_LENGTH, tag.length);
|
||||
// Generate two random frames
|
||||
byte[] frame = new byte[12345];
|
||||
random.nextBytes(frame);
|
||||
byte[] frame1 = new byte[321];
|
||||
random.nextBytes(frame1);
|
||||
// Write the frames
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PacketEncrypter encrypter = new PacketEncrypterImpl(out, tagCipher,
|
||||
packetCipher, tagKey, packetKey);
|
||||
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
|
||||
transportId, connection, tagCipher, frameCipher, tagKey,
|
||||
frameKey);
|
||||
mac.init(macKey);
|
||||
PacketWriter writer = new PacketWriterImpl(encrypter, mac, transportId,
|
||||
connection);
|
||||
ConnectionWriter writer = new ConnectionWriterImpl(encrypter, mac);
|
||||
OutputStream out1 = writer.getOutputStream();
|
||||
out1.write(packet);
|
||||
writer.finishPacket();
|
||||
out1.write(packet1);
|
||||
writer.finishPacket();
|
||||
// Read the packets back
|
||||
out1.write(frame);
|
||||
out1.flush();
|
||||
out1.write(frame1);
|
||||
out1.flush();
|
||||
// Read the frames back
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
byte[] firstTag = new byte[TAG_LENGTH];
|
||||
assertEquals(TAG_LENGTH, in.read(firstTag));
|
||||
PacketDecrypter decrypter = new PacketDecrypterImpl(firstTag, in,
|
||||
tagCipher, packetCipher, tagKey, packetKey);
|
||||
PacketReader reader = new PacketReaderImpl(decrypter, mac, transportId,
|
||||
connection);
|
||||
byte[] recoveredTag = new byte[TAG_LENGTH];
|
||||
assertEquals(TAG_LENGTH, in.read(recoveredTag));
|
||||
assertTrue(Arrays.equals(tag, recoveredTag));
|
||||
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in,
|
||||
transportId, connection, frameCipher, frameKey);
|
||||
ConnectionReader reader = new ConnectionReaderImpl(decrypter, mac);
|
||||
InputStream in1 = reader.getInputStream();
|
||||
byte[] recovered = new byte[packet.length];
|
||||
byte[] recovered = new byte[frame.length];
|
||||
int offset = 0;
|
||||
while(offset < recovered.length) {
|
||||
int read = in1.read(recovered, offset, recovered.length - offset);
|
||||
@@ -85,9 +91,8 @@ public class PacketReadWriteTest extends TestCase {
|
||||
offset += read;
|
||||
}
|
||||
assertEquals(recovered.length, offset);
|
||||
reader.finishPacket();
|
||||
assertTrue(Arrays.equals(packet, recovered));
|
||||
byte[] recovered1 = new byte[packet1.length];
|
||||
assertTrue(Arrays.equals(frame, recovered));
|
||||
byte[] recovered1 = new byte[frame1.length];
|
||||
offset = 0;
|
||||
while(offset < recovered1.length) {
|
||||
int read = in1.read(recovered1, offset, recovered1.length - offset);
|
||||
@@ -95,7 +100,6 @@ public class PacketReadWriteTest extends TestCase {
|
||||
offset += read;
|
||||
}
|
||||
assertEquals(recovered1.length, offset);
|
||||
reader.finishPacket();
|
||||
assertTrue(Arrays.equals(packet1, recovered1));
|
||||
assertTrue(Arrays.equals(frame1, recovered1));
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class PacketDecrypterImplTest extends TestCase {
|
||||
|
||||
private final Cipher tagCipher, packetCipher;
|
||||
private final SecretKey tagKey, packetKey;
|
||||
|
||||
public PacketDecrypterImplTest() {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||
tagCipher = crypto.getTagCipher();
|
||||
packetCipher = crypto.getPacketCipher();
|
||||
tagKey = crypto.generateSecretKey();
|
||||
packetKey = crypto.generateSecretKey();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleBytePackets() throws Exception {
|
||||
byte[] ciphertext = new byte[(TAG_LENGTH + 1) * 2];
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
|
||||
byte[] firstTag = new byte[TAG_LENGTH];
|
||||
assertEquals(TAG_LENGTH, in.read(firstTag));
|
||||
PacketDecrypter p = new PacketDecrypterImpl(firstTag, in, tagCipher,
|
||||
packetCipher, tagKey, packetKey);
|
||||
byte[] decryptedTag = p.readTag();
|
||||
assertEquals(TAG_LENGTH, decryptedTag.length);
|
||||
assertTrue(p.getInputStream().read() > -1);
|
||||
byte[] decryptedTag1 = p.readTag();
|
||||
assertEquals(TAG_LENGTH, decryptedTag1.length);
|
||||
assertTrue(p.getInputStream().read() > -1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecryption() throws Exception {
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
byte[] packet = new byte[123];
|
||||
byte[] tag1 = new byte[TAG_LENGTH];
|
||||
byte[] packet1 = new byte[234];
|
||||
// Calculate the first expected decrypted tag
|
||||
tagCipher.init(Cipher.DECRYPT_MODE, tagKey);
|
||||
byte[] expectedTag = tagCipher.doFinal(tag);
|
||||
assertEquals(tag.length, expectedTag.length);
|
||||
// Calculate the first expected decrypted packet
|
||||
IvParameterSpec iv = new IvParameterSpec(expectedTag);
|
||||
packetCipher.init(Cipher.DECRYPT_MODE, packetKey, iv);
|
||||
byte[] expectedPacket = packetCipher.doFinal(packet);
|
||||
assertEquals(packet.length, expectedPacket.length);
|
||||
// Calculate the second expected decrypted tag
|
||||
tagCipher.init(Cipher.DECRYPT_MODE, tagKey);
|
||||
byte[] expectedTag1 = tagCipher.doFinal(tag1);
|
||||
assertEquals(tag1.length, expectedTag1.length);
|
||||
// Calculate the second expected decrypted packet
|
||||
IvParameterSpec iv1 = new IvParameterSpec(expectedTag1);
|
||||
packetCipher.init(Cipher.DECRYPT_MODE, packetKey, iv1);
|
||||
byte[] expectedPacket1 = packetCipher.doFinal(packet1);
|
||||
assertEquals(packet1.length, expectedPacket1.length);
|
||||
// Check that the PacketDecrypter gets the same results
|
||||
byte[] ciphertext = new byte[tag.length + packet.length
|
||||
+ tag1.length + packet1.length];
|
||||
System.arraycopy(tag, 0, ciphertext, 0, tag.length);
|
||||
System.arraycopy(packet, 0, ciphertext, tag.length, packet.length);
|
||||
System.arraycopy(tag1, 0, ciphertext, tag.length + packet.length,
|
||||
tag1.length);
|
||||
System.arraycopy(packet1, 0, ciphertext,
|
||||
tag.length + packet.length + tag1.length, packet1.length);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
|
||||
PacketDecrypter p = new PacketDecrypterImpl(tag, in, tagCipher,
|
||||
packetCipher, tagKey, packetKey);
|
||||
// First tag
|
||||
assertTrue(Arrays.equals(expectedTag, p.readTag()));
|
||||
// First packet
|
||||
byte[] actualPacket = new byte[packet.length];
|
||||
int offset = 0;
|
||||
while(offset < actualPacket.length) {
|
||||
int read = p.getInputStream().read(actualPacket, offset,
|
||||
actualPacket.length - offset);
|
||||
if(read == -1) break;
|
||||
offset += read;
|
||||
}
|
||||
assertEquals(actualPacket.length, offset);
|
||||
assertTrue(Arrays.equals(expectedPacket, actualPacket));
|
||||
// Second tag
|
||||
assertTrue(Arrays.equals(expectedTag1, p.readTag()));
|
||||
// Second packet
|
||||
byte[] actualPacket1 = new byte[packet1.length];
|
||||
offset = 0;
|
||||
while(offset < actualPacket1.length) {
|
||||
int read = p.getInputStream().read(actualPacket1, offset,
|
||||
actualPacket1.length - offset);
|
||||
if(read == -1) break;
|
||||
offset += read;
|
||||
}
|
||||
assertEquals(actualPacket1.length, offset);
|
||||
assertTrue(Arrays.equals(expectedPacket1, actualPacket1));
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class PacketEncrypterImplTest extends TestCase {
|
||||
|
||||
private final Cipher tagCipher, packetCipher;
|
||||
private final SecretKey tagKey, packetKey;
|
||||
|
||||
public PacketEncrypterImplTest() {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||
tagCipher = crypto.getTagCipher();
|
||||
packetCipher = crypto.getPacketCipher();
|
||||
tagKey = crypto.generateSecretKey();
|
||||
packetKey = crypto.generateSecretKey();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleBytePacket() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PacketEncrypter p = new PacketEncrypterImpl(out, tagCipher,
|
||||
packetCipher, tagKey, packetKey);
|
||||
p.writeTag(new byte[TAG_LENGTH]);
|
||||
p.getOutputStream().write((byte) 0);
|
||||
p.finishPacket();
|
||||
assertEquals(TAG_LENGTH + 1, out.toByteArray().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryption() throws Exception {
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
byte[] packet = new byte[123];
|
||||
// Calculate the expected encrypted tag
|
||||
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
|
||||
byte[] expectedTag = tagCipher.doFinal(tag);
|
||||
assertEquals(tag.length, expectedTag.length);
|
||||
// Calculate the expected encrypted packet
|
||||
IvParameterSpec iv = new IvParameterSpec(tag);
|
||||
packetCipher.init(Cipher.ENCRYPT_MODE, packetKey, iv);
|
||||
byte[] expectedPacket = packetCipher.doFinal(packet);
|
||||
assertEquals(packet.length, expectedPacket.length);
|
||||
// Check that the PacketEncrypter gets the same results
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PacketEncrypter p = new PacketEncrypterImpl(out, tagCipher,
|
||||
packetCipher, tagKey, packetKey);
|
||||
p.writeTag(tag);
|
||||
p.getOutputStream().write(packet);
|
||||
p.finishPacket();
|
||||
byte[] ciphertext = out.toByteArray();
|
||||
assertEquals(TAG_LENGTH + packet.length, ciphertext.length);
|
||||
// Check the tag
|
||||
byte[] actualTag = new byte[TAG_LENGTH];
|
||||
System.arraycopy(ciphertext, 0, actualTag, 0, TAG_LENGTH);
|
||||
assertTrue(Arrays.equals(expectedTag, actualTag));
|
||||
// Check the packet
|
||||
byte[] actualPacket = new byte[packet.length];
|
||||
System.arraycopy(ciphertext, TAG_LENGTH, actualPacket, 0,
|
||||
actualPacket.length);
|
||||
assertTrue(Arrays.equals(expectedPacket, actualPacket));
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.transport.PacketReader;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.util.StringUtils;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class PacketReaderImplTest extends TestCase {
|
||||
|
||||
private final Mac mac;
|
||||
|
||||
public PacketReaderImplTest() throws Exception {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||
mac = crypto.getMac();
|
||||
mac.init(crypto.generateSecretKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFirstReadTriggersTag() throws Exception {
|
||||
// TAG_BYTES for the tag, 1 byte for the packet
|
||||
byte[] b = new byte[TAG_LENGTH + 1];
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
PacketDecrypter d = new NullPacketDecrypter(in);
|
||||
PacketReader p = new PacketReaderImpl(d, mac, 0, 0L);
|
||||
// There should be one byte available before EOF
|
||||
assertEquals(0, p.getInputStream().read());
|
||||
assertEquals(-1, p.getInputStream().read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFinishPacketAfterReadTriggersMac() throws Exception {
|
||||
// TAG_BYTES for the tag, 1 byte for the packet
|
||||
byte[] b = new byte[TAG_LENGTH + 1];
|
||||
// Calculate the MAC and append it to the packet
|
||||
mac.update(b);
|
||||
byte[] macBytes = mac.doFinal();
|
||||
byte[] b1 = Arrays.copyOf(b, b.length + macBytes.length);
|
||||
System.arraycopy(macBytes, 0, b1, b.length, macBytes.length);
|
||||
// Check that the PacketReader reads and verifies the MAC
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b1);
|
||||
PacketDecrypter d = new NullPacketDecrypter(in);
|
||||
PacketReader p = new PacketReaderImpl(d, mac, 0, 0L);
|
||||
assertEquals(0, p.getInputStream().read());
|
||||
p.finishPacket();
|
||||
// Reading the MAC should take us to EOF
|
||||
assertEquals(-1, p.getInputStream().read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModifyingPacketInvalidatesMac() throws Exception {
|
||||
// TAG_BYTES for the tag, 1 byte for the packet
|
||||
byte[] b = new byte[TAG_LENGTH + 1];
|
||||
// Calculate the MAC and append it to the packet
|
||||
mac.update(b);
|
||||
byte[] macBytes = mac.doFinal();
|
||||
byte[] b1 = Arrays.copyOf(b, b.length + macBytes.length);
|
||||
System.arraycopy(macBytes, 0, b1, b.length, macBytes.length);
|
||||
// Modify the packet
|
||||
b1[TAG_LENGTH] = (byte) 1;
|
||||
// Check that the PacketReader reads and fails to verify the MAC
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b1);
|
||||
PacketDecrypter d = new NullPacketDecrypter(in);
|
||||
PacketReader p = new PacketReaderImpl(d, mac, 0, 0L);
|
||||
assertEquals(1, p.getInputStream().read());
|
||||
try {
|
||||
p.finishPacket();
|
||||
fail();
|
||||
} catch(GeneralSecurityException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtraCallsToFinishPacketDoNothing() throws Exception {
|
||||
// TAG_BYTES for the tag, 1 byte for the packet
|
||||
byte[] b = new byte[TAG_LENGTH + 1];
|
||||
// Calculate the MAC and append it to the packet
|
||||
mac.update(b);
|
||||
byte[] macBytes = mac.doFinal();
|
||||
byte[] b1 = Arrays.copyOf(b, b.length + macBytes.length);
|
||||
System.arraycopy(macBytes, 0, b1, b.length, macBytes.length);
|
||||
// Check that the PacketReader reads and verifies the MAC
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b1);
|
||||
PacketDecrypter d = new NullPacketDecrypter(in);
|
||||
PacketReader p = new PacketReaderImpl(d, mac, 0, 0L);
|
||||
// Initial calls to finishPacket() should have no effect
|
||||
p.finishPacket();
|
||||
p.finishPacket();
|
||||
p.finishPacket();
|
||||
assertEquals(0, p.getInputStream().read());
|
||||
p.finishPacket();
|
||||
// Extra calls to finishPacket() should have no effect
|
||||
p.finishPacket();
|
||||
p.finishPacket();
|
||||
p.finishPacket();
|
||||
// Reading the MAC should take us to EOF
|
||||
assertEquals(-1, p.getInputStream().read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPacketNumberIsIncremented() throws Exception {
|
||||
byte[] tag = StringUtils.fromHexString(
|
||||
"0000" // 16 bits reserved
|
||||
+ "F00D" // 16 bits for the transport ID
|
||||
+ "DEADBEEF" // 32 bits for the connection number
|
||||
+ "00000000" // 32 bits for the packet number
|
||||
+ "00000000" // 32 bits for the block number
|
||||
);
|
||||
assertEquals(TAG_LENGTH, tag.length);
|
||||
byte[] tag1 = StringUtils.fromHexString(
|
||||
"0000" // 16 bits reserved
|
||||
+ "F00D" // 16 bits for the transport ID
|
||||
+ "DEADBEEF" // 32 bits for the connection number
|
||||
+ "00000001" // 32 bits for the packet number
|
||||
+ "00000000" // 32 bits for the block number
|
||||
);
|
||||
assertEquals(TAG_LENGTH, tag1.length);
|
||||
// Calculate the MAC on the first packet and append it to the packet
|
||||
mac.update(tag);
|
||||
mac.update((byte) 0);
|
||||
byte[] macBytes = mac.doFinal();
|
||||
byte[] b = Arrays.copyOf(tag, tag.length + 1 + macBytes.length);
|
||||
System.arraycopy(macBytes, 0, b, tag.length + 1, macBytes.length);
|
||||
// Calculate the MAC on the second packet and append it to the packet
|
||||
mac.update(tag1);
|
||||
mac.update((byte) 0);
|
||||
byte[] macBytes1 = mac.doFinal();
|
||||
byte[] b1 = Arrays.copyOf(tag1, tag1.length + 1 + macBytes1.length);
|
||||
System.arraycopy(macBytes1, 0, b1, tag.length + 1, macBytes1.length);
|
||||
// Check that the PacketReader accepts the correct tags and MACs
|
||||
byte[] b2 = Arrays.copyOf(b, b.length + b1.length);
|
||||
System.arraycopy(b1, 0, b2, b.length, b1.length);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b2);
|
||||
PacketDecrypter d = new NullPacketDecrypter(in);
|
||||
PacketReader p = new PacketReaderImpl(d, mac, 0xF00D, 0xDEADBEEFL);
|
||||
// Packet one
|
||||
assertEquals(0, p.getInputStream().read());
|
||||
p.finishPacket();
|
||||
// Packet two
|
||||
assertEquals(0, p.getInputStream().read());
|
||||
p.finishPacket();
|
||||
// We should be at EOF
|
||||
assertEquals(-1, p.getInputStream().read());
|
||||
}
|
||||
|
||||
/** A PacketDecrypter that performs no decryption. */
|
||||
private static class NullPacketDecrypter implements PacketDecrypter {
|
||||
|
||||
private final InputStream in;
|
||||
|
||||
private NullPacketDecrypter(InputStream in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return in;
|
||||
}
|
||||
|
||||
public byte[] readTag() throws IOException {
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
int offset = 0;
|
||||
while(offset < tag.length) {
|
||||
int read = in.read(tag, offset, tag.length - offset);
|
||||
if(read == -1) break;
|
||||
offset += read;
|
||||
}
|
||||
if(offset == 0) return null; // EOF between packets is acceptable
|
||||
if(offset < tag.length) throw new EOFException();
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.transport.PacketWriter;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.util.StringUtils;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class PacketWriterImplTest extends TestCase {
|
||||
|
||||
private final Mac mac;
|
||||
|
||||
public PacketWriterImplTest() throws Exception {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||
mac = crypto.getMac();
|
||||
mac.init(crypto.generateSecretKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFirstWriteTriggersTag() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PacketEncrypter e = new NullPacketEncrypter(out);
|
||||
PacketWriter p = new PacketWriterImpl(e, mac, 0, 0L);
|
||||
p.getOutputStream().write(0);
|
||||
// There should be TAG_BYTES bytes for the tag, 1 byte for the packet
|
||||
assertTrue(Arrays.equals(new byte[TAG_LENGTH + 1],
|
||||
out.toByteArray()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFinishPacketAfterWriteTriggersMac() throws Exception {
|
||||
// Calculate what the MAC should be
|
||||
mac.update(new byte[TAG_LENGTH + 1]);
|
||||
byte[] expectedMac = mac.doFinal();
|
||||
// Check that the PacketWriter calculates and writes the correct MAC
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PacketEncrypter e = new NullPacketEncrypter(out);
|
||||
PacketWriter p = new PacketWriterImpl(e, mac, 0, 0L);
|
||||
p.getOutputStream().write(0);
|
||||
p.finishPacket();
|
||||
byte[] written = out.toByteArray();
|
||||
assertEquals(TAG_LENGTH + 1 + expectedMac.length,
|
||||
written.length);
|
||||
byte[] actualMac = new byte[expectedMac.length];
|
||||
System.arraycopy(written, TAG_LENGTH + 1, actualMac, 0,
|
||||
actualMac.length);
|
||||
assertTrue(Arrays.equals(expectedMac, actualMac));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtraCallsToFinishPacketDoNothing() throws Exception {
|
||||
// Calculate what the MAC should be
|
||||
mac.update(new byte[TAG_LENGTH + 1]);
|
||||
byte[] expectedMac = mac.doFinal();
|
||||
// Check that the PacketWriter calculates and writes the correct MAC
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PacketEncrypter e = new NullPacketEncrypter(out);
|
||||
PacketWriter p = new PacketWriterImpl(e, mac, 0, 0L);
|
||||
// Initial calls to finishPacket() should have no effect
|
||||
p.finishPacket();
|
||||
p.finishPacket();
|
||||
p.finishPacket();
|
||||
p.getOutputStream().write(0);
|
||||
p.finishPacket();
|
||||
// Extra calls to finishPacket() should have no effect
|
||||
p.finishPacket();
|
||||
p.finishPacket();
|
||||
p.finishPacket();
|
||||
byte[] written = out.toByteArray();
|
||||
assertEquals(TAG_LENGTH + 1 + expectedMac.length,
|
||||
written.length);
|
||||
byte[] actualMac = new byte[expectedMac.length];
|
||||
System.arraycopy(written, TAG_LENGTH + 1, actualMac, 0,
|
||||
actualMac.length);
|
||||
assertTrue(Arrays.equals(expectedMac, actualMac));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPacketNumberIsIncremented() throws Exception {
|
||||
byte[] expectedTag = StringUtils.fromHexString(
|
||||
"0000" // 16 bits reserved
|
||||
+ "F00D" // 16 bits for the transport ID
|
||||
+ "DEADBEEF" // 32 bits for the connection number
|
||||
+ "00000000" // 32 bits for the packet number
|
||||
+ "00000000" // 32 bits for the block number
|
||||
);
|
||||
assertEquals(TAG_LENGTH, expectedTag.length);
|
||||
byte[] expectedTag1 = StringUtils.fromHexString(
|
||||
"0000" // 16 bits reserved
|
||||
+ "F00D" // 16 bits for the transport ID
|
||||
+ "DEADBEEF" // 32 bits for the connection number
|
||||
+ "00000001" // 32 bits for the packet number
|
||||
+ "00000000" // 32 bits for the block number
|
||||
);
|
||||
assertEquals(TAG_LENGTH, expectedTag1.length);
|
||||
// Calculate what the MAC on the first packet should be
|
||||
mac.update(expectedTag);
|
||||
mac.update((byte) 0);
|
||||
byte[] expectedMac = mac.doFinal();
|
||||
// Calculate what the MAC on the second packet should be
|
||||
mac.update(expectedTag1);
|
||||
mac.update((byte) 0);
|
||||
byte[] expectedMac1 = mac.doFinal();
|
||||
// Check that the PacketWriter writes the correct tags and MACs
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PacketEncrypter e = new NullPacketEncrypter(out);
|
||||
PacketWriter p = new PacketWriterImpl(e, mac, 0xF00D, 0xDEADBEEFL);
|
||||
// Packet one
|
||||
p.getOutputStream().write(0);
|
||||
p.finishPacket();
|
||||
// Packet two
|
||||
p.getOutputStream().write(0);
|
||||
p.finishPacket();
|
||||
byte[] written = out.toByteArray();
|
||||
assertEquals(TAG_LENGTH + 1 + expectedMac.length
|
||||
+ TAG_LENGTH + 1 + expectedMac1.length,
|
||||
written.length);
|
||||
// Check the first packet's tag
|
||||
byte[] actualTag = new byte[TAG_LENGTH];
|
||||
System.arraycopy(written, 0, actualTag, 0, TAG_LENGTH);
|
||||
assertTrue(Arrays.equals(expectedTag, actualTag));
|
||||
// Check the first packet's MAC
|
||||
byte[] actualMac = new byte[expectedMac.length];
|
||||
System.arraycopy(written, TAG_LENGTH + 1, actualMac, 0,
|
||||
actualMac.length);
|
||||
assertTrue(Arrays.equals(expectedMac, actualMac));
|
||||
// Check the second packet's tag
|
||||
byte[] actualTag1 = new byte[TAG_LENGTH];
|
||||
System.arraycopy(written, TAG_LENGTH + 1 + expectedMac.length,
|
||||
actualTag1, 0, TAG_LENGTH);
|
||||
assertTrue(Arrays.equals(expectedTag1, actualTag1));
|
||||
// Check the second packet's MAC
|
||||
byte[] actualMac1 = new byte[expectedMac1.length];
|
||||
System.arraycopy(written, TAG_LENGTH + 1 + expectedMac.length
|
||||
+ TAG_LENGTH + 1, actualMac1, 0, actualMac1.length);
|
||||
assertTrue(Arrays.equals(expectedMac1, actualMac1));
|
||||
}
|
||||
|
||||
/** A PacketEncrypter that performs no encryption. */
|
||||
private static class NullPacketEncrypter implements PacketEncrypter {
|
||||
|
||||
private final OutputStream out;
|
||||
|
||||
private NullPacketEncrypter(OutputStream out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return out;
|
||||
}
|
||||
|
||||
public void writeTag(byte[] tag) throws IOException {
|
||||
out.write(tag);
|
||||
}
|
||||
|
||||
public void finishPacket() {}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import net.sf.briar.util.StringUtils;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class TagDecoderTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void testReadUint16() {
|
||||
byte[] b = StringUtils.fromHexString("000000");
|
||||
assertEquals(0, TagDecoder.readUint16(b, 1));
|
||||
b = StringUtils.fromHexString("000001");
|
||||
assertEquals(1, TagDecoder.readUint16(b, 1));
|
||||
b = StringUtils.fromHexString("00FFFF");
|
||||
assertEquals(65535, TagDecoder.readUint16(b, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadUint32() {
|
||||
byte[] b = StringUtils.fromHexString("0000000000");
|
||||
assertEquals(0L, TagDecoder.readUint32(b, 1));
|
||||
b = StringUtils.fromHexString("0000000001");
|
||||
assertEquals(1L, TagDecoder.readUint32(b, 1));
|
||||
b = StringUtils.fromHexString("00FFFFFFFF");
|
||||
assertEquals(4294967295L, TagDecoder.readUint32(b, 1));
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import net.sf.briar.util.StringUtils;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class TagEncoderTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void testWriteUint16() {
|
||||
byte[] b = new byte[3];
|
||||
TagEncoder.writeUint16(0, b, 1);
|
||||
assertEquals("000000", StringUtils.toHexString(b));
|
||||
TagEncoder.writeUint16(1, b, 1);
|
||||
assertEquals("000001", StringUtils.toHexString(b));
|
||||
TagEncoder.writeUint16(65535, b, 1);
|
||||
assertEquals("00FFFF", StringUtils.toHexString(b));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteUint32() {
|
||||
byte[] b = new byte[5];
|
||||
TagEncoder.writeUint32(0L, b, 1);
|
||||
assertEquals("0000000000", StringUtils.toHexString(b));
|
||||
TagEncoder.writeUint32(1L, b, 1);
|
||||
assertEquals("0000000001", StringUtils.toHexString(b));
|
||||
TagEncoder.writeUint32(4294967295L, b, 1);
|
||||
assertEquals("00FFFFFFFF", StringUtils.toHexString(b));
|
||||
}
|
||||
}
|
||||
51
test/net/sf/briar/util/ByteUtilsTest.java
Normal file
51
test/net/sf/briar/util/ByteUtilsTest.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package net.sf.briar.util;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ByteUtilsTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void testReadUint16() {
|
||||
byte[] b = StringUtils.fromHexString("000000");
|
||||
assertEquals(0, ByteUtils.readUint16(b, 1));
|
||||
b = StringUtils.fromHexString("000001");
|
||||
assertEquals(1, ByteUtils.readUint16(b, 1));
|
||||
b = StringUtils.fromHexString("00FFFF");
|
||||
assertEquals(65535, ByteUtils.readUint16(b, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadUint32() {
|
||||
byte[] b = StringUtils.fromHexString("0000000000");
|
||||
assertEquals(0L, ByteUtils.readUint32(b, 1));
|
||||
b = StringUtils.fromHexString("0000000001");
|
||||
assertEquals(1L, ByteUtils.readUint32(b, 1));
|
||||
b = StringUtils.fromHexString("00FFFFFFFF");
|
||||
assertEquals(4294967295L, ByteUtils.readUint32(b, 1));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWriteUint16() {
|
||||
byte[] b = new byte[3];
|
||||
ByteUtils.writeUint16(0, b, 1);
|
||||
assertEquals("000000", StringUtils.toHexString(b));
|
||||
ByteUtils.writeUint16(1, b, 1);
|
||||
assertEquals("000001", StringUtils.toHexString(b));
|
||||
ByteUtils.writeUint16(65535, b, 1);
|
||||
assertEquals("00FFFF", StringUtils.toHexString(b));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteUint32() {
|
||||
byte[] b = new byte[5];
|
||||
ByteUtils.writeUint32(0L, b, 1);
|
||||
assertEquals("0000000000", StringUtils.toHexString(b));
|
||||
ByteUtils.writeUint32(1L, b, 1);
|
||||
assertEquals("0000000001", StringUtils.toHexString(b));
|
||||
ByteUtils.writeUint32(4294967295L, b, 1);
|
||||
assertEquals("00FFFFFFFF", StringUtils.toHexString(b));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user