Allow the transport to specify the maximum segment length.

This commit is contained in:
akwizgran
2012-01-20 23:06:51 +00:00
parent 13eff752da
commit 3a77ba9aaf
34 changed files with 195 additions and 39 deletions

View File

@@ -8,4 +8,9 @@ public interface SegmentSink {
/** Writes the given segment. */ /** Writes the given segment. */
void writeSegment(Segment s) throws IOException; void writeSegment(Segment s) throws IOException;
/**
* Returns the maximum length in bytes of the segments this sink accepts.
*/
int getMaxSegmentLength();
} }

View File

@@ -11,4 +11,9 @@ public interface SegmentSource {
* segment was read, or false if no more segments can be read. * segment was read, or false if no more segments can be read.
*/ */
boolean readSegment(Segment s) throws IOException; boolean readSegment(Segment s) throws IOException;
/**
* Returns the maximum length in bytes of the segments this source returns.
*/
int getMaxSegmentLength();
} }

View File

@@ -19,7 +19,7 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
ConnectionReaderImpl(IncomingReliabilityLayer in, boolean tolerateErrors) { ConnectionReaderImpl(IncomingReliabilityLayer in, boolean tolerateErrors) {
this.in = in; this.in = in;
this.tolerateErrors = tolerateErrors; this.tolerateErrors = tolerateErrors;
frame = new Frame(); frame = new Frame(in.getMaxFrameLength());
} }
public InputStream getInputStream() { public InputStream getInputStream() {

View File

@@ -2,7 +2,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.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED; import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import java.io.IOException; import java.io.IOException;
@@ -19,6 +18,7 @@ import net.sf.briar.api.transport.ConnectionWriter;
class ConnectionWriterImpl extends OutputStream implements ConnectionWriter { class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
private final OutgoingReliabilityLayer out; private final OutgoingReliabilityLayer out;
private final int maxFrameLength;
private final Frame frame; private final Frame frame;
private int offset = FRAME_HEADER_LENGTH; private int offset = FRAME_HEADER_LENGTH;
@@ -26,7 +26,8 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
ConnectionWriterImpl(OutgoingReliabilityLayer out) { ConnectionWriterImpl(OutgoingReliabilityLayer out) {
this.out = out; this.out = out;
frame = new Frame(); maxFrameLength = out.getMaxFrameLength();
frame = new Frame(maxFrameLength);
} }
public OutputStream getOutputStream() { public OutputStream getOutputStream() {
@@ -39,7 +40,7 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
if(offset > FRAME_HEADER_LENGTH) if(offset > FRAME_HEADER_LENGTH)
capacity -= offset + MAC_LENGTH; capacity -= offset + MAC_LENGTH;
// Subtract the overhead from the remaining capacity // Subtract the overhead from the remaining capacity
long frames = (long) Math.ceil((double) capacity / MAX_FRAME_LENGTH); long frames = (long) Math.ceil((double) capacity / maxFrameLength);
int overheadPerFrame = FRAME_HEADER_LENGTH + MAC_LENGTH; int overheadPerFrame = FRAME_HEADER_LENGTH + MAC_LENGTH;
return Math.max(0L, capacity - frames * overheadPerFrame); return Math.max(0L, capacity - frames * overheadPerFrame);
} }
@@ -53,7 +54,7 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
@Override @Override
public void write(int b) throws IOException { public void write(int b) throws IOException {
frame.getBuffer()[offset++] = (byte) b; frame.getBuffer()[offset++] = (byte) b;
if(offset + MAC_LENGTH == MAX_FRAME_LENGTH) writeFrame(); if(offset + MAC_LENGTH == maxFrameLength) writeFrame();
} }
@Override @Override
@@ -64,14 +65,14 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
@Override @Override
public void write(byte[] b, int off, int len) throws IOException { public void write(byte[] b, int off, int len) throws IOException {
byte[] buf = frame.getBuffer(); byte[] buf = frame.getBuffer();
int available = MAX_FRAME_LENGTH - offset - MAC_LENGTH; int available = maxFrameLength - offset - MAC_LENGTH;
while(available <= len) { while(available <= len) {
System.arraycopy(b, off, buf, offset, available); System.arraycopy(b, off, buf, offset, available);
offset += available; offset += available;
writeFrame(); writeFrame();
off += available; off += available;
len -= available; len -= available;
available = MAX_FRAME_LENGTH - offset - MAC_LENGTH; available = maxFrameLength - offset - MAC_LENGTH;
} }
System.arraycopy(b, off, buf, offset, len); System.arraycopy(b, off, buf, offset, len);
offset += len; offset += len;

View File

@@ -1,6 +1,9 @@
package net.sf.briar.transport; package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
class Frame { class Frame {
@@ -13,6 +16,9 @@ class Frame {
} }
Frame(int length) { Frame(int length) {
if(length < FRAME_HEADER_LENGTH + MAC_LENGTH)
throw new IllegalArgumentException();
if(length > MAX_SEGMENT_LENGTH) throw new IllegalArgumentException();
buf = new byte[length]; buf = new byte[length];
} }

View File

@@ -15,4 +15,7 @@ interface IncomingAuthenticationLayer {
*/ */
boolean readFrame(Frame f, FrameWindow window) throws IOException, boolean readFrame(Frame f, FrameWindow window) throws IOException,
InvalidDataException; InvalidDataException;
/** Returns the maximum length in bytes of the frames this layer returns. */
int getMaxFrameLength();
} }

View File

@@ -2,7 +2,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.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import java.io.IOException; import java.io.IOException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
@@ -14,13 +13,13 @@ import net.sf.briar.api.crypto.ErasableKey;
class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer { class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
private final IncomingErrorCorrectionLayer in; private final IncomingErrorCorrectionLayer in;
private final int maxFrameLength;
private final Mac mac; private final Mac mac;
IncomingAuthenticationLayerImpl(IncomingErrorCorrectionLayer in, Mac mac, IncomingAuthenticationLayerImpl(IncomingErrorCorrectionLayer in, Mac mac,
ErasableKey macKey) { ErasableKey macKey) {
this.in = in; this.in = in;
this.mac = mac; this.mac = mac;
// Initialise the MAC
try { try {
mac.init(macKey); mac.init(macKey);
} catch(InvalidKeyException e) { } catch(InvalidKeyException e) {
@@ -29,6 +28,7 @@ class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
macKey.erase(); macKey.erase();
if(mac.getMacLength() != MAC_LENGTH) if(mac.getMacLength() != MAC_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
maxFrameLength = in.getMaxFrameLength();
} }
public boolean readFrame(Frame f, FrameWindow window) throws IOException, public boolean readFrame(Frame f, FrameWindow window) throws IOException,
@@ -39,7 +39,7 @@ class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
int length = f.getLength(); int length = f.getLength();
if(length < FRAME_HEADER_LENGTH + MAC_LENGTH) if(length < FRAME_HEADER_LENGTH + MAC_LENGTH)
throw new InvalidDataException(); throw new InvalidDataException();
if(length > MAX_FRAME_LENGTH) throw new InvalidDataException(); if(length > maxFrameLength) throw new InvalidDataException();
// Check that the payload and padding lengths are correct // Check that the payload and padding lengths are correct
byte[] buf = f.getBuffer(); byte[] buf = f.getBuffer();
int payload = HeaderEncoder.getPayloadLength(buf); int payload = HeaderEncoder.getPayloadLength(buf);
@@ -61,4 +61,8 @@ class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
} }
return true; return true;
} }
public int getMaxFrameLength() {
return maxFrameLength;
}
} }

View File

@@ -15,4 +15,9 @@ interface IncomingEncryptionLayer {
* may choose whether to retry the read or close the connection. * may choose whether to retry the read or close the connection.
*/ */
boolean readSegment(Segment s) throws IOException, InvalidDataException; boolean readSegment(Segment s) throws IOException, InvalidDataException;
/**
* Returns the maximum length in bytes of the segments this layer returns.
*/
int getMaxSegmentLength();
} }

View File

@@ -23,9 +23,9 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
private final InputStream in; private final InputStream in;
private final Cipher tagCipher, segCipher; private final Cipher tagCipher, segCipher;
private final ErasableKey tagKey, segKey; private final ErasableKey tagKey, segKey;
private final boolean tagEverySegment;
private final int blockSize; private final int blockSize;
private final byte[] iv, ciphertext; private final byte[] iv, ciphertext;
private final boolean tagEverySegment;
private byte[] bufferedTag; private byte[] bufferedTag;
private boolean firstSegment = true; private boolean firstSegment = true;
@@ -128,4 +128,8 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
throw e; throw e;
} }
} }
public int getMaxSegmentLength() {
return MAX_SEGMENT_LENGTH - TAG_LENGTH;
}
} }

