Split the functionality of ConnectionWriterImpl into layers.

This commit is contained in:
akwizgran
2012-01-19 20:51:45 +00:00
parent 12393581f9
commit aabb8fb5b3
10 changed files with 190 additions and 98 deletions

View File

@@ -44,18 +44,18 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
// Create the decrypter
Cipher tagCipher = crypto.getTagCipher();
Cipher segCipher = crypto.getSegmentCipher();
IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
IncomingEncryptionLayer encryption = new IncomingEncryptionLayerImpl(in,
tagCipher, segCipher, tagKey, segKey, false, bufferedTag);
// No error correction
IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
IncomingErrorCorrectionLayer correction =
new NullIncomingErrorCorrectionLayer(encryption);
// Create the authenticator
Mac mac = crypto.getMac();
IncomingAuthenticationLayer authenticator =
new IncomingAuthenticationLayerImpl(correcter, mac, macKey);
IncomingAuthenticationLayer authentication =
new IncomingAuthenticationLayerImpl(correction, mac, macKey);
// No reordering or retransmission
IncomingReliabilityLayer reliability =
new NullIncomingReliabilityLayer(authenticator);
new NullIncomingReliabilityLayer(authentication);
// Create the reader - don't tolerate errors
return new ConnectionReaderImpl(reliability, false);
}
@@ -80,19 +80,19 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
// Create the decrypter
Cipher tagCipher = crypto.getTagCipher();
Cipher segCipher = crypto.getSegmentCipher();
IncomingEncryptionLayer decrypter =
IncomingEncryptionLayer encryption =
new IncomingSegmentedEncryptionLayer(in, tagCipher, segCipher,
tagKey, segKey, false, bufferedSegment);
// No error correction
IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
IncomingErrorCorrectionLayer correction =
new NullIncomingErrorCorrectionLayer(encryption);
// Create the authenticator
Mac mac = crypto.getMac();
IncomingAuthenticationLayer authenticator =
new IncomingAuthenticationLayerImpl(correcter, mac, macKey);
IncomingAuthenticationLayer authentication =
new IncomingAuthenticationLayerImpl(correction, mac, macKey);
// No reordering or retransmission
IncomingReliabilityLayer reliability =
new NullIncomingReliabilityLayer(authenticator);
new NullIncomingReliabilityLayer(authentication);
// Create the reader - don't tolerate errors
return new ConnectionReaderImpl(reliability, false);
}

View File

@@ -33,14 +33,20 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
// Create the encrypter
Cipher tagCipher = crypto.getTagCipher();
Cipher segCipher = crypto.getSegmentCipher();
OutgoingEncryptionLayer encrypter = new OutgoingEncryptionLayerImpl(out,
capacity, tagCipher, segCipher, tagKey, segKey, false);
OutgoingEncryptionLayer encryption = new OutgoingEncryptionLayerImpl(
out, capacity, tagCipher, segCipher, tagKey, segKey, false);
// No error correction
OutgoingErrorCorrectionLayer correcter =
new NullOutgoingErrorCorrectionLayer(encrypter);
// Create the writer
OutgoingErrorCorrectionLayer correction =
new NullOutgoingErrorCorrectionLayer(encryption);
// Authentication
Mac mac = crypto.getMac();
return new ConnectionWriterImpl(correcter, mac, macKey);
OutgoingAuthenticationLayer authentication =
new OutgoingAuthenticationLayerImpl(correction, mac, macKey);
// No retransmission
OutgoingReliabilityLayer reliability =
new NullOutgoingReliabilityLayer(authentication);
// Create the writer
return new ConnectionWriterImpl(reliability);
}
public ConnectionWriter createConnectionWriter(SegmentSink out,
@@ -53,14 +59,20 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
// Create the encrypter
Cipher tagCipher = crypto.getTagCipher();
Cipher segCipher = crypto.getSegmentCipher();
OutgoingEncryptionLayer encrypter =
OutgoingEncryptionLayer encryption =
new OutgoingSegmentedEncryptionLayer(out, capacity, tagCipher,
segCipher, tagKey, segKey, false);
// No error correction
OutgoingErrorCorrectionLayer correcter =
new NullOutgoingErrorCorrectionLayer(encrypter);
// Create the writer
OutgoingErrorCorrectionLayer correction =
new NullOutgoingErrorCorrectionLayer(encryption);
// Authentication
Mac mac = crypto.getMac();
return new ConnectionWriterImpl(correcter, mac, macKey);
OutgoingAuthenticationLayer authentication =
new OutgoingAuthenticationLayerImpl(correction, mac, macKey);
// No retransmission
OutgoingReliabilityLayer reliability =
new NullOutgoingReliabilityLayer(authentication);
// Create the writer
return new ConnectionWriterImpl(reliability);
}
}

