mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 11:49:04 +01:00
Frame-at-a-time decryption.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
@@ -8,7 +9,6 @@ import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
@@ -45,58 +45,40 @@ public class ConnectionDecrypterImplTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
private void testDecryption(boolean initiator) throws Exception {
|
||||
// Calculate the expected plaintext for the first frame
|
||||
byte[] iv = new byte[frameCipher.getBlockSize()];
|
||||
byte[] ciphertext = new byte[123];
|
||||
byte[] ciphertextMac = new byte[MAC_LENGTH];
|
||||
// Calculate the ciphertext for the first frame
|
||||
byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
|
||||
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
|
||||
byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||
frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
|
||||
byte[] plaintext = new byte[ciphertext.length + ciphertextMac.length];
|
||||
int offset = frameCipher.update(ciphertext, 0, ciphertext.length,
|
||||
plaintext);
|
||||
frameCipher.doFinal(ciphertextMac, 0, ciphertextMac.length, plaintext,
|
||||
offset);
|
||||
// Calculate the expected plaintext for the second frame
|
||||
byte[] ciphertext1 = new byte[1234];
|
||||
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
|
||||
byte[] ciphertext = new byte[plaintext.length];
|
||||
frameCipher.doFinal(plaintext, 0, plaintext.length, ciphertext);
|
||||
// Calculate the ciphertext for the second frame
|
||||
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
|
||||
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
|
||||
IvEncoder.updateIv(iv, 1L);
|
||||
ivSpec = new IvParameterSpec(iv);
|
||||
frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
|
||||
byte[] plaintext1 = new byte[ciphertext1.length + ciphertextMac.length];
|
||||
offset = frameCipher.update(ciphertext1, 0, ciphertext1.length,
|
||||
plaintext1);
|
||||
frameCipher.doFinal(ciphertextMac, 0, ciphertextMac.length, plaintext1,
|
||||
offset);
|
||||
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
|
||||
byte[] ciphertext1 = new byte[plaintext1.length];
|
||||
frameCipher.doFinal(plaintext1, 0, plaintext1.length, ciphertext1);
|
||||
// Concatenate the ciphertexts
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(ciphertext);
|
||||
out.write(ciphertextMac);
|
||||
out.write(ciphertext1);
|
||||
out.write(ciphertextMac);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
// Use a ConnectionDecrypter to decrypt the ciphertext
|
||||
ConnectionDecrypter d = new ConnectionDecrypterImpl(in, frameCipher,
|
||||
frameKey);
|
||||
frameKey, MAC_LENGTH);
|
||||
// First frame
|
||||
byte[] decrypted = new byte[ciphertext.length];
|
||||
TestUtils.readFully(d.getInputStream(), decrypted);
|
||||
byte[] decryptedMac = new byte[MAC_LENGTH];
|
||||
d.readFinal(decryptedMac);
|
||||
byte[] decrypted = new byte[MAX_FRAME_LENGTH];
|
||||
assertEquals(plaintext.length, d.readFrame(decrypted));
|
||||
for(int i = 0; i < plaintext.length; i++) {
|
||||
assertEquals(plaintext[i], decrypted[i]);
|
||||
}
|
||||
// Second frame
|
||||
byte[] decrypted1 = new byte[ciphertext1.length];
|
||||
TestUtils.readFully(d.getInputStream(), decrypted1);
|
||||
byte[] decryptedMac1 = new byte[MAC_LENGTH];
|
||||
d.readFinal(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();
|
||||
assertArrayEquals(expected, actual);
|
||||
assertEquals(plaintext1.length, d.readFrame(decrypted));
|
||||
for(int i = 0; i < plaintext1.length; i++) {
|
||||
assertEquals(plaintext1[i], decrypted[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,8 +69,8 @@ public class ConnectionEncrypterImplTest extends BriarTestCase {
|
||||
out.reset();
|
||||
ConnectionEncrypter e = new ConnectionEncrypterImpl(out, Long.MAX_VALUE,
|
||||
tagCipher, frameCipher, tagKey, frameKey);
|
||||
e.writeFrame(plaintext, 0, plaintext.length);
|
||||
e.writeFrame(plaintext1, 0, plaintext1.length);
|
||||
e.writeFrame(plaintext, plaintext.length);
|
||||
e.writeFrame(plaintext1, plaintext1.length);
|
||||
byte[] actual = out.toByteArray();
|
||||
// Check that the actual ciphertext matches the expected ciphertext
|
||||
assertArrayEquals(expected, actual);
|
||||
|
||||
@@ -31,7 +31,7 @@ public class ConnectionReaderImplTest extends TransportTest {
|
||||
mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength);
|
||||
// Read the frame
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(frame);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in, macLength);
|
||||
ConnectionReader r = new ConnectionReaderImpl(d, mac, macKey);
|
||||
// There should be no bytes available before EOF
|
||||
assertEquals(-1, r.getInputStream().read());
|
||||
@@ -49,7 +49,7 @@ public class ConnectionReaderImplTest extends TransportTest {
|
||||
mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength);
|
||||
// Read the frame
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(frame);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in, macLength);
|
||||
ConnectionReader r = new ConnectionReaderImpl(d, mac, macKey);
|
||||
// There should be one byte available before EOF
|
||||
assertEquals(0, r.getInputStream().read());
|
||||
@@ -75,7 +75,7 @@ public class ConnectionReaderImplTest extends TransportTest {
|
||||
out.write(frame1);
|
||||
// Read the first frame
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in, macLength);
|
||||
ConnectionReader r = new ConnectionReaderImpl(d, mac, macKey);
|
||||
byte[] read = new byte[maxPayloadLength];
|
||||
TestUtils.readFully(r.getInputStream(), read);
|
||||
@@ -109,7 +109,7 @@ public class ConnectionReaderImplTest extends TransportTest {
|
||||
out.write(frame1);
|
||||
// Read the first frame
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in, macLength);
|
||||
ConnectionReader r = new ConnectionReaderImpl(d, mac, macKey);
|
||||
byte[] read = new byte[maxPayloadLength - paddingLength];
|
||||
TestUtils.readFully(r.getInputStream(), read);
|
||||
@@ -135,7 +135,7 @@ public class ConnectionReaderImplTest extends TransportTest {
|
||||
mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength + paddingLength);
|
||||
// Read the frame
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(frame);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in, macLength);
|
||||
ConnectionReader r = new ConnectionReaderImpl(d, mac, macKey);
|
||||
// The non-zero padding should be rejected
|
||||
try {
|
||||
@@ -167,7 +167,7 @@ public class ConnectionReaderImplTest extends TransportTest {
|
||||
out.write(frame1);
|
||||
// Read the frames
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in, macLength);
|
||||
ConnectionReader r = new ConnectionReaderImpl(d, mac, macKey);
|
||||
byte[] read = new byte[payloadLength];
|
||||
TestUtils.readFully(r.getInputStream(), read);
|
||||
@@ -191,7 +191,7 @@ public class ConnectionReaderImplTest extends TransportTest {
|
||||
frame[12] ^= 1;
|
||||
// Try to read the frame - not a single byte should be read
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(frame);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in, macLength);
|
||||
ConnectionReader r = new ConnectionReaderImpl(d, mac, macKey);
|
||||
try {
|
||||
r.getInputStream().read();
|
||||
@@ -213,7 +213,7 @@ public class ConnectionReaderImplTest extends TransportTest {
|
||||
frame[17] ^= 1;
|
||||
// Try to read the frame - not a single byte should be read
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(frame);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in);
|
||||
ConnectionDecrypter d = new NullConnectionDecrypter(in, macLength);
|
||||
ConnectionReader r = new ConnectionReaderImpl(d, mac, macKey);
|
||||
try {
|
||||
r.getInputStream().read();
|
||||
|
||||
@@ -90,7 +90,7 @@ public class FrameReadWriteTest extends BriarTestCase {
|
||||
assertTrue(TagEncoder.validateTag(tag, 0, tagCipher, tagKey));
|
||||
// Read the frames back
|
||||
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in,
|
||||
frameCipher, frameKey);
|
||||
frameCipher, frameKey, mac.getMacLength());
|
||||
ConnectionReader reader = new ConnectionReaderImpl(decrypter, mac,
|
||||
macKey);
|
||||
InputStream in1 = reader.getInputStream();
|
||||
|
||||
@@ -1,29 +1,48 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.sf.briar.api.FormatException;
|
||||
|
||||
/** A ConnectionDecrypter that performs no decryption. */
|
||||
class NullConnectionDecrypter implements ConnectionDecrypter {
|
||||
|
||||
private final InputStream in;
|
||||
private final int macLength;
|
||||
|
||||
NullConnectionDecrypter(InputStream in) {
|
||||
NullConnectionDecrypter(InputStream in, int macLength) {
|
||||
this.in = in;
|
||||
this.macLength = macLength;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return in;
|
||||
}
|
||||
|
||||
public void readFinal(byte[] mac) throws IOException {
|
||||
int offset = 0;
|
||||
while(offset < mac.length) {
|
||||
int read = in.read(mac, offset, mac.length - offset);
|
||||
if(read == -1) break;
|
||||
public int readFrame(byte[] b) throws IOException {
|
||||
if(b.length < MAX_FRAME_LENGTH) throw new IllegalArgumentException();
|
||||
// Read the header to determine the frame length
|
||||
int offset = 0, length = FRAME_HEADER_LENGTH;
|
||||
while(offset < length) {
|
||||
int read = in.read(b, offset, length - offset);
|
||||
if(read == -1) {
|
||||
if(offset == 0) return -1;
|
||||
throw new EOFException();
|
||||
}
|
||||
offset += read;
|
||||
}
|
||||
if(offset < mac.length) throw new EOFException();
|
||||
// Parse the header
|
||||
int payload = HeaderEncoder.getPayloadLength(b);
|
||||
int padding = HeaderEncoder.getPaddingLength(b);
|
||||
length = FRAME_HEADER_LENGTH + payload + padding + macLength;
|
||||
if(length > MAX_FRAME_LENGTH) throw new FormatException();
|
||||
// Read the remainder of the frame
|
||||
while(offset < length) {
|
||||
int read = in.read(b, offset, length - offset);
|
||||
if(read == -1) throw new EOFException();
|
||||
offset += read;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ class NullConnectionEncrypter implements ConnectionEncrypter {
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
public void writeFrame(byte[] b, int off, int len) throws IOException {
|
||||
out.write(b, off, len);
|
||||
public void writeFrame(byte[] b, int len) throws IOException {
|
||||
out.write(b, 0, len);
|
||||
capacity -= len;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user