View File

@@ -15,4 +15,7 @@ interface IncomingErrorCorrectionLayer {
*/ */
boolean readFrame(Frame f, FrameWindow window) throws IOException, boolean readFrame(Frame f, FrameWindow window) throws IOException,
InvalidDataException; InvalidDataException;
/** Returns the maximum length in bytes of the frames this layer returns. */
int getMaxFrameLength();
} }

View File

@@ -1,5 +1,7 @@
package net.sf.briar.transport; package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -14,7 +16,7 @@ class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
private final IncomingEncryptionLayer in; private final IncomingEncryptionLayer in;
private final ErasureDecoder decoder; private final ErasureDecoder decoder;
private final int n, k; private final int n, k, maxSegmentLength, maxFrameLength;
private final Map<Long, Integer> discardCounts; private final Map<Long, Integer> discardCounts;
private final Map<Long, Segment[]> segmentSets; private final Map<Long, Segment[]> segmentSets;
private final ArrayList<Segment> freeSegments; private final ArrayList<Segment> freeSegments;
@@ -25,6 +27,8 @@ class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
this.decoder = decoder; this.decoder = decoder;
this.n = n; this.n = n;
this.k = k; this.k = k;
maxSegmentLength = in.getMaxSegmentLength();
maxFrameLength = Math.min(MAX_FRAME_LENGTH, maxSegmentLength * k);
discardCounts = new HashMap<Long, Integer>(); discardCounts = new HashMap<Long, Integer>();
segmentSets = new HashMap<Long, Segment[]>(); segmentSets = new HashMap<Long, Segment[]>();
freeSegments = new ArrayList<Segment>(); freeSegments = new ArrayList<Segment>();
@@ -47,7 +51,7 @@ class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
// Grab a free segment, or allocate one if necessary // Grab a free segment, or allocate one if necessary
Segment s; Segment s;
int free = freeSegments.size(); int free = freeSegments.size();
if(free == 0) s = new SegmentImpl(); if(free == 0) s = new SegmentImpl(maxSegmentLength);
else s = freeSegments.remove(free - 1); else s = freeSegments.remove(free - 1);
// Read segments until a frame can be decoded // Read segments until a frame can be decoded
while(true) { while(true) {
@@ -74,6 +78,10 @@ class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
} }
} }
public int getMaxFrameLength() {
return maxFrameLength;
}
private void countDiscard(long frameNumber) throws FormatException { private void countDiscard(long frameNumber) throws FormatException {
Integer count = discardCounts.get(frameNumber); Integer count = discardCounts.get(frameNumber);
if(count == null) discardCounts.put(frameNumber, 1); if(count == null) discardCounts.put(frameNumber, 1);

View File

@@ -13,4 +13,7 @@ interface IncomingReliabilityLayer {
* may choose whether to retry the read or close the connection. * may choose whether to retry the read or close the connection.
*/ */
boolean readFrame(Frame f) throws IOException, InvalidDataException; boolean readFrame(Frame f) throws IOException, InvalidDataException;
/** Returns the maximum length in bytes of the frames this layer returns. */
int getMaxFrameLength();
} }

View File

@@ -20,10 +20,10 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
private final SegmentSource in; private final SegmentSource in;
private final Cipher tagCipher, segCipher; private final Cipher tagCipher, segCipher;
private final ErasableKey tagKey, segKey; private final ErasableKey tagKey, segKey;
private final int blockSize;
private final byte[] iv;
private final boolean tagEverySegment; private final boolean tagEverySegment;
private final int blockSize, maxSegmentLength;
private final Segment segment; private final Segment segment;
private final byte[] iv;
private Segment bufferedSegment; private Segment bufferedSegment;
private boolean firstSegment = true; private boolean firstSegment = true;
@@ -42,8 +42,13 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
blockSize = segCipher.getBlockSize(); blockSize = segCipher.getBlockSize();
if(blockSize < FRAME_HEADER_LENGTH) if(blockSize < FRAME_HEADER_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
int length = in.getMaxSegmentLength();
if(length < TAG_LENGTH + FRAME_HEADER_LENGTH + 1 + MAC_LENGTH)
throw new IllegalArgumentException();
if(length > MAX_SEGMENT_LENGTH) throw new IllegalArgumentException();
maxSegmentLength = length - TAG_LENGTH;
segment = new SegmentImpl(length);
iv = IvEncoder.encodeIv(0L, blockSize); iv = IvEncoder.encodeIv(0L, blockSize);
segment = new SegmentImpl();
} }
public boolean readSegment(Segment s) throws IOException, public boolean readSegment(Segment s) throws IOException,
@@ -62,9 +67,10 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
} }
int offset = expectTag ? TAG_LENGTH : 0; int offset = expectTag ? TAG_LENGTH : 0;
int length = segment.getLength(); int length = segment.getLength();
if(length > MAX_SEGMENT_LENGTH) throw new InvalidDataException();
if(length < offset + FRAME_HEADER_LENGTH + MAC_LENGTH) if(length < offset + FRAME_HEADER_LENGTH + MAC_LENGTH)
throw new InvalidDataException(); throw new InvalidDataException();
if(length > offset + maxSegmentLength)
throw new InvalidDataException();
byte[] ciphertext = segment.getBuffer(); byte[] ciphertext = segment.getBuffer();
// If a tag is expected then decrypt and validate it // If a tag is expected then decrypt and validate it
if(expectTag) { if(expectTag) {
@@ -92,4 +98,8 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
throw e; throw e;
} }
} }
public int getMaxSegmentLength() {
return maxSegmentLength;
}
} }