View File

@@ -7,12 +7,7 @@ import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import javax.crypto.Mac;
import javax.crypto.ShortBufferException;
import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.api.transport.ConnectionWriter;
/**
@@ -23,26 +18,14 @@ import net.sf.briar.api.transport.ConnectionWriter;
*/
class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
private final OutgoingErrorCorrectionLayer out;
private final Mac mac;
private final OutgoingReliabilityLayer out;
private final Frame frame;
private int offset = FRAME_HEADER_LENGTH;
private long frameNumber = 0L;
ConnectionWriterImpl(OutgoingErrorCorrectionLayer out, Mac mac,
ErasableKey macKey) {
ConnectionWriterImpl(OutgoingReliabilityLayer out) {
this.out = out;
this.mac = mac;
// Initialise the MAC
try {
mac.init(macKey);
} catch(InvalidKeyException badKey) {
throw new IllegalArgumentException(badKey);
}
macKey.erase();
if(mac.getMacLength() != MAC_LENGTH)
throw new IllegalArgumentException();
frame = new Frame();
}
@@ -96,16 +79,9 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
private void writeFrame() throws IOException {
if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
byte[] buf = frame.getBuffer();
int payloadLength = offset - FRAME_HEADER_LENGTH;
assert payloadLength > 0;
HeaderEncoder.encodeHeader(buf, frameNumber, payloadLength, 0);
mac.update(buf, 0, offset);
try {
mac.doFinal(buf, offset);
} catch(ShortBufferException badMac) {
throw new RuntimeException(badMac);
}
int payload = offset - FRAME_HEADER_LENGTH;
assert payload > 0;
HeaderEncoder.encodeHeader(frame.getBuffer(), frameNumber, payload, 0);
frame.setLength(offset + MAC_LENGTH);
out.writeFrame(frame);
offset = FRAME_HEADER_LENGTH;

View File

@@ -0,0 +1,24 @@
package net.sf.briar.transport;
import java.io.IOException;
class NullOutgoingReliabilityLayer implements OutgoingReliabilityLayer {
private final OutgoingAuthenticationLayer out;
NullOutgoingReliabilityLayer(OutgoingAuthenticationLayer out) {
this.out = out;
}
public void writeFrame(Frame f) throws IOException {
out.writeFrame(f);
}
public void flush() throws IOException {
out.flush();
}
public long getRemainingCapacity() {
return out.getRemainingCapacity();
}
}

View File

@@ -0,0 +1,15 @@
package net.sf.briar.transport;
import java.io.IOException;
interface OutgoingAuthenticationLayer {
/** Writes the given frame. */
void writeFrame(Frame f) throws IOException;
/** Flushes the stack. */
void flush() throws IOException;
/** Returns the maximum number of bytes that can be written. */
long getRemainingCapacity();
}

View File

@@ -0,0 +1,52 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
import java.io.IOException;
import java.security.InvalidKeyException;
import javax.crypto.Mac;
import javax.crypto.ShortBufferException;
import net.sf.briar.api.crypto.ErasableKey;
class OutgoingAuthenticationLayerImpl implements OutgoingAuthenticationLayer {
private final OutgoingErrorCorrectionLayer out;
private final Mac mac;
OutgoingAuthenticationLayerImpl(OutgoingErrorCorrectionLayer out, Mac mac,
ErasableKey macKey) {
this.out = out;
this.mac = mac;
// Initialise the MAC
try {
mac.init(macKey);
} catch(InvalidKeyException badKey) {
throw new IllegalArgumentException(badKey);
}
macKey.erase();
if(mac.getMacLength() != MAC_LENGTH)
throw new IllegalArgumentException();
}
public void writeFrame(Frame f) throws IOException {
byte[] buf = f.getBuffer();
int length = f.getLength() - MAC_LENGTH;
mac.update(buf, 0, length);
try {
mac.doFinal(buf, length);
} catch(ShortBufferException badMac) {
throw new RuntimeException(badMac);
}
out.writeFrame(f);
}
public void flush() throws IOException {
out.flush();
}
public long getRemainingCapacity() {
return out.getRemainingCapacity();
}
}

View File

@@ -0,0 +1,15 @@
package net.sf.briar.transport;
import java.io.IOException;
interface OutgoingReliabilityLayer {
/** Writes the given frame. */
void writeFrame(Frame f) throws IOException;
/** Flushes the stack. */
void flush() throws IOException;
/** Returns the maximum number of bytes that can be written. */
long getRemainingCapacity();
}

View File

@@ -217,13 +217,14 @@ public class ConnectionReaderImplTest extends TransportTest {
}
private ConnectionReader createConnectionReader(InputStream in) {
IncomingEncryptionLayer decrypter = new NullIncomingEncryptionLayer(in);
IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
IncomingAuthenticationLayer authenticator =
new IncomingAuthenticationLayerImpl(correcter, mac, macKey);
IncomingEncryptionLayer encryption =
new NullIncomingEncryptionLayer(in);
IncomingErrorCorrectionLayer correction =
new NullIncomingErrorCorrectionLayer(encryption);
IncomingAuthenticationLayer authentication =
new IncomingAuthenticationLayerImpl(correction, mac, macKey);
IncomingReliabilityLayer reliability =
new NullIncomingReliabilityLayer(authenticator);
new NullIncomingReliabilityLayer(authentication);
return new ConnectionReaderImpl(reliability, false);
}
}

View File

@@ -12,6 +12,7 @@ import net.sf.briar.api.transport.ConnectionWriter;
import org.junit.Test;
// FIXME: This test covers too many classes
public class ConnectionWriterImplTest extends TransportTest {
public ConnectionWriterImplTest() throws Exception {
@@ -21,11 +22,7 @@ public class ConnectionWriterImplTest extends TransportTest {
@Test
public void testFlushWithoutWriteProducesNothing() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutgoingEncryptionLayer encrypter =
new NullOutgoingEncryptionLayer(out);
OutgoingErrorCorrectionLayer correcter =
new NullOutgoingErrorCorrectionLayer(encrypter);
ConnectionWriter w = new ConnectionWriterImpl(correcter, mac, macKey);
ConnectionWriter w = createConnectionWriter(out);
w.getOutputStream().flush();
w.getOutputStream().flush();
w.getOutputStream().flush();
@@ -44,11 +41,7 @@ public class ConnectionWriterImplTest extends TransportTest {
mac.doFinal(frame, FRAME_HEADER_LENGTH + payloadLength);
// Check that the ConnectionWriter gets the same results
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutgoingEncryptionLayer encrypter =
new NullOutgoingEncryptionLayer(out);
OutgoingErrorCorrectionLayer correcter =
new NullOutgoingErrorCorrectionLayer(encrypter);
ConnectionWriter w = new ConnectionWriterImpl(correcter, mac, macKey);
ConnectionWriter w = createConnectionWriter(out);
w.getOutputStream().write(0);
w.getOutputStream().flush();
assertArrayEquals(frame, out.toByteArray());
@@ -57,11 +50,7 @@ public class ConnectionWriterImplTest extends TransportTest {
@Test
public void testWriteByteToMaxLengthWritesFrame() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutgoingEncryptionLayer encrypter =
new NullOutgoingEncryptionLayer(out);
OutgoingErrorCorrectionLayer correcter =
new NullOutgoingErrorCorrectionLayer(encrypter);
ConnectionWriter w = new ConnectionWriterImpl(correcter, mac, macKey);
ConnectionWriter w = createConnectionWriter(out);
OutputStream out1 = w.getOutputStream();
// The first maxPayloadLength - 1 bytes should be buffered
for(int i = 0; i < MAX_PAYLOAD_LENGTH - 1; i++) out1.write(0);
@@ -74,11 +63,7 @@ public class ConnectionWriterImplTest extends TransportTest {
@Test
public void testWriteArrayToMaxLengthWritesFrame() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutgoingEncryptionLayer encrypter =
new NullOutgoingEncryptionLayer(out);
OutgoingErrorCorrectionLayer correcter =
new NullOutgoingErrorCorrectionLayer(encrypter);
ConnectionWriter w = new ConnectionWriterImpl(correcter, mac, macKey);
ConnectionWriter w = createConnectionWriter(out);
OutputStream out1 = w.getOutputStream();
// The first maxPayloadLength - 1 bytes should be buffered
out1.write(new byte[MAX_PAYLOAD_LENGTH - 1]);
@@ -112,11 +97,7 @@ public class ConnectionWriterImplTest extends TransportTest {
byte[] expected = out.toByteArray();
// Check that the ConnectionWriter gets the same results
out.reset();
OutgoingEncryptionLayer encrypter =
new NullOutgoingEncryptionLayer(out);
OutgoingErrorCorrectionLayer correcter =
new NullOutgoingErrorCorrectionLayer(encrypter);
ConnectionWriter w = new ConnectionWriterImpl(correcter, mac, macKey);
ConnectionWriter w = createConnectionWriter(out);
w.getOutputStream().write(new byte[123]);
w.getOutputStream().flush();
w.getOutputStream().write(new byte[1234]);
@@ -124,4 +105,16 @@ public class ConnectionWriterImplTest extends TransportTest {
byte[] actual = out.toByteArray();
assertArrayEquals(expected, actual);
}
private ConnectionWriter createConnectionWriter(OutputStream out) {
OutgoingEncryptionLayer encryption =
new NullOutgoingEncryptionLayer(out);
OutgoingErrorCorrectionLayer correction =
new NullOutgoingErrorCorrectionLayer(encryption);
OutgoingAuthenticationLayer authentication =
new OutgoingAuthenticationLayerImpl(correction, mac, macKey);
OutgoingReliabilityLayer reliability =
new NullOutgoingReliabilityLayer(authentication);
return new ConnectionWriterImpl(reliability);
}
}

View File

@@ -74,13 +74,16 @@ public class FrameReadWriteTest extends BriarTestCase {
ErasableKey macCopy = macKey.copy();
// Write the frames
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutgoingEncryptionLayer encrypter = new OutgoingEncryptionLayerImpl(out,
Long.MAX_VALUE, tagCipher, segCipher, tagCopy, segCopy,
OutgoingEncryptionLayer encryptionOut = new OutgoingEncryptionLayerImpl(
out, Long.MAX_VALUE, tagCipher, segCipher, tagCopy, segCopy,
false);
OutgoingErrorCorrectionLayer correcter =
new NullOutgoingErrorCorrectionLayer(encrypter);
ConnectionWriter writer = new ConnectionWriterImpl(correcter, mac,
macCopy);
OutgoingErrorCorrectionLayer correctionOut =
new NullOutgoingErrorCorrectionLayer(encryptionOut);
OutgoingAuthenticationLayer authenticationOut =
new OutgoingAuthenticationLayerImpl(correctionOut, mac, macCopy);
OutgoingReliabilityLayer reliabilityOut =
new NullOutgoingReliabilityLayer(authenticationOut);
ConnectionWriter writer = new ConnectionWriterImpl(reliabilityOut);
OutputStream out1 = writer.getOutputStream();
out1.write(frame);
out1.flush();
@@ -93,15 +96,16 @@ public class FrameReadWriteTest extends BriarTestCase {
assertArrayEquals(tag, recoveredTag);
assertEquals(0L, TagEncoder.decodeTag(tag, tagCipher, tagKey));
// Read the frames back
IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
tagCipher, segCipher, tagKey, segKey, false, recoveredTag);
IncomingErrorCorrectionLayer correcter1 =
new NullIncomingErrorCorrectionLayer(decrypter);
IncomingAuthenticationLayer authenticator =
new IncomingAuthenticationLayerImpl(correcter1, mac, macKey);
IncomingReliabilityLayer reliability =
new NullIncomingReliabilityLayer(authenticator);
ConnectionReader reader = new ConnectionReaderImpl(reliability, false);
IncomingEncryptionLayer encryptionIn = new IncomingEncryptionLayerImpl(
in, tagCipher, segCipher, tagKey, segKey, false, recoveredTag);
IncomingErrorCorrectionLayer correctionIn =
new NullIncomingErrorCorrectionLayer(encryptionIn);
IncomingAuthenticationLayer authenticationIn =
new IncomingAuthenticationLayerImpl(correctionIn, mac, macKey);
IncomingReliabilityLayer reliabilityIn =
new NullIncomingReliabilityLayer(authenticationIn);
ConnectionReader reader = new ConnectionReaderImpl(reliabilityIn,
false);
InputStream in1 = reader.getInputStream();
byte[] recovered = new byte[frame.length];
int offset = 0;