Expose the encryption and authentication overhead without breaking

encapsulation.

This should allow callers to calculate maximum packet sizes without
knowing the details of the transport protocol.
This commit is contained in:
akwizgran
2011-09-21 15:22:25 +01:00
parent 7e58b25618
commit 10c3b21726
10 changed files with 115 additions and 35 deletions

View File

@@ -11,4 +11,11 @@ interface ConnectionEncrypter {
/** Encrypts and writes the MAC for the current frame. */
void writeMac(byte[] mac) throws IOException;
/**
* Returns the number of encrypted bytes that can be written without
* writing more than the given number of bytes, including encryption
* overhead.
*/
long getCapacity(long capacity);
}

View File

@@ -58,6 +58,11 @@ implements ConnectionEncrypter {
betweenFrames = true;
}
public long getCapacity(long capacity) {
if(capacity < 0L) throw new IllegalArgumentException();
return ivWritten ? capacity : Math.max(0L, capacity - IV_LENGTH);
}
@Override
public void write(int b) throws IOException {
if(!ivWritten) writeIv();

View File

@@ -20,13 +20,13 @@ import net.sf.briar.util.ByteUtils;
class ConnectionWriterImpl extends FilterOutputStream
implements ConnectionWriter {
private final ConnectionEncrypter encrypter;
private final Mac mac;
private final int maxPayloadLength;
private final ByteArrayOutputStream buf;
private final byte[] header;
protected final ConnectionEncrypter encrypter;
protected final Mac mac;
protected final int maxPayloadLength;
protected final ByteArrayOutputStream buf;
protected final byte[] header;
private long frame = 0L;
protected long frame = 0L;
ConnectionWriterImpl(ConnectionEncrypter encrypter, Mac mac) {
super(encrypter.getOutputStream());
@@ -41,6 +41,18 @@ implements ConnectionWriter {
return this;
}
public long getCapacity(long capacity) {
if(capacity < 0L) throw new IllegalArgumentException();
// Subtract the encryption overhead
capacity = encrypter.getCapacity(capacity);
// If there's any data buffered, subtract it and its auth overhead
int overheadPerFrame = header.length + mac.getMacLength();
if(buf.size() > 0) capacity -= buf.size() + overheadPerFrame;
// Subtract the auth overhead from the remaining capacity
long frames = (long) Math.ceil((double) capacity / MAX_FRAME_LENGTH);
return Math.max(0L, capacity - frames * overheadPerFrame);
}
@Override
public void flush() throws IOException {
if(buf.size() > 0) writeFrame();

View File

@@ -1,16 +1,11 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.crypto.Mac;
import net.sf.briar.api.transport.ConnectionWriter;
import net.sf.briar.util.ByteUtils;
/**
@@ -19,33 +14,18 @@ import net.sf.briar.util.ByteUtils;
* padding inserted if necessary. Calls to the writer's write() methods will
* block until there is space to buffer the data.
*/
class PaddedConnectionWriter extends FilterOutputStream
implements ConnectionWriter {
class PaddedConnectionWriter extends ConnectionWriterImpl {
private final ConnectionEncrypter encrypter;
private final Mac mac;
private final int maxPayloadLength;
private final ByteArrayOutputStream buf;
private final byte[] header, padding;
private final byte[] padding;
private long frame = 0L;
private boolean closed = false;
private IOException exception = null;
PaddedConnectionWriter(ConnectionEncrypter encrypter, Mac mac) {
super(encrypter.getOutputStream());
this.encrypter = encrypter;
this.mac = mac;
maxPayloadLength = MAX_FRAME_LENGTH - 4 - mac.getMacLength();
buf = new ByteArrayOutputStream(maxPayloadLength);
header = new byte[4];
super(encrypter, mac);
padding = new byte[maxPayloadLength];
}
public OutputStream getOutputStream() {
return this;
}
@Override
public synchronized void close() throws IOException {
if(exception != null) throw exception;