View File

@@ -7,11 +7,13 @@ import net.sf.briar.api.transport.Segment;
class NullIncomingErrorCorrectionLayer implements IncomingErrorCorrectionLayer { class NullIncomingErrorCorrectionLayer implements IncomingErrorCorrectionLayer {
private final IncomingEncryptionLayer in; private final IncomingEncryptionLayer in;
private final int maxFrameLength;
private final Segment segment; private final Segment segment;
NullIncomingErrorCorrectionLayer(IncomingEncryptionLayer in) { NullIncomingErrorCorrectionLayer(IncomingEncryptionLayer in) {
this.in = in; this.in = in;
segment = new SegmentImpl(); maxFrameLength = in.getMaxSegmentLength();
segment = new SegmentImpl(maxFrameLength);
} }
public boolean readFrame(Frame f, FrameWindow window) throws IOException, public boolean readFrame(Frame f, FrameWindow window) throws IOException,
@@ -28,4 +30,8 @@ class NullIncomingErrorCorrectionLayer implements IncomingErrorCorrectionLayer {
f.setLength(length); f.setLength(length);
return true; return true;
} }
public int getMaxFrameLength() {
return maxFrameLength;
}
} }

View File

@@ -5,10 +5,12 @@ import java.io.IOException;
class NullIncomingReliabilityLayer implements IncomingReliabilityLayer { class NullIncomingReliabilityLayer implements IncomingReliabilityLayer {
private final IncomingAuthenticationLayer in; private final IncomingAuthenticationLayer in;
private final int maxFrameLength;
private final FrameWindow window; private final FrameWindow window;
NullIncomingReliabilityLayer(IncomingAuthenticationLayer in) { NullIncomingReliabilityLayer(IncomingAuthenticationLayer in) {
this.in = in; this.in = in;
maxFrameLength = in.getMaxFrameLength();
window = new NullFrameWindow(); window = new NullFrameWindow();
} }
@@ -18,4 +20,8 @@ class NullIncomingReliabilityLayer implements IncomingReliabilityLayer {
if(!window.remove(frameNumber)) throw new IllegalStateException(); if(!window.remove(frameNumber)) throw new IllegalStateException();
return true; return true;
} }
public int getMaxFrameLength() {
return maxFrameLength;
}
} }

