mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 03:39:05 +01:00
Include the frame number in the header.
This ensures the frame number is covered by the MAC, cleanly separating encryption from authentication (previously we depended on the encryption layer to garble frames if they were reordered).
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
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 static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
|
||||
@@ -11,11 +12,10 @@ import java.security.InvalidKeyException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
class ConnectionReaderImpl extends FilterInputStream
|
||||
implements ConnectionReader {
|
||||
@@ -41,8 +41,9 @@ implements ConnectionReader {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
macKey.erase();
|
||||
maxPayloadLength = MAX_FRAME_LENGTH - 4 - mac.getMacLength();
|
||||
header = new byte[4];
|
||||
maxPayloadLength =
|
||||
MAX_FRAME_LENGTH - FRAME_HEADER_LENGTH - mac.getMacLength();
|
||||
header = new byte[FRAME_HEADER_LENGTH];
|
||||
payload = new byte[maxPayloadLength];
|
||||
footer = new byte[mac.getMacLength()];
|
||||
}
|
||||
@@ -81,7 +82,6 @@ implements ConnectionReader {
|
||||
assert betweenFrames;
|
||||
// Don't allow more than 2^32 frames to be read
|
||||
if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
|
||||
frame++;
|
||||
// Read the header
|
||||
int offset = 0;
|
||||
while(offset < header.length) {
|
||||
@@ -91,13 +91,12 @@ implements ConnectionReader {
|
||||
}
|
||||
if(offset == 0) return false; // EOF between frames
|
||||
if(offset < header.length) throw new EOFException(); // Unexpected EOF
|
||||
mac.update(header);
|
||||
// Check that the payload and padding lengths are legal
|
||||
payloadLen = ByteUtils.readUint16(header, 0);
|
||||
int paddingLen = ByteUtils.readUint16(header, 2);
|
||||
if(payloadLen + paddingLen == 0) throw new FormatException();
|
||||
if(payloadLen + paddingLen > maxPayloadLength)
|
||||
// Check that the frame number is correct and the length is legal
|
||||
if(!HeaderEncoder.validateHeader(header, frame, maxPayloadLength))
|
||||
throw new FormatException();
|
||||
payloadLen = HeaderEncoder.getPayloadLength(header);
|
||||
int paddingLen = HeaderEncoder.getPaddingLength(header);
|
||||
mac.update(header);
|
||||
// Read the payload
|
||||
offset = 0;
|
||||
while(offset < payloadLen) {
|
||||
@@ -124,6 +123,7 @@ implements ConnectionReader {
|
||||
decrypter.readMac(footer);
|
||||
if(!Arrays.equals(expectedMac, footer)) throw new FormatException();
|
||||
betweenFrames = false;
|
||||
frame++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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 static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
|
||||
@@ -10,14 +11,15 @@ import java.io.OutputStream;
|
||||
import java.security.InvalidKeyException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
import net.sf.briar.api.transport.ConnectionWriter;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
/**
|
||||
* A ConnectionWriter that buffers its input and writes a frame whenever there
|
||||
* is a full-size frame to write or the flush() method is called.
|
||||
* <p>
|
||||
* This class is not thread-safe.
|
||||
*/
|
||||
class ConnectionWriterImpl extends FilterOutputStream
|
||||
implements ConnectionWriter {
|
||||
@@ -42,9 +44,10 @@ implements ConnectionWriter {
|
||||
throw new IllegalArgumentException(badKey);
|
||||
}
|
||||
macKey.erase();
|
||||
maxPayloadLength = MAX_FRAME_LENGTH - 4 - mac.getMacLength();
|
||||
maxPayloadLength =
|
||||
MAX_FRAME_LENGTH - FRAME_HEADER_LENGTH - mac.getMacLength();
|
||||
buf = new ByteArrayOutputStream(maxPayloadLength);
|
||||
header = new byte[4];
|
||||
header = new byte[FRAME_HEADER_LENGTH];
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
@@ -95,7 +98,7 @@ implements ConnectionWriter {
|
||||
if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
|
||||
byte[] payload = buf.toByteArray();
|
||||
if(payload.length > maxPayloadLength) throw new IllegalStateException();
|
||||
ByteUtils.writeUint16(payload.length, header, 0);
|
||||
HeaderEncoder.encodeHeader(header, frame, payload.length, 0);
|
||||
out.write(header);
|
||||
mac.update(header);
|
||||
out.write(payload);
|
||||
|
||||
45
components/net/sf/briar/transport/HeaderEncoder.java
Normal file
45
components/net/sf/briar/transport/HeaderEncoder.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import net.sf.briar.api.transport.TransportConstants;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
class HeaderEncoder {
|
||||
|
||||
static void encodeHeader(byte[] header, long frame, int payload,
|
||||
int padding) {
|
||||
if(header.length < TransportConstants.FRAME_HEADER_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
if(frame < 0 || frame > ByteUtils.MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if(payload < 0 || payload > ByteUtils.MAX_16_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if(padding < 0 || padding > ByteUtils.MAX_16_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
ByteUtils.writeUint32(frame, header, 0);
|
||||
ByteUtils.writeUint16(payload, header, 4);
|
||||
ByteUtils.writeUint16(padding, header, 6);
|
||||
}
|
||||
|
||||
static boolean validateHeader(byte[] header, long frame, int max) {
|
||||
if(header.length < TransportConstants.FRAME_HEADER_LENGTH)
|
||||
return false;
|
||||
if(ByteUtils.readUint32(header, 0) != frame) return false;
|
||||
int payload = ByteUtils.readUint16(header, 4);
|
||||
int padding = ByteUtils.readUint16(header, 6);
|
||||
if(payload + padding == 0) return false;
|
||||
if(payload + padding > max) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int getPayloadLength(byte[] header) {
|
||||
if(header.length < TransportConstants.FRAME_HEADER_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
return ByteUtils.readUint16(header, 4);
|
||||
}
|
||||
|
||||
static int getPaddingLength(byte[] header) {
|
||||
if(header.length < TransportConstants.FRAME_HEADER_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
return ByteUtils.readUint16(header, 6);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import net.sf.briar.util.ByteUtils;
|
||||
class IvEncoder {
|
||||
|
||||
static byte[] encodeIv(long frame, int blockSize) {
|
||||
if(frame > ByteUtils.MAX_32_BIT_UNSIGNED)
|
||||
if(frame < 0 || frame > ByteUtils.MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
byte[] iv = new byte[blockSize];
|
||||
updateIv(iv, frame);
|
||||
|
||||
@@ -12,7 +12,7 @@ import net.sf.briar.util.ByteUtils;
|
||||
class TagEncoder {
|
||||
|
||||
static byte[] encodeTag(long frame, Cipher tagCipher, ErasableKey tagKey) {
|
||||
if(frame > ByteUtils.MAX_32_BIT_UNSIGNED)
|
||||
if(frame < 0 || frame > ByteUtils.MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
// The plaintext is blank
|
||||
byte[] plaintext = new byte[TransportConstants.TAG_LENGTH];
|
||||
@@ -32,6 +32,8 @@ class TagEncoder {
|
||||
|
||||
static boolean validateTag(byte[] tag, long frame, Cipher tagCipher,
|
||||
ErasableKey tagKey) {
|
||||
if(frame < 0 || frame > ByteUtils.MAX_32_BIT_UNSIGNED)
|
||||
throw new IllegalArgumentException();
|
||||
if(tag.length != TransportConstants.TAG_LENGTH) return false;
|
||||
// Encode the frame number as a uint32 at the end of the IV
|
||||
byte[] iv = new byte[tagCipher.getBlockSize()];
|
||||
|
||||
Reference in New Issue
Block a user