mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 03:39:05 +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:
105
test/net/sf/briar/transport/FrameReadWriteTest.java
Normal file
105
test/net/sf/briar/transport/FrameReadWriteTest.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
import net.sf.briar.api.transport.ConnectionWriter;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class FrameReadWriteTest extends TestCase {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
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 FrameReadWriteTest() {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
crypto = i.getInstance(CryptoComponent.class);
|
||||
tagCipher = crypto.getTagCipher();
|
||||
frameCipher = crypto.getFrameCipher();
|
||||
// Since we're sending packets to ourselves, we only need outgoing keys
|
||||
macKey = crypto.deriveOutgoingMacKey(secret);
|
||||
tagKey = crypto.deriveOutgoingTagKey(secret);
|
||||
frameKey = crypto.deriveOutgoingFrameKey(secret);
|
||||
mac = crypto.getMac();
|
||||
random = new Random();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteAndRead() 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);
|
||||
// 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();
|
||||
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
|
||||
transportId, connection, tagCipher, frameCipher, tagKey,
|
||||
frameKey);
|
||||
mac.init(macKey);
|
||||
ConnectionWriter writer = new ConnectionWriterImpl(encrypter, mac);
|
||||
OutputStream out1 = writer.getOutputStream();
|
||||
out1.write(frame);
|
||||
out1.flush();
|
||||
out1.write(frame1);
|
||||
out1.flush();
|
||||
// Read the frames back
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
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[frame.length];
|
||||
int offset = 0;
|
||||
while(offset < recovered.length) {
|
||||
int read = in1.read(recovered, offset, recovered.length - offset);
|
||||
if(read == -1) break;
|
||||
offset += read;
|
||||
}
|
||||
assertEquals(recovered.length, offset);
|
||||
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);
|
||||
if(read == -1) break;
|
||||
offset += read;
|
||||
}
|
||||
assertEquals(recovered1.length, offset);
|
||||
assertTrue(Arrays.equals(frame1, recovered1));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user