View File

@@ -9,13 +9,15 @@ import net.sf.briar.api.transport.Segment;
class NullOutgoingErrorCorrectionLayer implements OutgoingErrorCorrectionLayer { class NullOutgoingErrorCorrectionLayer implements OutgoingErrorCorrectionLayer {
private final OutgoingEncryptionLayer out; private final OutgoingEncryptionLayer out;
private final int maxSegmentLength;
private final Segment segment; private final Segment segment;
private long segmentNumber = 0L; private long segmentNumber = 0L;
public NullOutgoingErrorCorrectionLayer(OutgoingEncryptionLayer out) { public NullOutgoingErrorCorrectionLayer(OutgoingEncryptionLayer out) {
this.out = out; this.out = out;
segment = new SegmentImpl(); maxSegmentLength = out.getMaxSegmentLength();
segment = new SegmentImpl(maxSegmentLength);
} }
public void writeFrame(Frame f) throws IOException { public void writeFrame(Frame f) throws IOException {
@@ -36,4 +38,8 @@ class NullOutgoingErrorCorrectionLayer implements OutgoingErrorCorrectionLayer {
public long getRemainingCapacity() { public long getRemainingCapacity() {
return out.getRemainingCapacity(); return out.getRemainingCapacity();
} }
public int getMaxFrameLength() {
return maxSegmentLength;
}
} }

View File

@@ -5,9 +5,11 @@ import java.io.IOException;
class NullOutgoingReliabilityLayer implements OutgoingReliabilityLayer { class NullOutgoingReliabilityLayer implements OutgoingReliabilityLayer {
private final OutgoingAuthenticationLayer out; private final OutgoingAuthenticationLayer out;
private final int maxFrameLength;
NullOutgoingReliabilityLayer(OutgoingAuthenticationLayer out) { NullOutgoingReliabilityLayer(OutgoingAuthenticationLayer out) {
this.out = out; this.out = out;
maxFrameLength = out.getMaxFrameLength();
} }
public void writeFrame(Frame f) throws IOException { public void writeFrame(Frame f) throws IOException {
@@ -21,4 +23,8 @@ class NullOutgoingReliabilityLayer implements OutgoingReliabilityLayer {
public long getRemainingCapacity() { public long getRemainingCapacity() {
return out.getRemainingCapacity(); return out.getRemainingCapacity();
} }
public int getMaxFrameLength() {
return maxFrameLength;
}
} }

View File

@@ -12,4 +12,7 @@ interface OutgoingAuthenticationLayer {
/** Returns the maximum number of bytes that can be written. */ /** Returns the maximum number of bytes that can be written. */
long getRemainingCapacity(); long getRemainingCapacity();
/** Returns the maximum length in bytes of the frames this layer accepts. */
int getMaxFrameLength();
} }

View File

@@ -14,12 +14,12 @@ class OutgoingAuthenticationLayerImpl implements OutgoingAuthenticationLayer {
private final OutgoingErrorCorrectionLayer out; private final OutgoingErrorCorrectionLayer out;
private final Mac mac; private final Mac mac;
private final int maxFrameLength;
OutgoingAuthenticationLayerImpl(OutgoingErrorCorrectionLayer out, Mac mac, OutgoingAuthenticationLayerImpl(OutgoingErrorCorrectionLayer out, Mac mac,
ErasableKey macKey) { ErasableKey macKey) {
this.out = out; this.out = out;
this.mac = mac; this.mac = mac;
// Initialise the MAC
try { try {
mac.init(macKey); mac.init(macKey);
} catch(InvalidKeyException badKey) { } catch(InvalidKeyException badKey) {
@@ -28,6 +28,7 @@ class OutgoingAuthenticationLayerImpl implements OutgoingAuthenticationLayer {
macKey.erase(); macKey.erase();
if(mac.getMacLength() != MAC_LENGTH) if(mac.getMacLength() != MAC_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
maxFrameLength = out.getMaxFrameLength();
} }
public void writeFrame(Frame f) throws IOException { public void writeFrame(Frame f) throws IOException {
@@ -49,4 +50,8 @@ class OutgoingAuthenticationLayerImpl implements OutgoingAuthenticationLayer {
public long getRemainingCapacity() { public long getRemainingCapacity() {
return out.getRemainingCapacity(); return out.getRemainingCapacity();
} }
public int getMaxFrameLength() {
return maxFrameLength;
}
} }

View File

@@ -14,4 +14,9 @@ interface OutgoingEncryptionLayer {
/** Returns the maximum number of bytes that can be written. */ /** Returns the maximum number of bytes that can be written. */
long getRemainingCapacity(); long getRemainingCapacity();
/**
* Returns the maximum length in bytes of the segments this layer accepts.
*/
int getMaxSegmentLength();
} }

View File

@@ -73,4 +73,8 @@ class OutgoingEncryptionLayerImpl implements OutgoingEncryptionLayer {
public long getRemainingCapacity() { public long getRemainingCapacity() {
return capacity; return capacity;
} }
public int getMaxSegmentLength() {
return MAX_SEGMENT_LENGTH - TAG_LENGTH;
}
} }

View File

@@ -12,4 +12,7 @@ interface OutgoingErrorCorrectionLayer {
/** Returns the maximum number of bytes that can be written. */ /** Returns the maximum number of bytes that can be written. */
long getRemainingCapacity(); long getRemainingCapacity();
/** Returns the maximum length in bytes of the frames this layer accepts. */
int getMaxFrameLength();
} }

View File

@@ -1,5 +1,7 @@
package net.sf.briar.transport; package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import java.io.IOException; import java.io.IOException;
import net.sf.briar.api.transport.Segment; import net.sf.briar.api.transport.Segment;
@@ -8,13 +10,15 @@ class OutgoingErrorCorrectionLayerImpl implements OutgoingErrorCorrectionLayer {
private final OutgoingEncryptionLayer out; private final OutgoingEncryptionLayer out;
private final ErasureEncoder encoder; private final ErasureEncoder encoder;
private final int n; private final int n, maxFrameLength;
OutgoingErrorCorrectionLayerImpl(OutgoingEncryptionLayer out, OutgoingErrorCorrectionLayerImpl(OutgoingEncryptionLayer out,
ErasureEncoder encoder, int n) { ErasureEncoder encoder, int n, int k) {
this.out = out; this.out = out;
this.encoder = encoder; this.encoder = encoder;
this.n = n; this.n = n;
maxFrameLength = Math.min(MAX_FRAME_LENGTH,
out.getMaxSegmentLength() * k);
} }
public void writeFrame(Frame f) throws IOException { public void writeFrame(Frame f) throws IOException {
@@ -28,4 +32,8 @@ class OutgoingErrorCorrectionLayerImpl implements OutgoingErrorCorrectionLayer {
public long getRemainingCapacity() { public long getRemainingCapacity() {
return out.getRemainingCapacity() / n; return out.getRemainingCapacity() / n;
} }
public int getMaxFrameLength() {
return maxFrameLength;
}
} }

View File

@@ -12,4 +12,7 @@ interface OutgoingReliabilityLayer {
/** Returns the maximum number of bytes that can be written. */ /** Returns the maximum number of bytes that can be written. */
long getRemainingCapacity(); long getRemainingCapacity();
/** Returns the maximum length in bytes of the frames this layer accepts. */
int getMaxFrameLength();
} }

View File

@@ -1,5 +1,8 @@
package net.sf.briar.transport; package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import java.io.IOException; import java.io.IOException;
@@ -18,8 +21,9 @@ class OutgoingSegmentedEncryptionLayer implements OutgoingEncryptionLayer {
private final Cipher tagCipher, segCipher; private final Cipher tagCipher, segCipher;
private final ErasableKey tagKey, segKey; private final ErasableKey tagKey, segKey;
private final boolean tagEverySegment; private final boolean tagEverySegment;
private final byte[] iv; private final int maxSegmentLength;
private final Segment segment; private final Segment segment;
private final byte[] iv;
private long capacity; private long capacity;
@@ -33,8 +37,13 @@ class OutgoingSegmentedEncryptionLayer implements OutgoingEncryptionLayer {
this.tagKey = tagKey; this.tagKey = tagKey;
this.segKey = segKey; this.segKey = segKey;
this.tagEverySegment = tagEverySegment; this.tagEverySegment = tagEverySegment;
int length = out.getMaxSegmentLength();
if(length < TAG_LENGTH + FRAME_HEADER_LENGTH + 1 + MAC_LENGTH)
throw new RuntimeException();
if(length > MAX_SEGMENT_LENGTH) throw new RuntimeException();
maxSegmentLength = length - MAC_LENGTH;
segment = new SegmentImpl(length);
iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize()); iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
segment = new SegmentImpl();
} }
public void writeSegment(Segment s) throws IOException { public void writeSegment(Segment s) throws IOException {
@@ -72,4 +81,8 @@ class OutgoingSegmentedEncryptionLayer implements OutgoingEncryptionLayer {
public long getRemainingCapacity() { public long getRemainingCapacity() {
return capacity; return capacity;
} }
public int getMaxSegmentLength() {
return maxSegmentLength;
}
} }

View File

@@ -1,5 +1,7 @@
package net.sf.briar.transport; package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED; import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import net.sf.briar.api.transport.Segment; import net.sf.briar.api.transport.Segment;
@@ -16,6 +18,9 @@ class SegmentImpl implements Segment {
} }
SegmentImpl(int length) { SegmentImpl(int length) {
if(length < FRAME_HEADER_LENGTH + MAC_LENGTH)
throw new IllegalArgumentException();
if(length > MAX_SEGMENT_LENGTH) throw new IllegalArgumentException();
buf = new byte[length]; buf = new byte[length];
} }

View File

@@ -160,5 +160,9 @@ public class IncomingErrorCorrectionLayerImplTest extends BriarTestCase {
s.setLength(length); s.setLength(length);
return true; return true;
} }
public int getMaxSegmentLength() {
return length;
}
} }
} }

View File

@@ -2,6 +2,7 @@ package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import java.io.IOException; import java.io.IOException;
@@ -149,5 +150,9 @@ public class IncomingSegmentedEncryptionLayerTest extends BriarTestCase {
s.setLength(segment.length); s.setLength(segment.length);
return true; return true;
} }
public int getMaxSegmentLength() {
return MAX_SEGMENT_LENGTH;
}
} }
} }

View File

@@ -3,6 +3,8 @@ package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
@@ -49,4 +51,8 @@ class NullIncomingEncryptionLayer implements IncomingEncryptionLayer {
s.setSegmentNumber(segmentNumber++); s.setSegmentNumber(segmentNumber++);
return true; return true;
} }
public int getMaxSegmentLength() {
return MAX_SEGMENT_LENGTH - TAG_LENGTH;
}
} }

View File

@@ -1,5 +1,8 @@
package net.sf.briar.transport; package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@@ -34,4 +37,8 @@ class NullOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
public long getRemainingCapacity() { public long getRemainingCapacity() {
return capacity; return capacity;
} }
public int getMaxSegmentLength() {
return MAX_SEGMENT_LENGTH - TAG_LENGTH;
}
} }

View File

@@ -1,5 +1,6 @@
package net.sf.briar.transport; package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
@@ -136,5 +137,9 @@ public class OutgoingSegmentedEncryptionLayerTest extends BriarTestCase {
public void writeSegment(Segment s) throws IOException { public void writeSegment(Segment s) throws IOException {
write(s.getBuffer(), 0, s.getLength()); write(s.getBuffer(), 0, s.getLength());
} }
public int getMaxSegmentLength() {
return MAX_SEGMENT_LENGTH;
}
} }
} }

View File

@@ -2,13 +2,13 @@ package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import java.util.Random; import java.util.Random;
import net.sf.briar.BriarTestCase; import net.sf.briar.BriarTestCase;
import net.sf.briar.api.transport.Segment; import net.sf.briar.api.transport.Segment;
import static org.junit.Assert.assertArrayEquals;
import org.junit.Test; import org.junit.Test;
public class XorErasureCodeTest extends BriarTestCase { public class XorErasureCodeTest extends BriarTestCase {

View File

@@ -3,11 +3,11 @@ package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH; import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import net.sf.briar.BriarTestCase; import net.sf.briar.BriarTestCase;
import net.sf.briar.api.FormatException; import net.sf.briar.api.FormatException;
import net.sf.briar.api.transport.Segment; import net.sf.briar.api.transport.Segment;
import static org.junit.Assert.assertArrayEquals;
import org.junit.Test; import org.junit.Test;
public class XorErasureDecoderTest extends BriarTestCase { public class XorErasureDecoderTest extends BriarTestCase {

View File

@@ -10,30 +10,29 @@ public class XorErasureEncoderTest extends BriarTestCase {
@Test @Test
public void testEncoding() { public void testEncoding() {
// Create a 100-byte frame // Create a frame
Frame f = new Frame(); Frame f = new Frame();
f.setLength(100); f.setLength(200);
byte[] b = f.getBuffer(); byte[] b = f.getBuffer();
for(int i = 0; i < 100; i++) b[i] = (byte) i; for(int i = 0; i < 200; i++) b[i] = (byte) i;
// Encode the frame // Encode the frame
XorErasureEncoder e = new XorErasureEncoder(4); XorErasureEncoder e = new XorErasureEncoder(4);
Segment[] set = e.encodeFrame(f); Segment[] set = e.encodeFrame(f);
// There should be four pieces of 34 bytes each // There should be four pieces of 67 bytes each
assertEquals(4, set.length); assertEquals(4, set.length);
for(int i = 0; i < 4; i++) assertEquals(34, set[i].getLength()); for(int i = 0; i < 4; i++) assertEquals(67, set[i].getLength());
// The first three pieces should contain the data, plus two zero bytes // The first three pieces should contain the data plus on padding byte
byte[] b1 = set[0].getBuffer(); byte[] b1 = set[0].getBuffer();
for(int i = 0; i < 34; i++) assertEquals(i, b1[i]); for(int i = 0; i < 67; i++) assertEquals((byte) i, b1[i]);
byte[] b2 = set[1].getBuffer(); byte[] b2 = set[1].getBuffer();
for(int i = 0; i < 34; i++) assertEquals(i + 34, b2[i]); for(int i = 0; i < 67; i++) assertEquals((byte) (i + 67), b2[i]);
byte[] b3 = set[2].getBuffer(); byte[] b3 = set[2].getBuffer();
for(int i = 0; i < 32; i++) assertEquals(i + 68, b3[i]); for(int i = 0; i < 66; i++) assertEquals((byte) (i + 134), b3[i]);
assertEquals(0, b3[32]); assertEquals(0, b3[66]);
assertEquals(0, b3[33]);
// The fourth piece should be the XOR of the other three // The fourth piece should be the XOR of the other three
byte[] b4 = set[3].getBuffer(); byte[] b4 = set[3].getBuffer();
byte[] expected = new byte[34]; byte[] expected = new byte[67];
for(int i = 0; i < 34; i++) { for(int i = 0; i < 67; i++) {
expected[i] = (byte) (b1[i] ^ b2[i] ^ b3[i]); expected[i] = (byte) (b1[i] ^ b2[i] ^ b3[i]);
} }
assertArrayEquals(expected, b4); assertArrayEquals(expected, b4);