mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 12:19:54 +01:00
Plan B: Remove error correction and reliability layers and the
consequent distinction between segments and frames.
This commit is contained in:
@@ -9,10 +9,10 @@ import javax.crypto.Mac;
|
|||||||
|
|
||||||
public interface CryptoComponent {
|
public interface CryptoComponent {
|
||||||
|
|
||||||
ErasableKey deriveSegmentKey(byte[] secret, boolean initiator);
|
|
||||||
|
|
||||||
ErasableKey deriveTagKey(byte[] secret, boolean initiator);
|
ErasableKey deriveTagKey(byte[] secret, boolean initiator);
|
||||||
|
|
||||||
|
ErasableKey deriveFrameKey(byte[] secret, boolean initiator);
|
||||||
|
|
||||||
ErasableKey deriveMacKey(byte[] secret, boolean initiator);
|
ErasableKey deriveMacKey(byte[] secret, boolean initiator);
|
||||||
|
|
||||||
byte[] deriveNextSecret(byte[] secret, int index, long connection);
|
byte[] deriveNextSecret(byte[] secret, int index, long connection);
|
||||||
@@ -27,11 +27,11 @@ public interface CryptoComponent {
|
|||||||
|
|
||||||
SecureRandom getSecureRandom();
|
SecureRandom getSecureRandom();
|
||||||
|
|
||||||
Cipher getSegmentCipher();
|
Cipher getTagCipher();
|
||||||
|
|
||||||
|
Cipher getFrameCipher();
|
||||||
|
|
||||||
Signature getSignature();
|
Signature getSignature();
|
||||||
|
|
||||||
Cipher getTagCipher();
|
|
||||||
|
|
||||||
Mac getMac();
|
Mac getMac();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
package net.sf.briar.api.plugins;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
public interface SegmentSink {
|
|
||||||
|
|
||||||
/** Writes the given segment. */
|
|
||||||
void writeSegment(Segment s) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the maximum length in bytes of the segments this sink accepts.
|
|
||||||
*/
|
|
||||||
int getMaxSegmentLength();
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package net.sf.briar.api.plugins;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
public interface SegmentSource {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to read a segment into the given buffer and returns true if a
|
|
||||||
* segment was read, or false if no more segments can be read.
|
|
||||||
*/
|
|
||||||
boolean readSegment(Segment s) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the maximum length in bytes of the segments this source returns.
|
|
||||||
*/
|
|
||||||
int getMaxSegmentLength();
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package net.sf.briar.api.plugins.duplex;
|
|
||||||
|
|
||||||
import net.sf.briar.api.ContactId;
|
|
||||||
import net.sf.briar.api.plugins.Plugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for transport plugins that support duplex segmented
|
|
||||||
* communication.
|
|
||||||
*/
|
|
||||||
public interface DuplexSegmentedPlugin extends Plugin {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to create and return a connection to the given contact using
|
|
||||||
* the current transport and configuration properties. Returns null if a
|
|
||||||
* connection could not be created.
|
|
||||||
*/
|
|
||||||
DuplexSegmentedTransportConnection createConnection(ContactId c);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the invitation process from the inviter's side. Returns null if
|
|
||||||
* no connection can be established within the given timeout.
|
|
||||||
*/
|
|
||||||
DuplexSegmentedTransportConnection sendInvitation(int code, long timeout);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the invitation process from the invitee's side. Returns null if
|
|
||||||
* no connection can be established within the given timeout.
|
|
||||||
*/
|
|
||||||
DuplexSegmentedTransportConnection acceptInvitation(int code, long timeout);
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package net.sf.briar.api.plugins.duplex;
|
|
||||||
|
|
||||||
import net.sf.briar.api.ContactId;
|
|
||||||
import net.sf.briar.api.plugins.PluginCallback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for handling connections created by a duplex segmented
|
|
||||||
* transport plugin.
|
|
||||||
*/
|
|
||||||
public interface DuplexSegmentedPluginCallback extends PluginCallback {
|
|
||||||
|
|
||||||
void incomingConnectionCreated(DuplexSegmentedTransportConnection d);
|
|
||||||
|
|
||||||
void outgoingConnectionCreated(ContactId c,
|
|
||||||
DuplexSegmentedTransportConnection d);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package net.sf.briar.api.plugins.duplex;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
public interface DuplexSegmentedPluginFactory {
|
|
||||||
|
|
||||||
DuplexSegmentedPlugin createPlugin(Executor pluginExecutor,
|
|
||||||
DuplexSegmentedPluginCallback callback);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package net.sf.briar.api.plugins.duplex;
|
|
||||||
|
|
||||||
import net.sf.briar.api.plugins.SegmentSink;
|
|
||||||
import net.sf.briar.api.plugins.SegmentSource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for reading and writing data over a duplex segmented transport.
|
|
||||||
* The connection is not responsible for encrypting/decrypting or authenticating
|
|
||||||
* the data.
|
|
||||||
*/
|
|
||||||
public interface DuplexSegmentedTransportConnection extends SegmentSource,
|
|
||||||
SegmentSink {
|
|
||||||
|
|
||||||
/** Returns the maximum length of a segment in bytes. */
|
|
||||||
int getMaximumSegmentLength();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the output stream should be flushed after each packet.
|
|
||||||
*/
|
|
||||||
boolean shouldFlush();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the connection and disposes of any associated resources. The
|
|
||||||
* first argument indicates whether the connection is being closed because
|
|
||||||
* of an exception and the second argument indicates whether the connection
|
|
||||||
* was recognised, which may affect how resources are disposed of.
|
|
||||||
*/
|
|
||||||
void dispose(boolean exception, boolean recognised);
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package net.sf.briar.api.plugins.simplex;
|
|
||||||
|
|
||||||
import net.sf.briar.api.ContactId;
|
|
||||||
import net.sf.briar.api.plugins.Plugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for transport plugins that support simplex segmented
|
|
||||||
* communication.
|
|
||||||
*/
|
|
||||||
public interface SimplexSegmentedPlugin extends Plugin {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to create and return a reader for the given contact using the
|
|
||||||
* current transport and configuration properties. Returns null if a reader
|
|
||||||
* could not be created.
|
|
||||||
*/
|
|
||||||
SimplexSegmentedTransportReader createReader(ContactId c);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to create and return a writer for the given contact using the
|
|
||||||
* current transport and configuration properties. Returns null if a writer
|
|
||||||
* could not be created.
|
|
||||||
*/
|
|
||||||
SimplexSegmentedTransportWriter createWriter(ContactId c);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the invitation process from the inviter's side. Returns null if
|
|
||||||
* no connection can be established within the given timeout.
|
|
||||||
*/
|
|
||||||
SimplexSegmentedTransportWriter sendInvitation(int code, long timeout);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the invitation process from the invitee's side. Returns null if
|
|
||||||
* no connection can be established within the given timeout.
|
|
||||||
*/
|
|
||||||
SimplexSegmentedTransportReader acceptInvitation(int code, long timeout);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Continues the invitation process from the invitee's side. Returns null
|
|
||||||
* if no connection can be established within the given timeout.
|
|
||||||
*/
|
|
||||||
SimplexSegmentedTransportWriter sendInvitationResponse(int code,
|
|
||||||
long timeout);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Continues the invitation process from the inviter's side. Returns null
|
|
||||||
* if no connection can be established within the given timeout.
|
|
||||||
*/
|
|
||||||
SimplexSegmentedTransportReader acceptInvitationResponse(int code,
|
|
||||||
long timeout);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package net.sf.briar.api.plugins.simplex;
|
|
||||||
|
|
||||||
import net.sf.briar.api.ContactId;
|
|
||||||
import net.sf.briar.api.plugins.PluginCallback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for handling readers and writers created by a simplex
|
|
||||||
* segmented transport plugin.
|
|
||||||
*/
|
|
||||||
public interface SimplexSegmentedPluginCallback extends PluginCallback {
|
|
||||||
|
|
||||||
void readerCreated(SimplexSegmentedTransportReader r);
|
|
||||||
|
|
||||||
void writerCreated(ContactId c, SimplexSegmentedTransportWriter w);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package net.sf.briar.api.plugins.simplex;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
public interface SimplexSegmentedPluginFactory {
|
|
||||||
|
|
||||||
SimplexSegmentedPlugin createPlugin(Executor pluginExecutor,
|
|
||||||
SimplexSegmentedPluginCallback callback);
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package net.sf.briar.api.plugins.simplex;
|
|
||||||
|
|
||||||
import net.sf.briar.api.plugins.SegmentSource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for reading data from a simplex segmented transport. The reader
|
|
||||||
* is not responsible for decrypting or authenticating the data before
|
|
||||||
* returning it.
|
|
||||||
*/
|
|
||||||
public interface SimplexSegmentedTransportReader extends SegmentSource {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the reader and disposes of any associated resources. The first
|
|
||||||
* argument indicates whether the reader is being closed because of an
|
|
||||||
* exception and the second argument indicates whether the connection was
|
|
||||||
* recognised, which may affect how resources are disposed of.
|
|
||||||
*/
|
|
||||||
void dispose(boolean exception, boolean recognised);
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package net.sf.briar.api.plugins.simplex;
|
|
||||||
|
|
||||||
import net.sf.briar.api.plugins.SegmentSink;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for writing data to a simplex segmented transport. The writer is
|
|
||||||
* not responsible for authenticating or encrypting the data before writing it.
|
|
||||||
*/
|
|
||||||
public interface SimplexSegmentedTransportWriter extends SegmentSink {
|
|
||||||
|
|
||||||
/** Returns the capacity of the transport in bytes. */
|
|
||||||
long getCapacity();
|
|
||||||
|
|
||||||
/** Returns the maximum length of a segment in bytes. */
|
|
||||||
int getMaximumSegmentLength();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the output stream should be flushed after each packet.
|
|
||||||
*/
|
|
||||||
boolean shouldFlush();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the writer and disposes of any associated resources. The
|
|
||||||
* argument indicates whether the writer is being closed because of an
|
|
||||||
* exception, which may affect how resources are disposed of.
|
|
||||||
*/
|
|
||||||
void dispose(boolean exception);
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,7 @@ import net.sf.briar.api.transport.ConnectionContext;
|
|||||||
public interface DuplexConnectionFactory {
|
public interface DuplexConnectionFactory {
|
||||||
|
|
||||||
void createIncomingConnection(ConnectionContext ctx, TransportId t,
|
void createIncomingConnection(ConnectionContext ctx, TransportId t,
|
||||||
DuplexTransportConnection d, byte[] tag);
|
DuplexTransportConnection d);
|
||||||
|
|
||||||
void createOutgoingConnection(ContactId c, TransportId t, TransportIndex i,
|
void createOutgoingConnection(ContactId c, TransportId t, TransportIndex i,
|
||||||
DuplexTransportConnection d);
|
DuplexTransportConnection d);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import net.sf.briar.api.transport.ConnectionContext;
|
|||||||
public interface SimplexConnectionFactory {
|
public interface SimplexConnectionFactory {
|
||||||
|
|
||||||
void createIncomingConnection(ConnectionContext ctx, TransportId t,
|
void createIncomingConnection(ConnectionContext ctx, TransportId t,
|
||||||
SimplexTransportReader r, byte[] tag);
|
SimplexTransportReader r);
|
||||||
|
|
||||||
void createOutgoingConnection(ContactId c, TransportId t, TransportIndex i,
|
void createOutgoingConnection(ContactId c, TransportId t, TransportIndex i,
|
||||||
SimplexTransportWriter w);
|
SimplexTransportWriter w);
|
||||||
|
|||||||
@@ -2,35 +2,12 @@ package net.sf.briar.api.transport;
|
|||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import net.sf.briar.api.plugins.SegmentSource;
|
|
||||||
|
|
||||||
public interface ConnectionReaderFactory {
|
public interface ConnectionReaderFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a connection reader for a simplex connection or the initiator's
|
* Creates a connection reader for a simplex connection or one side of a
|
||||||
* side of a duplex connection. The secret is erased before this method
|
* duplex connection. The secret is erased before this method returns.
|
||||||
* returns.
|
|
||||||
*/
|
*/
|
||||||
ConnectionReader createConnectionReader(InputStream in, byte[] secret,
|
ConnectionReader createConnectionReader(InputStream in, byte[] secret,
|
||||||
byte[] bufferedTag);
|
boolean initiator);
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a connection reader for a simplex connection or the initiator's
|
|
||||||
* side of a duplex connection. The secret is erased before this method
|
|
||||||
* returns.
|
|
||||||
*/
|
|
||||||
ConnectionReader createConnectionReader(SegmentSource in, byte[] secret,
|
|
||||||
Segment bufferedSegment);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a connection reader for the responder's side of a duplex
|
|
||||||
* connection. The secret is erased before this method returns.
|
|
||||||
*/
|
|
||||||
ConnectionReader createConnectionReader(InputStream in, byte[] secret);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a connection reader for the responder's side of a duplex
|
|
||||||
* connection. The secret is erased before this method returns.
|
|
||||||
*/
|
|
||||||
ConnectionReader createConnectionReader(SegmentSource in, byte[] secret);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package net.sf.briar.api.transport;
|
|||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import net.sf.briar.api.plugins.SegmentSink;
|
|
||||||
|
|
||||||
public interface ConnectionWriterFactory {
|
public interface ConnectionWriterFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,11 +10,4 @@ public interface ConnectionWriterFactory {
|
|||||||
*/
|
*/
|
||||||
ConnectionWriter createConnectionWriter(OutputStream out, long capacity,
|
ConnectionWriter createConnectionWriter(OutputStream out, long capacity,
|
||||||
byte[] secret, boolean initiator);
|
byte[] secret, boolean initiator);
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a connection writer for a simplex connection or one side of a
|
|
||||||
* duplex connection. The secret is erased before this method returns.
|
|
||||||
*/
|
|
||||||
ConnectionWriter createConnectionWriter(SegmentSink out, long capacity,
|
|
||||||
byte[] secret, boolean initiator);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package net.sf.briar.api.transport;
|
|
||||||
|
|
||||||
public interface Segment {
|
|
||||||
|
|
||||||
byte[] getBuffer();
|
|
||||||
|
|
||||||
int getLength();
|
|
||||||
|
|
||||||
long getSegmentNumber();
|
|
||||||
|
|
||||||
void setLength(int length);
|
|
||||||
|
|
||||||
void setSegmentNumber(long segmentNumber);
|
|
||||||
}
|
|
||||||
@@ -2,21 +2,15 @@ package net.sf.briar.api.transport;
|
|||||||
|
|
||||||
public interface TransportConstants {
|
public interface TransportConstants {
|
||||||
|
|
||||||
/** The maximum length of a segment in bytes, including the tag. */
|
/** The length of the connection tag in bytes. */
|
||||||
static final int MAX_SEGMENT_LENGTH = 65536; // 2^16, 64 KiB
|
|
||||||
|
|
||||||
/** The length of the segment tag in bytes. */
|
|
||||||
static final int TAG_LENGTH = 16;
|
static final int TAG_LENGTH = 16;
|
||||||
|
|
||||||
/** The maximum length of a frame in bytes, including the header and MAC. */
|
/** The maximum length of a frame in bytes, including the header and MAC. */
|
||||||
static final int MAX_FRAME_LENGTH = MAX_SEGMENT_LENGTH - TAG_LENGTH;
|
static final int MAX_FRAME_LENGTH = 65536; // 2^16, 64 KiB
|
||||||
|
|
||||||
/** The length of the frame header in bytes. */
|
/** The length of the frame header in bytes. */
|
||||||
static final int FRAME_HEADER_LENGTH = 8;
|
static final int FRAME_HEADER_LENGTH = 8;
|
||||||
|
|
||||||
/** The length of the ack header in bytes. */
|
|
||||||
static final int ACK_HEADER_LENGTH = 5;
|
|
||||||
|
|
||||||
/** The length of the MAC in bytes. */
|
/** The length of the MAC in bytes. */
|
||||||
static final int MAC_LENGTH = 32;
|
static final int MAC_LENGTH = 32;
|
||||||
|
|
||||||
@@ -29,7 +23,4 @@ public interface TransportConstants {
|
|||||||
|
|
||||||
/** The size of the connection reordering window. */
|
/** The size of the connection reordering window. */
|
||||||
static final int CONNECTION_WINDOW_SIZE = 32;
|
static final int CONNECTION_WINDOW_SIZE = 32;
|
||||||
|
|
||||||
/** The size of the frame reordering window. */
|
|
||||||
static final int FRAME_WINDOW_SIZE = 32;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
private static final String DIGEST_ALGO = "SHA-256";
|
private static final String DIGEST_ALGO = "SHA-256";
|
||||||
private static final String SIGNATURE_ALGO = "ECDSA";
|
private static final String SIGNATURE_ALGO = "ECDSA";
|
||||||
private static final String TAG_CIPHER_ALGO = "AES/ECB/NoPadding";
|
private static final String TAG_CIPHER_ALGO = "AES/ECB/NoPadding";
|
||||||
private static final String SEGMENT_CIPHER_ALGO = "AES/CTR/NoPadding";
|
private static final String FRAME_CIPHER_ALGO = "AES/CTR/NoPadding";
|
||||||
private static final String MAC_ALGO = "HMacSHA256";
|
private static final String MAC_ALGO = "HMacSHA256";
|
||||||
|
|
||||||
// Labels for key derivation, null-terminated
|
// Labels for key derivation, null-terminated
|
||||||
private static final byte[] TAG = { 'T', 'A', 'G', 0 };
|
private static final byte[] TAG = { 'T', 'A', 'G', 0 };
|
||||||
private static final byte[] SEGMENT = { 'S', 'E', 'G', 0 };
|
private static final byte[] FRAME = { 'F', 'R', 'A', 'M', 'E', 0 };
|
||||||
private static final byte[] MAC = { 'M', 'A', 'C', 0 };
|
private static final byte[] MAC = { 'M', 'A', 'C', 0 };
|
||||||
private static final byte[] NEXT = { 'N', 'E', 'X', 'T', 0 };
|
private static final byte[] NEXT = { 'N', 'E', 'X', 'T', 0 };
|
||||||
// Context strings for key derivation
|
// Context strings for key derivation
|
||||||
@@ -71,9 +71,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
else return deriveKey(secret, TAG, RESPONDER);
|
else return deriveKey(secret, TAG, RESPONDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErasableKey deriveSegmentKey(byte[] secret, boolean initiator) {
|
public ErasableKey deriveFrameKey(byte[] secret, boolean initiator) {
|
||||||
if(initiator) return deriveKey(secret, SEGMENT, INITIATOR);
|
if(initiator) return deriveKey(secret, FRAME, INITIATOR);
|
||||||
else return deriveKey(secret, SEGMENT, RESPONDER);
|
else return deriveKey(secret, FRAME, RESPONDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErasableKey deriveMacKey(byte[] secret, boolean initiator) {
|
public ErasableKey deriveMacKey(byte[] secret, boolean initiator) {
|
||||||
@@ -168,9 +168,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cipher getSegmentCipher() {
|
public Cipher getFrameCipher() {
|
||||||
try {
|
try {
|
||||||
return Cipher.getInstance(SEGMENT_CIPHER_ALGO, PROVIDER);
|
return Cipher.getInstance(FRAME_CIPHER_ALGO, PROVIDER);
|
||||||
} catch(GeneralSecurityException e) {
|
} catch(GeneralSecurityException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import net.sf.briar.api.transport.ConnectionWriterFactory;
|
|||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
class StreamConnectionFactoryImpl implements DuplexConnectionFactory {
|
class DuplexConnectionFactoryImpl implements DuplexConnectionFactory {
|
||||||
|
|
||||||
private final Executor dbExecutor, verificationExecutor;
|
private final Executor dbExecutor, verificationExecutor;
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
@@ -30,7 +30,7 @@ class StreamConnectionFactoryImpl implements DuplexConnectionFactory {
|
|||||||
private final ProtocolWriterFactory protoWriterFactory;
|
private final ProtocolWriterFactory protoWriterFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
StreamConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor,
|
DuplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor,
|
||||||
@VerificationExecutor Executor verificationExecutor,
|
@VerificationExecutor Executor verificationExecutor,
|
||||||
DatabaseComponent db, ConnectionRegistry connRegistry,
|
DatabaseComponent db, ConnectionRegistry connRegistry,
|
||||||
ConnectionReaderFactory connReaderFactory,
|
ConnectionReaderFactory connReaderFactory,
|
||||||
@@ -48,11 +48,11 @@ class StreamConnectionFactoryImpl implements DuplexConnectionFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void createIncomingConnection(ConnectionContext ctx, TransportId t,
|
public void createIncomingConnection(ConnectionContext ctx, TransportId t,
|
||||||
DuplexTransportConnection d, byte[] tag) {
|
DuplexTransportConnection d) {
|
||||||
final DuplexConnection conn = new IncomingDuplexConnection(dbExecutor,
|
final DuplexConnection conn = new IncomingDuplexConnection(dbExecutor,
|
||||||
verificationExecutor, db, connRegistry, connReaderFactory,
|
verificationExecutor, db, connRegistry, connReaderFactory,
|
||||||
connWriterFactory, protoReaderFactory, protoWriterFactory,
|
connWriterFactory, protoReaderFactory, protoWriterFactory,
|
||||||
ctx, t, d, tag);
|
ctx, t, d);
|
||||||
Runnable write = new Runnable() {
|
Runnable write = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
conn.write();
|
conn.write();
|
||||||
@@ -10,6 +10,6 @@ public class DuplexProtocolModule extends AbstractModule {
|
|||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(DuplexConnectionFactory.class).to(
|
bind(DuplexConnectionFactory.class).to(
|
||||||
StreamConnectionFactoryImpl.class).in(Singleton.class);
|
DuplexConnectionFactoryImpl.class).in(Singleton.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import net.sf.briar.api.transport.ConnectionWriterFactory;
|
|||||||
class IncomingDuplexConnection extends DuplexConnection {
|
class IncomingDuplexConnection extends DuplexConnection {
|
||||||
|
|
||||||
private final ConnectionContext ctx;
|
private final ConnectionContext ctx;
|
||||||
private final byte[] tag;
|
|
||||||
|
|
||||||
IncomingDuplexConnection(@DatabaseExecutor Executor dbExecutor,
|
IncomingDuplexConnection(@DatabaseExecutor Executor dbExecutor,
|
||||||
@VerificationExecutor Executor verificationExecutor,
|
@VerificationExecutor Executor verificationExecutor,
|
||||||
@@ -30,18 +29,17 @@ class IncomingDuplexConnection extends DuplexConnection {
|
|||||||
ProtocolReaderFactory protoReaderFactory,
|
ProtocolReaderFactory protoReaderFactory,
|
||||||
ProtocolWriterFactory protoWriterFactory,
|
ProtocolWriterFactory protoWriterFactory,
|
||||||
ConnectionContext ctx, TransportId transportId,
|
ConnectionContext ctx, TransportId transportId,
|
||||||
DuplexTransportConnection transport, byte[] tag) {
|
DuplexTransportConnection transport) {
|
||||||
super(dbExecutor, verificationExecutor, db, connRegistry,
|
super(dbExecutor, verificationExecutor, db, connRegistry,
|
||||||
connReaderFactory, connWriterFactory, protoReaderFactory,
|
connReaderFactory, connWriterFactory, protoReaderFactory,
|
||||||
protoWriterFactory, ctx.getContactId(), transportId, transport);
|
protoWriterFactory, ctx.getContactId(), transportId, transport);
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.tag = tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ConnectionReader createConnectionReader() throws IOException {
|
protected ConnectionReader createConnectionReader() throws IOException {
|
||||||
return connReaderFactory.createConnectionReader(
|
return connReaderFactory.createConnectionReader(
|
||||||
transport.getInputStream(), ctx.getSecret(), tag);
|
transport.getInputStream(), ctx.getSecret(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class OutgoingDuplexConnection extends DuplexConnection {
|
|||||||
ctx = db.getConnectionContext(contactId, transportIndex);
|
ctx = db.getConnectionContext(contactId, transportIndex);
|
||||||
}
|
}
|
||||||
return connReaderFactory.createConnectionReader(
|
return connReaderFactory.createConnectionReader(
|
||||||
transport.getInputStream(), ctx.getSecret());
|
transport.getInputStream(), ctx.getSecret(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ class IncomingSimplexConnection {
|
|||||||
private final ConnectionContext ctx;
|
private final ConnectionContext ctx;
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final SimplexTransportReader transport;
|
private final SimplexTransportReader transport;
|
||||||
private final byte[] tag;
|
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
|
|
||||||
IncomingSimplexConnection(@DatabaseExecutor Executor dbExecutor,
|
IncomingSimplexConnection(@DatabaseExecutor Executor dbExecutor,
|
||||||
@@ -48,8 +47,7 @@ class IncomingSimplexConnection {
|
|||||||
DatabaseComponent db, ConnectionRegistry connRegistry,
|
DatabaseComponent db, ConnectionRegistry connRegistry,
|
||||||
ConnectionReaderFactory connFactory,
|
ConnectionReaderFactory connFactory,
|
||||||
ProtocolReaderFactory protoFactory, ConnectionContext ctx,
|
ProtocolReaderFactory protoFactory, ConnectionContext ctx,
|
||||||
TransportId transportId, SimplexTransportReader transport,
|
TransportId transportId, SimplexTransportReader transport) {
|
||||||
byte[] tag) {
|
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.verificationExecutor = verificationExecutor;
|
this.verificationExecutor = verificationExecutor;
|
||||||
this.db = db;
|
this.db = db;
|
||||||
@@ -59,7 +57,6 @@ class IncomingSimplexConnection {
|
|||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.transportId = transportId;
|
this.transportId = transportId;
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
this.tag = tag;
|
|
||||||
contactId = ctx.getContactId();
|
contactId = ctx.getContactId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +64,7 @@ class IncomingSimplexConnection {
|
|||||||
connRegistry.registerConnection(contactId, transportId);
|
connRegistry.registerConnection(contactId, transportId);
|
||||||
try {
|
try {
|
||||||
ConnectionReader conn = connFactory.createConnectionReader(
|
ConnectionReader conn = connFactory.createConnectionReader(
|
||||||
transport.getInputStream(), ctx.getSecret(), tag);
|
transport.getInputStream(), ctx.getSecret(), true);
|
||||||
InputStream in = conn.getInputStream();
|
InputStream in = conn.getInputStream();
|
||||||
ProtocolReader reader = protoFactory.createProtocolReader(in);
|
ProtocolReader reader = protoFactory.createProtocolReader(in);
|
||||||
// Read packets until EOF
|
// Read packets until EOF
|
||||||
|
|||||||
@@ -49,10 +49,10 @@ class SimplexConnectionFactoryImpl implements SimplexConnectionFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void createIncomingConnection(ConnectionContext ctx, TransportId t,
|
public void createIncomingConnection(ConnectionContext ctx, TransportId t,
|
||||||
SimplexTransportReader r, byte[] tag) {
|
SimplexTransportReader r) {
|
||||||
final IncomingSimplexConnection conn = new IncomingSimplexConnection(
|
final IncomingSimplexConnection conn = new IncomingSimplexConnection(
|
||||||
dbExecutor, verificationExecutor, db, connRegistry,
|
dbExecutor, verificationExecutor, db, connRegistry,
|
||||||
connReaderFactory, protoReaderFactory, ctx, t, r, tag);
|
connReaderFactory, protoReaderFactory, ctx, t, r);
|
||||||
Runnable read = new Runnable() {
|
Runnable read = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
conn.read();
|
conn.read();
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class ConnectionDispatcherImpl implements ConnectionDispatcher {
|
|||||||
tag);
|
tag);
|
||||||
if(ctx == null) transport.dispose(false, false);
|
if(ctx == null) transport.dispose(false, false);
|
||||||
else batchConnFactory.createIncomingConnection(ctx, transportId,
|
else batchConnFactory.createIncomingConnection(ctx, transportId,
|
||||||
transport, tag);
|
transport);
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
|
||||||
transport.dispose(true, false);
|
transport.dispose(true, false);
|
||||||
@@ -122,7 +122,7 @@ class ConnectionDispatcherImpl implements ConnectionDispatcher {
|
|||||||
tag);
|
tag);
|
||||||
if(ctx == null) transport.dispose(false, false);
|
if(ctx == null) transport.dispose(false, false);
|
||||||
else streamConnFactory.createIncomingConnection(ctx,
|
else streamConnFactory.createIncomingConnection(ctx,
|
||||||
transportId, transport, tag);
|
transportId, transport);
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
|
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
|
||||||
transport.dispose(true, false);
|
transport.dispose(true, false);
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ import javax.crypto.Mac;
|
|||||||
|
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
import net.sf.briar.api.crypto.CryptoComponent;
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
import net.sf.briar.api.plugins.SegmentSource;
|
|
||||||
import net.sf.briar.api.transport.ConnectionReader;
|
import net.sf.briar.api.transport.ConnectionReader;
|
||||||
import net.sf.briar.api.transport.ConnectionReaderFactory;
|
import net.sf.briar.api.transport.ConnectionReaderFactory;
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
import net.sf.briar.util.ByteUtils;
|
import net.sf.briar.util.ByteUtils;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@@ -25,76 +23,22 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ConnectionReader createConnectionReader(InputStream in,
|
public ConnectionReader createConnectionReader(InputStream in,
|
||||||
byte[] secret, byte[] bufferedTag) {
|
byte[] secret, boolean initiator) {
|
||||||
return createConnectionReader(in, secret, bufferedTag, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionReader createConnectionReader(InputStream in,
|
|
||||||
byte[] secret) {
|
|
||||||
return createConnectionReader(in, secret, null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConnectionReader createConnectionReader(InputStream in,
|
|
||||||
byte[] secret, byte[] bufferedTag, boolean initiator) {
|
|
||||||
// Derive the keys and erase the secret
|
// Derive the keys and erase the secret
|
||||||
ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
|
ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
|
||||||
ErasableKey segKey = crypto.deriveSegmentKey(secret, initiator);
|
ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
|
||||||
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
|
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
|
||||||
ByteUtils.erase(secret);
|
ByteUtils.erase(secret);
|
||||||
// Create the decrypter
|
// Encryption
|
||||||
Cipher tagCipher = crypto.getTagCipher();
|
Cipher tagCipher = crypto.getTagCipher();
|
||||||
Cipher segCipher = crypto.getSegmentCipher();
|
Cipher frameCipher = crypto.getFrameCipher();
|
||||||
IncomingEncryptionLayer encryption = new IncomingEncryptionLayerImpl(in,
|
FrameReader encryption = new IncomingEncryptionLayerImpl(in, tagCipher,
|
||||||
tagCipher, segCipher, tagKey, segKey, false, false,
|
frameCipher, tagKey, frameKey, !initiator);
|
||||||
bufferedTag);
|
// Authentication
|
||||||
// No error correction
|
|
||||||
IncomingErrorCorrectionLayer correction =
|
|
||||||
new NullIncomingErrorCorrectionLayer(encryption);
|
|
||||||
// Create the authenticator
|
|
||||||
Mac mac = crypto.getMac();
|
Mac mac = crypto.getMac();
|
||||||
IncomingAuthenticationLayer authentication =
|
FrameReader authentication = new IncomingAuthenticationLayerImpl(
|
||||||
new IncomingAuthenticationLayerImpl(correction, mac, macKey, false);
|
encryption, mac, macKey);
|
||||||
// No reordering or retransmission
|
// Create the reader
|
||||||
IncomingReliabilityLayer reliability =
|
return new ConnectionReaderImpl(authentication);
|
||||||
new NullIncomingReliabilityLayer(authentication);
|
|
||||||
// Create the reader - don't tolerate errors
|
|
||||||
return new ConnectionReaderImpl(reliability, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionReader createConnectionReader(SegmentSource in,
|
|
||||||
byte[] secret, Segment bufferedSegment) {
|
|
||||||
return createConnectionReader(in, secret, bufferedSegment, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionReader createConnectionReader(SegmentSource in,
|
|
||||||
byte[] secret) {
|
|
||||||
return createConnectionReader(in, secret, null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConnectionReader createConnectionReader(SegmentSource in,
|
|
||||||
byte[] secret, Segment bufferedSegment, boolean initiator) {
|
|
||||||
// Derive the keys and erase the secret
|
|
||||||
ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
|
|
||||||
ErasableKey segKey = crypto.deriveSegmentKey(secret, initiator);
|
|
||||||
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
|
|
||||||
ByteUtils.erase(secret);
|
|
||||||
// Create the decrypter
|
|
||||||
Cipher tagCipher = crypto.getTagCipher();
|
|
||||||
Cipher segCipher = crypto.getSegmentCipher();
|
|
||||||
IncomingEncryptionLayer encryption =
|
|
||||||
new SegmentedIncomingEncryptionLayer(in, tagCipher, segCipher,
|
|
||||||
tagKey, segKey, false, false, bufferedSegment);
|
|
||||||
// No error correction
|
|
||||||
IncomingErrorCorrectionLayer correction =
|
|
||||||
new NullIncomingErrorCorrectionLayer(encryption);
|
|
||||||
// Create the authenticator
|
|
||||||
Mac mac = crypto.getMac();
|
|
||||||
IncomingAuthenticationLayer authentication =
|
|
||||||
new IncomingAuthenticationLayerImpl(correction, mac, macKey, false);
|
|
||||||
// No reordering or retransmission
|
|
||||||
IncomingReliabilityLayer reliability =
|
|
||||||
new NullIncomingReliabilityLayer(authentication);
|
|
||||||
// Create the reader - don't tolerate errors
|
|
||||||
return new ConnectionReaderImpl(reliability, false, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,23 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.ACK_HEADER_LENGTH;
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import net.sf.briar.api.FormatException;
|
|
||||||
import net.sf.briar.api.transport.ConnectionReader;
|
import net.sf.briar.api.transport.ConnectionReader;
|
||||||
|
|
||||||
class ConnectionReaderImpl extends InputStream implements ConnectionReader {
|
class ConnectionReaderImpl extends InputStream implements ConnectionReader {
|
||||||
|
|
||||||
private final IncomingReliabilityLayer in;
|
private final FrameReader in;
|
||||||
private final boolean tolerateErrors;
|
private final Frame frame;
|
||||||
private final int headerLength;
|
|
||||||
|
|
||||||
private Frame frame;
|
|
||||||
private int offset = 0, length = 0;
|
private int offset = 0, length = 0;
|
||||||
|
|
||||||
ConnectionReaderImpl(IncomingReliabilityLayer in, boolean tolerateErrors,
|
ConnectionReaderImpl(FrameReader in) {
|
||||||
boolean ackHeader) {
|
|
||||||
this.in = in;
|
this.in = in;
|
||||||
this.tolerateErrors = tolerateErrors;
|
frame = new Frame();
|
||||||
if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
|
offset = FRAME_HEADER_LENGTH;
|
||||||
else headerLength = FRAME_HEADER_LENGTH;
|
|
||||||
frame = new Frame(in.getMaxFrameLength());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
public InputStream getInputStream() {
|
||||||
@@ -60,19 +53,14 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
|
|||||||
private boolean readFrame() throws IOException {
|
private boolean readFrame() throws IOException {
|
||||||
assert length == 0;
|
assert length == 0;
|
||||||
while(true) {
|
while(true) {
|
||||||
try {
|
frame.reset();
|
||||||
frame = in.readFrame(frame);
|
if(!in.readFrame(frame)) {
|
||||||
if(frame == null) {
|
length = -1;
|
||||||
length = -1;
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
offset = headerLength;
|
|
||||||
length = HeaderEncoder.getPayloadLength(frame.getBuffer());
|
|
||||||
return true;
|
|
||||||
} catch(InvalidDataException e) {
|
|
||||||
if(tolerateErrors) continue;
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
}
|
||||||
|
offset = FRAME_HEADER_LENGTH;
|
||||||
|
length = HeaderEncoder.getPayloadLength(frame.getBuffer());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ DatabaseListener {
|
|||||||
private Bytes calculateTag(Context ctx, byte[] secret) {
|
private Bytes calculateTag(Context ctx, byte[] secret) {
|
||||||
ErasableKey tagKey = crypto.deriveTagKey(secret, true);
|
ErasableKey tagKey = crypto.deriveTagKey(secret, true);
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
|
TagEncoder.encodeTag(tag, tagCipher, tagKey);
|
||||||
tagKey.erase();
|
tagKey.erase();
|
||||||
return new Bytes(tag);
|
return new Bytes(tag);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import javax.crypto.Mac;
|
|||||||
|
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
import net.sf.briar.api.crypto.CryptoComponent;
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
import net.sf.briar.api.plugins.SegmentSink;
|
|
||||||
import net.sf.briar.api.transport.ConnectionWriter;
|
import net.sf.briar.api.transport.ConnectionWriter;
|
||||||
import net.sf.briar.api.transport.ConnectionWriterFactory;
|
import net.sf.briar.api.transport.ConnectionWriterFactory;
|
||||||
import net.sf.briar.util.ByteUtils;
|
import net.sf.briar.util.ByteUtils;
|
||||||
@@ -27,52 +26,19 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
|
|||||||
long capacity, byte[] secret, boolean initiator) {
|
long capacity, byte[] secret, boolean initiator) {
|
||||||
// Derive the keys and erase the secret
|
// Derive the keys and erase the secret
|
||||||
ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
|
ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
|
||||||
ErasableKey segKey = crypto.deriveSegmentKey(secret, initiator);
|
ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
|
||||||
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
|
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
|
||||||
ByteUtils.erase(secret);
|
ByteUtils.erase(secret);
|
||||||
// Create the encrypter
|
// Encryption
|
||||||
Cipher tagCipher = crypto.getTagCipher();
|
Cipher tagCipher = crypto.getTagCipher();
|
||||||
Cipher segCipher = crypto.getSegmentCipher();
|
Cipher frameCipher = crypto.getFrameCipher();
|
||||||
OutgoingEncryptionLayer encryption = new OutgoingEncryptionLayerImpl(
|
FrameWriter encryption = new OutgoingEncryptionLayerImpl(
|
||||||
out, capacity, tagCipher, segCipher, tagKey, segKey, false);
|
out, capacity, tagCipher, frameCipher, tagKey, frameKey);
|
||||||
// No error correction
|
|
||||||
OutgoingErrorCorrectionLayer correction =
|
|
||||||
new NullOutgoingErrorCorrectionLayer(encryption);
|
|
||||||
// Authentication
|
// Authentication
|
||||||
Mac mac = crypto.getMac();
|
Mac mac = crypto.getMac();
|
||||||
OutgoingAuthenticationLayer authentication =
|
FrameWriter authentication =
|
||||||
new OutgoingAuthenticationLayerImpl(correction, mac, macKey);
|
new OutgoingAuthenticationLayerImpl(encryption, mac, macKey);
|
||||||
// No retransmission
|
|
||||||
OutgoingReliabilityLayer reliability =
|
|
||||||
new NullOutgoingReliabilityLayer(authentication);
|
|
||||||
// Create the writer
|
// Create the writer
|
||||||
return new ConnectionWriterImpl(reliability, false);
|
return new ConnectionWriterImpl(authentication);
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionWriter createConnectionWriter(SegmentSink out,
|
|
||||||
long capacity, byte[] secret, boolean initiator) {
|
|
||||||
// Derive the keys and erase the secret
|
|
||||||
ErasableKey tagKey = crypto.deriveTagKey(secret, initiator);
|
|
||||||
ErasableKey segKey = crypto.deriveSegmentKey(secret, initiator);
|
|
||||||
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
|
|
||||||
ByteUtils.erase(secret);
|
|
||||||
// Create the encrypter
|
|
||||||
Cipher tagCipher = crypto.getTagCipher();
|
|
||||||
Cipher segCipher = crypto.getSegmentCipher();
|
|
||||||
OutgoingEncryptionLayer encryption =
|
|
||||||
new SegmentedOutgoingEncryptionLayer(out, capacity, tagCipher,
|
|
||||||
segCipher, tagKey, segKey, false, false);
|
|
||||||
// No error correction
|
|
||||||
OutgoingErrorCorrectionLayer correction =
|
|
||||||
new NullOutgoingErrorCorrectionLayer(encryption);
|
|
||||||
// Authentication
|
|
||||||
Mac mac = crypto.getMac();
|
|
||||||
OutgoingAuthenticationLayer authentication =
|
|
||||||
new OutgoingAuthenticationLayerImpl(correction, mac, macKey);
|
|
||||||
// No retransmission
|
|
||||||
OutgoingReliabilityLayer reliability =
|
|
||||||
new NullOutgoingReliabilityLayer(authentication);
|
|
||||||
// Create the writer
|
|
||||||
return new ConnectionWriterImpl(reliability, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.ACK_HEADER_LENGTH;
|
|
||||||
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;
|
||||||
@@ -18,20 +18,16 @@ 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 FrameWriter out;
|
||||||
private final int headerLength, maxFrameLength;
|
|
||||||
private final Frame frame;
|
private final Frame frame;
|
||||||
|
|
||||||
private int offset;
|
private int offset;
|
||||||
private long frameNumber;
|
private long frameNumber;
|
||||||
|
|
||||||
ConnectionWriterImpl(OutgoingReliabilityLayer out, boolean ackHeader) {
|
ConnectionWriterImpl(FrameWriter out) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
|
frame = new Frame();
|
||||||
else headerLength = FRAME_HEADER_LENGTH;
|
offset = FRAME_HEADER_LENGTH;
|
||||||
maxFrameLength = out.getMaxFrameLength();
|
|
||||||
frame = new Frame(maxFrameLength);
|
|
||||||
offset = headerLength;
|
|
||||||
frameNumber = 0L;
|
frameNumber = 0L;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,23 +38,23 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
|
|||||||
public long getRemainingCapacity() {
|
public long getRemainingCapacity() {
|
||||||
long capacity = out.getRemainingCapacity();
|
long capacity = out.getRemainingCapacity();
|
||||||
// If there's any data buffered, subtract it and its overhead
|
// If there's any data buffered, subtract it and its overhead
|
||||||
if(offset > headerLength) capacity -= offset + MAC_LENGTH;
|
if(offset > FRAME_HEADER_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 / maxFrameLength);
|
long frames = (long) Math.ceil((double) capacity / MAX_FRAME_LENGTH);
|
||||||
int overheadPerFrame = headerLength + MAC_LENGTH;
|
int overheadPerFrame = FRAME_HEADER_LENGTH + MAC_LENGTH;
|
||||||
return Math.max(0L, capacity - frames * overheadPerFrame);
|
return Math.max(0L, capacity - frames * overheadPerFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flush() throws IOException {
|
public void flush() throws IOException {
|
||||||
if(offset > headerLength) writeFrame();
|
if(offset > FRAME_HEADER_LENGTH) writeFrame();
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 == maxFrameLength) writeFrame();
|
if(offset + MAC_LENGTH == MAX_FRAME_LENGTH) writeFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -69,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 = maxFrameLength - offset - MAC_LENGTH;
|
int available = MAX_FRAME_LENGTH - 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 = maxFrameLength - offset - MAC_LENGTH;
|
available = MAX_FRAME_LENGTH - offset - MAC_LENGTH;
|
||||||
}
|
}
|
||||||
System.arraycopy(b, off, buf, offset, len);
|
System.arraycopy(b, off, buf, offset, len);
|
||||||
offset += len;
|
offset += len;
|
||||||
@@ -84,12 +80,13 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
|
|||||||
|
|
||||||
private void writeFrame() throws IOException {
|
private void writeFrame() throws IOException {
|
||||||
if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
|
if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
|
||||||
int payload = offset - headerLength;
|
int payload = offset - FRAME_HEADER_LENGTH;
|
||||||
assert payload > 0;
|
assert payload > 0;
|
||||||
HeaderEncoder.encodeHeader(frame.getBuffer(), frameNumber, payload, 0);
|
HeaderEncoder.encodeHeader(frame.getBuffer(), frameNumber, payload, 0);
|
||||||
frame.setLength(offset + MAC_LENGTH);
|
frame.setLength(offset + MAC_LENGTH);
|
||||||
out.writeFrame(frame);
|
out.writeFrame(frame);
|
||||||
offset = headerLength;
|
frame.reset();
|
||||||
|
offset = FRAME_HEADER_LENGTH;
|
||||||
frameNumber++;
|
frameNumber++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import net.sf.briar.api.FormatException;
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
interface ErasureDecoder {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decodes the given set of segments into the given frame, or returns false
|
|
||||||
* if the segments cannot be decoded. The segment set may contain nulls.
|
|
||||||
*/
|
|
||||||
public boolean decodeFrame(Frame f, Segment[] set) throws FormatException;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
interface ErasureEncoder {
|
|
||||||
|
|
||||||
/** Encodes the given frame as a set of segments. */
|
|
||||||
Segment[] encodeFrame(Frame f);
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,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.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
import static net.sf.briar.api.transport.TransportConstants.MAX_SEGMENT_LENGTH;
|
|
||||||
|
|
||||||
class Frame {
|
class Frame {
|
||||||
|
|
||||||
@@ -12,25 +11,13 @@ class Frame {
|
|||||||
private int length = -1;
|
private int length = -1;
|
||||||
|
|
||||||
Frame() {
|
Frame() {
|
||||||
this(MAX_FRAME_LENGTH);
|
buf = new byte[MAX_FRAME_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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getBuffer() {
|
public byte[] getBuffer() {
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getFrameNumber() {
|
|
||||||
if(length == -1) throw new IllegalStateException();
|
|
||||||
return HeaderEncoder.getFrameNumber(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLength() {
|
public int getLength() {
|
||||||
if(length == -1) throw new IllegalStateException();
|
if(length == -1) throw new IllegalStateException();
|
||||||
return length;
|
return length;
|
||||||
@@ -41,4 +28,8 @@ class Frame {
|
|||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
this.length = length;
|
this.length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
length = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
components/net/sf/briar/transport/FrameReader.java
Normal file
12
components/net/sf/briar/transport/FrameReader.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
interface FrameReader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a frame into the given buffer. Returns false if no more frames can
|
||||||
|
* be read from the connection.
|
||||||
|
*/
|
||||||
|
boolean readFrame(Frame f) throws IOException;
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
interface FrameWindow {
|
|
||||||
|
|
||||||
/** Returns true if the given number is too high to fit in the window. */
|
|
||||||
boolean isTooHigh(long frameNumber);
|
|
||||||
|
|
||||||
/** Returns true if the given number is in the window. */
|
|
||||||
boolean contains(long frameNumber);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given number from the window and advances the window.
|
|
||||||
* Returns false if the given number is not in the window.
|
|
||||||
*/
|
|
||||||
boolean remove(long frameNumber);
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.FRAME_WINDOW_SIZE;
|
|
||||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
/** A frame window that allows a limited amount of reordering. */
|
|
||||||
class FrameWindowImpl implements FrameWindow {
|
|
||||||
|
|
||||||
private final Collection<Long> window;
|
|
||||||
|
|
||||||
private long base;
|
|
||||||
|
|
||||||
FrameWindowImpl() {
|
|
||||||
window = new HashSet<Long>();
|
|
||||||
fill(0, FRAME_WINDOW_SIZE);
|
|
||||||
base = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTooHigh(long frameNumber) {
|
|
||||||
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
return frameNumber >= base + FRAME_WINDOW_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean contains(long frameNumber) {
|
|
||||||
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
return window.contains(frameNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean remove(long frameNumber) {
|
|
||||||
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
if(!window.remove(frameNumber)) return false;
|
|
||||||
if(frameNumber == base) {
|
|
||||||
// Find the new base
|
|
||||||
if(window.isEmpty()) {
|
|
||||||
base += FRAME_WINDOW_SIZE;
|
|
||||||
fill(base, base + FRAME_WINDOW_SIZE);
|
|
||||||
} else {
|
|
||||||
for(long l = base; l < base + FRAME_WINDOW_SIZE; l++) {
|
|
||||||
if(window.contains(l)) {
|
|
||||||
fill(base + FRAME_WINDOW_SIZE, l + FRAME_WINDOW_SIZE);
|
|
||||||
base = l;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fill(long from, long to) {
|
|
||||||
for(long l = from; l < to; l++) {
|
|
||||||
if(l <= MAX_32_BIT_UNSIGNED) window.add(l);
|
|
||||||
else return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ package net.sf.briar.transport;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
interface OutgoingReliabilityLayer {
|
interface FrameWriter {
|
||||||
|
|
||||||
/** Writes the given frame. */
|
/** Writes the given frame. */
|
||||||
void writeFrame(Frame f) throws IOException;
|
void writeFrame(Frame f) throws IOException;
|
||||||
@@ -12,7 +12,4 @@ 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();
|
|
||||||
}
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
interface IncomingAuthenticationLayer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a frame into the given buffer. The frame number must be contained
|
|
||||||
* in the given window. Returns false if no more frames can be read from
|
|
||||||
* the connection.
|
|
||||||
* @throws IOException if an unrecoverable error occurs and the connection
|
|
||||||
* must be closed.
|
|
||||||
* @throws InvalidDataException if a recoverable error occurs. The caller
|
|
||||||
* may choose whether to retry the read or close the connection.
|
|
||||||
*/
|
|
||||||
boolean readFrame(Frame f, FrameWindow window) throws IOException,
|
|
||||||
InvalidDataException;
|
|
||||||
|
|
||||||
/** Returns the maximum length in bytes of the frames this layer returns. */
|
|
||||||
int getMaxFrameLength();
|
|
||||||
}
|
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.ACK_HEADER_LENGTH;
|
|
||||||
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;
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
|
|
||||||
|
import net.sf.briar.api.FormatException;
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
|
|
||||||
class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
|
class IncomingAuthenticationLayerImpl implements FrameReader {
|
||||||
|
|
||||||
private final IncomingErrorCorrectionLayer in;
|
private final FrameReader in;
|
||||||
private final Mac mac;
|
private final Mac mac;
|
||||||
private final int headerLength, maxFrameLength;
|
|
||||||
|
|
||||||
IncomingAuthenticationLayerImpl(IncomingErrorCorrectionLayer in, Mac mac,
|
IncomingAuthenticationLayerImpl(FrameReader in, Mac mac,
|
||||||
ErasableKey macKey, boolean ackHeader) {
|
ErasableKey macKey) {
|
||||||
this.in = in;
|
this.in = in;
|
||||||
this.mac = mac;
|
this.mac = mac;
|
||||||
try {
|
try {
|
||||||
@@ -29,43 +29,34 @@ class IncomingAuthenticationLayerImpl implements IncomingAuthenticationLayer {
|
|||||||
macKey.erase();
|
macKey.erase();
|
||||||
if(mac.getMacLength() != MAC_LENGTH)
|
if(mac.getMacLength() != MAC_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
|
|
||||||
else headerLength = FRAME_HEADER_LENGTH;
|
|
||||||
maxFrameLength = in.getMaxFrameLength();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean readFrame(Frame f, FrameWindow window) throws IOException,
|
public boolean readFrame(Frame f) throws IOException {
|
||||||
InvalidDataException {
|
|
||||||
// Read a frame
|
// Read a frame
|
||||||
if(!in.readFrame(f, window)) return false;
|
if(!in.readFrame(f)) return false;
|
||||||
// Check that the length is legal
|
// Check that the length is legal
|
||||||
int length = f.getLength();
|
int length = f.getLength();
|
||||||
if(length < headerLength + MAC_LENGTH)
|
if(length < FRAME_HEADER_LENGTH + MAC_LENGTH)
|
||||||
throw new InvalidDataException();
|
throw new FormatException();
|
||||||
if(length > maxFrameLength) throw new InvalidDataException();
|
if(length > MAX_FRAME_LENGTH) throw new FormatException();
|
||||||
// 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);
|
||||||
int padding = HeaderEncoder.getPaddingLength(buf);
|
int padding = HeaderEncoder.getPaddingLength(buf);
|
||||||
if(length != headerLength + payload + padding + MAC_LENGTH)
|
if(length != FRAME_HEADER_LENGTH + payload + padding + MAC_LENGTH)
|
||||||
throw new InvalidDataException();
|
throw new FormatException();
|
||||||
// Check that the padding is all zeroes
|
// Check that the padding is all zeroes
|
||||||
int paddingStart = headerLength + payload;
|
int paddingStart = FRAME_HEADER_LENGTH + payload;
|
||||||
for(int i = paddingStart; i < paddingStart + padding; i++) {
|
for(int i = paddingStart; i < paddingStart + padding; i++) {
|
||||||
if(buf[i] != 0) throw new InvalidDataException();
|
if(buf[i] != 0) throw new FormatException();
|
||||||
}
|
}
|
||||||
// Verify the MAC
|
// Verify the MAC
|
||||||
int macStart = headerLength + payload + padding;
|
int macStart = FRAME_HEADER_LENGTH + payload + padding;
|
||||||
mac.update(buf, 0, macStart);
|
mac.update(buf, 0, macStart);
|
||||||
byte[] expectedMac = mac.doFinal();
|
byte[] expectedMac = mac.doFinal();
|
||||||
for(int i = 0; i < expectedMac.length; i++) {
|
for(int i = 0; i < expectedMac.length; i++) {
|
||||||
if(expectedMac[i] != buf[macStart + i])
|
if(expectedMac[i] != buf[macStart + i]) throw new FormatException();
|
||||||
throw new InvalidDataException();
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
|
||||||
return maxFrameLength;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
interface IncomingEncryptionLayer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a segment, excluding its tag, into the given buffer. Returns false
|
|
||||||
* if no more segments can be read from the connection.
|
|
||||||
* @throws IOException if an unrecoverable error occurs and the connection
|
|
||||||
* must be closed.
|
|
||||||
* @throws InvalidDataException if a recoverable error occurs. The caller
|
|
||||||
* may choose whether to retry the read or close the connection.
|
|
||||||
*/
|
|
||||||
boolean readSegment(Segment s) throws IOException, InvalidDataException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the maximum length in bytes of the segments this layer returns.
|
|
||||||
*/
|
|
||||||
int getMaxSegmentLength();
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.ACK_HEADER_LENGTH;
|
|
||||||
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 static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
@@ -17,86 +15,70 @@ import javax.crypto.spec.IvParameterSpec;
|
|||||||
|
|
||||||
import net.sf.briar.api.FormatException;
|
import net.sf.briar.api.FormatException;
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
|
class IncomingEncryptionLayerImpl implements FrameReader {
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
private final Cipher tagCipher, segCipher;
|
private final Cipher tagCipher, frameCipher;
|
||||||
private final ErasableKey tagKey, segKey;
|
private final ErasableKey tagKey, frameKey;
|
||||||
private final boolean tagEverySegment;
|
private final int blockSize;
|
||||||
private final int headerLength, blockSize;
|
|
||||||
private final byte[] iv, ciphertext;
|
private final byte[] iv, ciphertext;
|
||||||
|
|
||||||
private byte[] bufferedTag;
|
private boolean readTag;
|
||||||
private boolean firstSegment = true;
|
private long frameNumber;
|
||||||
private long segmentNumber = 0L;
|
|
||||||
|
|
||||||
IncomingEncryptionLayerImpl(InputStream in, Cipher tagCipher,
|
IncomingEncryptionLayerImpl(InputStream in, Cipher tagCipher,
|
||||||
Cipher segCipher, ErasableKey tagKey, ErasableKey segKey,
|
Cipher frameCipher, ErasableKey tagKey, ErasableKey frameKey,
|
||||||
boolean tagEverySegment, boolean ackHeader, byte[] bufferedTag) {
|
boolean readTag) {
|
||||||
this.in = in;
|
this.in = in;
|
||||||
this.tagCipher = tagCipher;
|
this.tagCipher = tagCipher;
|
||||||
this.segCipher = segCipher;
|
this.frameCipher = frameCipher;
|
||||||
this.tagKey = tagKey;
|
this.tagKey = tagKey;
|
||||||
this.segKey = segKey;
|
this.frameKey = frameKey;
|
||||||
this.tagEverySegment = tagEverySegment;
|
this.readTag = readTag;
|
||||||
this.bufferedTag = bufferedTag;
|
blockSize = frameCipher.getBlockSize();
|
||||||
if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
|
|
||||||
else headerLength = FRAME_HEADER_LENGTH;
|
|
||||||
blockSize = segCipher.getBlockSize();
|
|
||||||
if(blockSize < FRAME_HEADER_LENGTH)
|
if(blockSize < FRAME_HEADER_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
iv = IvEncoder.encodeIv(0L, blockSize);
|
iv = IvEncoder.encodeIv(0L, blockSize);
|
||||||
ciphertext = new byte[MAX_SEGMENT_LENGTH];
|
ciphertext = new byte[MAX_FRAME_LENGTH];
|
||||||
|
frameNumber = 0L;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean readSegment(Segment s) throws IOException {
|
public boolean readFrame(Frame f) throws IOException {
|
||||||
boolean expectTag = tagEverySegment || firstSegment;
|
|
||||||
firstSegment = false;
|
|
||||||
try {
|
try {
|
||||||
if(expectTag) {
|
// Read the tag if it hasn't already been read
|
||||||
// Read the tag if we don't have one buffered
|
if(readTag) {
|
||||||
if(bufferedTag == null) {
|
int offset = 0;
|
||||||
int offset = 0;
|
while(offset < TAG_LENGTH) {
|
||||||
while(offset < TAG_LENGTH) {
|
int read = in.read(ciphertext, offset,
|
||||||
int read = in.read(ciphertext, offset,
|
TAG_LENGTH - offset);
|
||||||
TAG_LENGTH - offset);
|
if(read == -1) {
|
||||||
if(read == -1) {
|
if(offset == 0) return false;
|
||||||
if(offset == 0) return false;
|
throw new EOFException();
|
||||||
throw new EOFException();
|
|
||||||
}
|
|
||||||
offset += read;
|
|
||||||
}
|
}
|
||||||
long seg = TagEncoder.decodeTag(ciphertext, tagCipher,
|
offset += read;
|
||||||
tagKey);
|
|
||||||
if(seg == -1) throw new FormatException();
|
|
||||||
segmentNumber = seg;
|
|
||||||
} else {
|
|
||||||
long seg = TagEncoder.decodeTag(bufferedTag, tagCipher,
|
|
||||||
tagKey);
|
|
||||||
bufferedTag = null;
|
|
||||||
if(seg == -1) throw new FormatException();
|
|
||||||
segmentNumber = seg;
|
|
||||||
}
|
}
|
||||||
|
if(!TagEncoder.decodeTag(ciphertext, tagCipher, tagKey))
|
||||||
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
// Read the first block of the frame
|
// Read the first block of the frame
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
while(offset < blockSize) {
|
while(offset < blockSize) {
|
||||||
int read = in.read(ciphertext, offset, blockSize - offset);
|
int read = in.read(ciphertext, offset, blockSize - offset);
|
||||||
if(read == -1) {
|
if(read == -1) {
|
||||||
if(offset == 0 && !expectTag) return false;
|
if(offset == 0 && !readTag) return false;
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
|
readTag = false;
|
||||||
// Decrypt the first block of the frame
|
// Decrypt the first block of the frame
|
||||||
byte[] plaintext = s.getBuffer();
|
byte[] plaintext = f.getBuffer();
|
||||||
try {
|
try {
|
||||||
IvEncoder.updateIv(iv, segmentNumber);
|
IvEncoder.updateIv(iv, frameNumber);
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
segCipher.init(Cipher.DECRYPT_MODE, segKey, ivSpec);
|
frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec);
|
||||||
int decrypted = segCipher.update(ciphertext, 0, blockSize,
|
int decrypted = frameCipher.update(ciphertext, 0, blockSize,
|
||||||
plaintext);
|
plaintext);
|
||||||
if(decrypted != blockSize) throw new RuntimeException();
|
if(decrypted != blockSize) throw new RuntimeException();
|
||||||
} catch(GeneralSecurityException badCipher) {
|
} catch(GeneralSecurityException badCipher) {
|
||||||
@@ -105,7 +87,7 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
|
|||||||
// Parse the frame header
|
// Parse the frame header
|
||||||
int payload = HeaderEncoder.getPayloadLength(plaintext);
|
int payload = HeaderEncoder.getPayloadLength(plaintext);
|
||||||
int padding = HeaderEncoder.getPaddingLength(plaintext);
|
int padding = HeaderEncoder.getPaddingLength(plaintext);
|
||||||
int length = headerLength + payload + padding + MAC_LENGTH;
|
int length = FRAME_HEADER_LENGTH + payload + padding + MAC_LENGTH;
|
||||||
if(length > MAX_FRAME_LENGTH) throw new FormatException();
|
if(length > MAX_FRAME_LENGTH) throw new FormatException();
|
||||||
// Read the remainder of the frame
|
// Read the remainder of the frame
|
||||||
while(offset < length) {
|
while(offset < length) {
|
||||||
@@ -115,24 +97,20 @@ class IncomingEncryptionLayerImpl implements IncomingEncryptionLayer {
|
|||||||
}
|
}
|
||||||
// Decrypt the remainder of the frame
|
// Decrypt the remainder of the frame
|
||||||
try {
|
try {
|
||||||
int decrypted = segCipher.doFinal(ciphertext, blockSize,
|
int decrypted = frameCipher.doFinal(ciphertext, blockSize,
|
||||||
length - blockSize, plaintext, blockSize);
|
length - blockSize, plaintext, blockSize);
|
||||||
if(decrypted != length - blockSize)
|
if(decrypted != length - blockSize)
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
} catch(GeneralSecurityException badCipher) {
|
} catch(GeneralSecurityException badCipher) {
|
||||||
throw new RuntimeException(badCipher);
|
throw new RuntimeException(badCipher);
|
||||||
}
|
}
|
||||||
s.setLength(length);
|
f.setLength(length);
|
||||||
s.setSegmentNumber(segmentNumber++);
|
frameNumber++;
|
||||||
return true;
|
return true;
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
segKey.erase();
|
frameKey.erase();
|
||||||
tagKey.erase();
|
tagKey.erase();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxSegmentLength() {
|
|
||||||
return MAX_SEGMENT_LENGTH - TAG_LENGTH;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
interface IncomingErrorCorrectionLayer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a frame into the given buffer. The frame number must be contained
|
|
||||||
* in the given window. Returns false if no more frames can be read from
|
|
||||||
* the connection.
|
|
||||||
* @throws IOException if an unrecoverable error occurs and the connection
|
|
||||||
* must be closed.
|
|
||||||
* @throws InvalidDataException if a recoverable error occurs. The caller
|
|
||||||
* may choose whether to retry the read or close the connection.
|
|
||||||
*/
|
|
||||||
boolean readFrame(Frame f, FrameWindow window) throws IOException,
|
|
||||||
InvalidDataException;
|
|
||||||
|
|
||||||
/** Returns the maximum length in bytes of the frames this layer returns. */
|
|
||||||
int getMaxFrameLength();
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import net.sf.briar.api.FormatException;
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
|
|
||||||
|
|
||||||
private final IncomingEncryptionLayer in;
|
|
||||||
private final ErasureDecoder decoder;
|
|
||||||
private final int n, k, maxSegmentLength, maxFrameLength;
|
|
||||||
private final Map<Long, Integer> discardCounts;
|
|
||||||
private final Map<Long, Segment[]> segmentSets;
|
|
||||||
private final ArrayList<Segment> freeSegments;
|
|
||||||
|
|
||||||
IncomingErrorCorrectionLayerImpl(IncomingEncryptionLayer in,
|
|
||||||
ErasureDecoder decoder, int n, int k) {
|
|
||||||
this.in = in;
|
|
||||||
this.decoder = decoder;
|
|
||||||
this.n = n;
|
|
||||||
this.k = k;
|
|
||||||
maxSegmentLength = in.getMaxSegmentLength();
|
|
||||||
maxFrameLength = Math.min(MAX_FRAME_LENGTH, maxSegmentLength * k);
|
|
||||||
discardCounts = new HashMap<Long, Integer>();
|
|
||||||
segmentSets = new HashMap<Long, Segment[]>();
|
|
||||||
freeSegments = new ArrayList<Segment>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean readFrame(Frame f, FrameWindow window) throws IOException,
|
|
||||||
InvalidDataException {
|
|
||||||
// Free any segment sets that have been removed from the window
|
|
||||||
Iterator<Entry<Long, Segment[]>> it = segmentSets.entrySet().iterator();
|
|
||||||
while(it.hasNext()) {
|
|
||||||
Entry<Long, Segment[]> e = it.next();
|
|
||||||
if(!window.contains(e.getKey())) {
|
|
||||||
it.remove();
|
|
||||||
for(Segment s : e.getValue()) if(s != null) freeSegments.add(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Free any discard counts that are no longer too high for the window
|
|
||||||
Iterator<Long> it1 = discardCounts.keySet().iterator();
|
|
||||||
while(it1.hasNext()) if(!window.isTooHigh(it1.next())) it1.remove();
|
|
||||||
// Grab a free segment, or allocate one if necessary
|
|
||||||
Segment s;
|
|
||||||
int free = freeSegments.size();
|
|
||||||
if(free == 0) s = new SegmentImpl(maxSegmentLength);
|
|
||||||
else s = freeSegments.remove(free - 1);
|
|
||||||
// Read segments until a frame can be decoded
|
|
||||||
while(true) {
|
|
||||||
// Read segments until a segment in the window is returned
|
|
||||||
long frameNumber;
|
|
||||||
while(true) {
|
|
||||||
if(!in.readSegment(s)) {
|
|
||||||
freeSegments.add(s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
frameNumber = s.getSegmentNumber() / n;
|
|
||||||
if(window.contains(frameNumber)) break;
|
|
||||||
if(window.isTooHigh(frameNumber)) countDiscard(frameNumber);
|
|
||||||
}
|
|
||||||
// Add the segment to its set, creating a set if necessary
|
|
||||||
Segment[] set = segmentSets.get(frameNumber);
|
|
||||||
if(set == null) {
|
|
||||||
set = new Segment[n];
|
|
||||||
segmentSets.put(frameNumber, set);
|
|
||||||
}
|
|
||||||
set[(int) (frameNumber % n)] = s;
|
|
||||||
// Try to decode the frame
|
|
||||||
if(decoder.decodeFrame(f, set)) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
|
||||||
return maxFrameLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void countDiscard(long frameNumber) throws FormatException {
|
|
||||||
Integer count = discardCounts.get(frameNumber);
|
|
||||||
if(count == null) discardCounts.put(frameNumber, 1);
|
|
||||||
else if(count == n - k) throw new FormatException();
|
|
||||||
else discardCounts.put(frameNumber, count + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only for testing
|
|
||||||
Map<Long, Segment[]> getSegmentSets() {
|
|
||||||
return segmentSets;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only for testing
|
|
||||||
Map<Long, Integer> getDiscardCounts() {
|
|
||||||
return discardCounts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
interface IncomingReliabilityLayer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads and returns a frame, possibly using the given buffer. Returns null
|
|
||||||
* if no more frames can be read from the connection.
|
|
||||||
* @throws IOException if an unrecoverable error occurs and the connection
|
|
||||||
* must be closed.
|
|
||||||
* @throws InvalidDataException if a recoverable error occurs. The caller
|
|
||||||
* may choose whether to retry the read or close the connection.
|
|
||||||
*/
|
|
||||||
Frame readFrame(Frame f) throws IOException, InvalidDataException;
|
|
||||||
|
|
||||||
/** Returns the maximum length in bytes of the frames this layer returns. */
|
|
||||||
int getMaxFrameLength();
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.SortedMap;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
/** A reliability layer that reorders out-of-order frames. */
|
|
||||||
class IncomingReliabilityLayerImpl implements IncomingReliabilityLayer {
|
|
||||||
|
|
||||||
private final IncomingAuthenticationLayer in;
|
|
||||||
private final int maxFrameLength;
|
|
||||||
private final FrameWindow window;
|
|
||||||
private final SortedMap<Long, Frame> frames;
|
|
||||||
private final ArrayList<Frame> freeFrames;
|
|
||||||
|
|
||||||
private long nextFrameNumber = 0L;
|
|
||||||
|
|
||||||
IncomingReliabilityLayerImpl(IncomingAuthenticationLayer in) {
|
|
||||||
this.in = in;
|
|
||||||
maxFrameLength = in.getMaxFrameLength();
|
|
||||||
window = new FrameWindowImpl();
|
|
||||||
frames = new TreeMap<Long, Frame>();
|
|
||||||
freeFrames = new ArrayList<Frame>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Frame readFrame(Frame f) throws IOException,
|
|
||||||
InvalidDataException {
|
|
||||||
freeFrames.add(f);
|
|
||||||
// Read frames until there's an in-order frame to return
|
|
||||||
while(frames.isEmpty() || frames.firstKey() > nextFrameNumber) {
|
|
||||||
// Grab a free frame, or allocate one if necessary
|
|
||||||
int free = freeFrames.size();
|
|
||||||
if(free == 0) f = new Frame(maxFrameLength);
|
|
||||||
else f = freeFrames.remove(free - 1);
|
|
||||||
// Read a frame
|
|
||||||
if(!in.readFrame(f, window)) return null;
|
|
||||||
// If the frame is in order, return it
|
|
||||||
long frameNumber = f.getFrameNumber();
|
|
||||||
if(frameNumber == nextFrameNumber) {
|
|
||||||
if(!window.remove(nextFrameNumber))
|
|
||||||
throw new IllegalStateException();
|
|
||||||
nextFrameNumber++;
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
// Insert the frame into the map
|
|
||||||
frames.put(frameNumber, f);
|
|
||||||
}
|
|
||||||
if(!window.remove(nextFrameNumber)) throw new IllegalStateException();
|
|
||||||
nextFrameNumber++;
|
|
||||||
return frames.remove(frames.firstKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
|
||||||
return maxFrameLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only for testing
|
|
||||||
public int getFreeFramesCount() {
|
|
||||||
return freeFrames.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
/** An exception that indicates a recoverable formatting error. */
|
|
||||||
class InvalidDataException extends Exception {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 4455775710413826953L;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
|
||||||
|
|
||||||
/** A frame window that does not allow any reordering. */
|
|
||||||
class NullFrameWindow implements FrameWindow {
|
|
||||||
|
|
||||||
private long base = 0L;
|
|
||||||
|
|
||||||
public boolean isTooHigh(long frameNumber) {
|
|
||||||
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
return frameNumber != base;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean contains(long frameNumber) {
|
|
||||||
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
return frameNumber == base;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean remove(long frameNumber) {
|
|
||||||
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
if(frameNumber != base) return false;
|
|
||||||
base++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
class NullIncomingErrorCorrectionLayer implements IncomingErrorCorrectionLayer {
|
|
||||||
|
|
||||||
private final IncomingEncryptionLayer in;
|
|
||||||
private final int maxFrameLength;
|
|
||||||
private final Segment segment;
|
|
||||||
|
|
||||||
NullIncomingErrorCorrectionLayer(IncomingEncryptionLayer in) {
|
|
||||||
this.in = in;
|
|
||||||
maxFrameLength = in.getMaxSegmentLength();
|
|
||||||
segment = new SegmentImpl(maxFrameLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean readFrame(Frame f, FrameWindow window) throws IOException,
|
|
||||||
InvalidDataException {
|
|
||||||
while(true) {
|
|
||||||
if(!in.readSegment(segment)) return false;
|
|
||||||
byte[] buf = segment.getBuffer();
|
|
||||||
long frameNumber = HeaderEncoder.getFrameNumber(buf);
|
|
||||||
if(window.contains(frameNumber)) break;
|
|
||||||
}
|
|
||||||
int length = segment.getLength();
|
|
||||||
// FIXME: Unnecessary copy
|
|
||||||
System.arraycopy(segment.getBuffer(), 0, f.getBuffer(), 0, length);
|
|
||||||
f.setLength(length);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
|
||||||
return maxFrameLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
class NullIncomingReliabilityLayer implements IncomingReliabilityLayer {
|
|
||||||
|
|
||||||
private final IncomingAuthenticationLayer in;
|
|
||||||
private final int maxFrameLength;
|
|
||||||
private final FrameWindow window;
|
|
||||||
|
|
||||||
NullIncomingReliabilityLayer(IncomingAuthenticationLayer in) {
|
|
||||||
this.in = in;
|
|
||||||
maxFrameLength = in.getMaxFrameLength();
|
|
||||||
window = new NullFrameWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Frame readFrame(Frame f) throws IOException, InvalidDataException {
|
|
||||||
if(!in.readFrame(f, window)) return null;
|
|
||||||
if(!window.remove(f.getFrameNumber()))
|
|
||||||
throw new IllegalStateException();
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
|
||||||
return maxFrameLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
class NullOutgoingErrorCorrectionLayer implements OutgoingErrorCorrectionLayer {
|
|
||||||
|
|
||||||
private final OutgoingEncryptionLayer out;
|
|
||||||
private final int maxSegmentLength;
|
|
||||||
private final Segment segment;
|
|
||||||
|
|
||||||
private long segmentNumber = 0L;
|
|
||||||
|
|
||||||
public NullOutgoingErrorCorrectionLayer(OutgoingEncryptionLayer out) {
|
|
||||||
this.out = out;
|
|
||||||
maxSegmentLength = out.getMaxSegmentLength();
|
|
||||||
segment = new SegmentImpl(maxSegmentLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeFrame(Frame f) throws IOException {
|
|
||||||
if(segmentNumber > MAX_32_BIT_UNSIGNED)
|
|
||||||
throw new IllegalStateException();
|
|
||||||
int length = f.getLength();
|
|
||||||
// FIXME: Unnecessary copy
|
|
||||||
System.arraycopy(f.getBuffer(), 0, segment.getBuffer(), 0, length);
|
|
||||||
segment.setLength(length);
|
|
||||||
segment.setSegmentNumber(segmentNumber++);
|
|
||||||
out.writeSegment(segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void flush() throws IOException {
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getRemainingCapacity() {
|
|
||||||
return out.getRemainingCapacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
|
||||||
return maxSegmentLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
class NullOutgoingReliabilityLayer implements OutgoingReliabilityLayer {
|
|
||||||
|
|
||||||
private final OutgoingAuthenticationLayer out;
|
|
||||||
private final int maxFrameLength;
|
|
||||||
|
|
||||||
NullOutgoingReliabilityLayer(OutgoingAuthenticationLayer out) {
|
|
||||||
this.out = out;
|
|
||||||
maxFrameLength = out.getMaxFrameLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeFrame(Frame f) throws IOException {
|
|
||||||
out.writeFrame(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void flush() throws IOException {
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getRemainingCapacity() {
|
|
||||||
return out.getRemainingCapacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
|
||||||
return maxFrameLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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();
|
|
||||||
|
|
||||||
/** Returns the maximum length in bytes of the frames this layer accepts. */
|
|
||||||
int getMaxFrameLength();
|
|
||||||
}
|
|
||||||
@@ -10,13 +10,12 @@ import javax.crypto.ShortBufferException;
|
|||||||
|
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
|
|
||||||
class OutgoingAuthenticationLayerImpl implements OutgoingAuthenticationLayer {
|
class OutgoingAuthenticationLayerImpl implements FrameWriter {
|
||||||
|
|
||||||
private final OutgoingErrorCorrectionLayer out;
|
private final FrameWriter out;
|
||||||
private final Mac mac;
|
private final Mac mac;
|
||||||
private final int maxFrameLength;
|
|
||||||
|
|
||||||
OutgoingAuthenticationLayerImpl(OutgoingErrorCorrectionLayer out, Mac mac,
|
OutgoingAuthenticationLayerImpl(FrameWriter out, Mac mac,
|
||||||
ErasableKey macKey) {
|
ErasableKey macKey) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
this.mac = mac;
|
this.mac = mac;
|
||||||
@@ -28,7 +27,6 @@ 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 {
|
||||||
@@ -50,8 +48,4 @@ class OutgoingAuthenticationLayerImpl implements OutgoingAuthenticationLayer {
|
|||||||
public long getRemainingCapacity() {
|
public long getRemainingCapacity() {
|
||||||
return out.getRemainingCapacity();
|
return out.getRemainingCapacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
|
||||||
return maxFrameLength;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
interface OutgoingEncryptionLayer {
|
|
||||||
|
|
||||||
/** Writes the given segment. */
|
|
||||||
void writeSegment(Segment s) throws IOException;
|
|
||||||
|
|
||||||
/** Flushes the stack. */
|
|
||||||
void flush() throws IOException;
|
|
||||||
|
|
||||||
/** Returns the maximum number of bytes that can be written. */
|
|
||||||
long getRemainingCapacity();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the maximum length in bytes of the segments this layer accepts.
|
|
||||||
*/
|
|
||||||
int getMaxSegmentLength();
|
|
||||||
}
|
|
||||||
@@ -1,6 +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.MAX_FRAME_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;
|
||||||
@@ -11,46 +11,43 @@ import javax.crypto.Cipher;
|
|||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
class OutgoingEncryptionLayerImpl implements OutgoingEncryptionLayer {
|
class OutgoingEncryptionLayerImpl implements FrameWriter {
|
||||||
|
|
||||||
private final OutputStream out;
|
private final OutputStream out;
|
||||||
private final Cipher tagCipher, segCipher;
|
private final Cipher tagCipher, frameCipher;
|
||||||
private final ErasableKey tagKey, segKey;
|
private final ErasableKey tagKey, frameKey;
|
||||||
private final boolean tagEverySegment;
|
|
||||||
private final byte[] iv, ciphertext;
|
private final byte[] iv, ciphertext;
|
||||||
|
|
||||||
private long capacity;
|
private long capacity, frameNumber;
|
||||||
|
|
||||||
OutgoingEncryptionLayerImpl(OutputStream out, long capacity,
|
OutgoingEncryptionLayerImpl(OutputStream out, long capacity,
|
||||||
Cipher tagCipher, Cipher segCipher, ErasableKey tagKey,
|
Cipher tagCipher, Cipher frameCipher, ErasableKey tagKey,
|
||||||
ErasableKey segKey, boolean tagEverySegment) {
|
ErasableKey frameKey) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
this.capacity = capacity;
|
this.capacity = capacity;
|
||||||
this.tagCipher = tagCipher;
|
this.tagCipher = tagCipher;
|
||||||
this.segCipher = segCipher;
|
this.frameCipher = frameCipher;
|
||||||
this.tagKey = tagKey;
|
this.tagKey = tagKey;
|
||||||
this.segKey = segKey;
|
this.frameKey = frameKey;
|
||||||
this.tagEverySegment = tagEverySegment;
|
iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
|
||||||
iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
|
ciphertext = new byte[TAG_LENGTH + MAX_FRAME_LENGTH];
|
||||||
ciphertext = new byte[MAX_SEGMENT_LENGTH];
|
frameNumber = 0L;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeSegment(Segment s) throws IOException {
|
public void writeFrame(Frame f) throws IOException {
|
||||||
byte[] plaintext = s.getBuffer();
|
byte[] plaintext = f.getBuffer();
|
||||||
int length = s.getLength();
|
int length = f.getLength();
|
||||||
long segmentNumber = s.getSegmentNumber();
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
if(tagEverySegment || segmentNumber == 0) {
|
if(frameNumber == 0) {
|
||||||
TagEncoder.encodeTag(ciphertext, segmentNumber, tagCipher, tagKey);
|
TagEncoder.encodeTag(ciphertext, tagCipher, tagKey);
|
||||||
offset = TAG_LENGTH;
|
offset = TAG_LENGTH;
|
||||||
}
|
}
|
||||||
IvEncoder.updateIv(iv, segmentNumber);
|
IvEncoder.updateIv(iv, frameNumber);
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
try {
|
try {
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
|
||||||
int encrypted = segCipher.doFinal(plaintext, 0, length,
|
int encrypted = frameCipher.doFinal(plaintext, 0, length,
|
||||||
ciphertext, offset);
|
ciphertext, offset);
|
||||||
if(encrypted != length) throw new RuntimeException();
|
if(encrypted != length) throw new RuntimeException();
|
||||||
} catch(GeneralSecurityException badCipher) {
|
} catch(GeneralSecurityException badCipher) {
|
||||||
@@ -59,11 +56,12 @@ class OutgoingEncryptionLayerImpl implements OutgoingEncryptionLayer {
|
|||||||
try {
|
try {
|
||||||
out.write(ciphertext, 0, offset + length);
|
out.write(ciphertext, 0, offset + length);
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
segKey.erase();
|
frameKey.erase();
|
||||||
tagKey.erase();
|
tagKey.erase();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
capacity -= offset + length;
|
capacity -= offset + length;
|
||||||
|
frameNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush() throws IOException {
|
public void flush() throws IOException {
|
||||||
@@ -73,8 +71,4 @@ class OutgoingEncryptionLayerImpl implements OutgoingEncryptionLayer {
|
|||||||
public long getRemainingCapacity() {
|
public long getRemainingCapacity() {
|
||||||
return capacity;
|
return capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxSegmentLength() {
|
|
||||||
return MAX_SEGMENT_LENGTH - TAG_LENGTH;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
interface OutgoingErrorCorrectionLayer {
|
|
||||||
|
|
||||||
/** 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();
|
|
||||||
|
|
||||||
/** Returns the maximum length in bytes of the frames this layer accepts. */
|
|
||||||
int getMaxFrameLength();
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
class OutgoingErrorCorrectionLayerImpl implements OutgoingErrorCorrectionLayer {
|
|
||||||
|
|
||||||
private final OutgoingEncryptionLayer out;
|
|
||||||
private final ErasureEncoder encoder;
|
|
||||||
private final int n, maxFrameLength;
|
|
||||||
|
|
||||||
OutgoingErrorCorrectionLayerImpl(OutgoingEncryptionLayer out,
|
|
||||||
ErasureEncoder encoder, int n, int k) {
|
|
||||||
this.out = out;
|
|
||||||
this.encoder = encoder;
|
|
||||||
this.n = n;
|
|
||||||
maxFrameLength = Math.min(MAX_FRAME_LENGTH,
|
|
||||||
out.getMaxSegmentLength() * k);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeFrame(Frame f) throws IOException {
|
|
||||||
for(Segment s : encoder.encodeFrame(f)) out.writeSegment(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void flush() throws IOException {
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getRemainingCapacity() {
|
|
||||||
return out.getRemainingCapacity() / n;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
|
||||||
return maxFrameLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
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.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
class SegmentImpl implements Segment {
|
|
||||||
|
|
||||||
private final byte[] buf;
|
|
||||||
|
|
||||||
private int length = -1;
|
|
||||||
private long segmentNumber = -1;
|
|
||||||
|
|
||||||
SegmentImpl() {
|
|
||||||
this(MAX_SEGMENT_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];
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getBuffer() {
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLength() {
|
|
||||||
if(length == -1) throw new IllegalStateException();
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getSegmentNumber() {
|
|
||||||
if(segmentNumber == -1) throw new IllegalStateException();
|
|
||||||
return segmentNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLength(int length) {
|
|
||||||
if(length < FRAME_HEADER_LENGTH + MAC_LENGTH || length > buf.length)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
this.length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSegmentNumber(long segmentNumber) {
|
|
||||||
if(segmentNumber < 0 || segmentNumber > MAX_32_BIT_UNSIGNED)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
this.segmentNumber = segmentNumber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.ACK_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.MAX_SEGMENT_LENGTH;
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
|
||||||
|
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
|
||||||
import net.sf.briar.api.plugins.SegmentSource;
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
class SegmentedIncomingEncryptionLayer implements IncomingEncryptionLayer {
|
|
||||||
|
|
||||||
private final SegmentSource in;
|
|
||||||
private final Cipher tagCipher, segCipher;
|
|
||||||
private final ErasableKey tagKey, segKey;
|
|
||||||
private final boolean tagEverySegment;
|
|
||||||
private final int blockSize, headerLength, maxSegmentLength;
|
|
||||||
private final Segment segment;
|
|
||||||
private final byte[] iv;
|
|
||||||
|
|
||||||
private Segment bufferedSegment;
|
|
||||||
private boolean firstSegment = true;
|
|
||||||
private long segmentNumber = 0L;
|
|
||||||
|
|
||||||
SegmentedIncomingEncryptionLayer(SegmentSource in, Cipher tagCipher,
|
|
||||||
Cipher segCipher, ErasableKey tagKey, ErasableKey segKey,
|
|
||||||
boolean tagEverySegment, boolean ackHeader,
|
|
||||||
Segment bufferedSegment) {
|
|
||||||
this.in = in;
|
|
||||||
this.tagCipher = tagCipher;
|
|
||||||
this.segCipher = segCipher;
|
|
||||||
this.tagKey = tagKey;
|
|
||||||
this.segKey = segKey;
|
|
||||||
this.tagEverySegment = tagEverySegment;
|
|
||||||
this.bufferedSegment = bufferedSegment;
|
|
||||||
blockSize = segCipher.getBlockSize();
|
|
||||||
if(blockSize < FRAME_HEADER_LENGTH)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
|
|
||||||
else headerLength = FRAME_HEADER_LENGTH;
|
|
||||||
int length = in.getMaxSegmentLength();
|
|
||||||
if(length < TAG_LENGTH + headerLength + 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean readSegment(Segment s) throws IOException,
|
|
||||||
InvalidDataException {
|
|
||||||
boolean expectTag = tagEverySegment || firstSegment;
|
|
||||||
firstSegment = false;
|
|
||||||
try {
|
|
||||||
// Read the segment, unless we have one buffered
|
|
||||||
Segment segment;
|
|
||||||
if(bufferedSegment == null) {
|
|
||||||
segment = this.segment;
|
|
||||||
if(!in.readSegment(segment)) return false;
|
|
||||||
} else {
|
|
||||||
segment = bufferedSegment;
|
|
||||||
bufferedSegment = null;
|
|
||||||
}
|
|
||||||
int offset = expectTag ? TAG_LENGTH : 0;
|
|
||||||
int length = segment.getLength();
|
|
||||||
if(length < offset + headerLength + MAC_LENGTH)
|
|
||||||
throw new InvalidDataException();
|
|
||||||
if(length > offset + maxSegmentLength)
|
|
||||||
throw new InvalidDataException();
|
|
||||||
byte[] ciphertext = segment.getBuffer();
|
|
||||||
// If a tag is expected then decrypt and validate it
|
|
||||||
if(expectTag) {
|
|
||||||
long seg = TagEncoder.decodeTag(ciphertext, tagCipher, tagKey);
|
|
||||||
if(seg == -1) throw new InvalidDataException();
|
|
||||||
segmentNumber = seg;
|
|
||||||
}
|
|
||||||
// Decrypt the segment
|
|
||||||
try {
|
|
||||||
IvEncoder.updateIv(iv, segmentNumber);
|
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
|
||||||
segCipher.init(Cipher.DECRYPT_MODE, segKey, ivSpec);
|
|
||||||
int decrypted = segCipher.doFinal(ciphertext, offset,
|
|
||||||
length - offset, s.getBuffer());
|
|
||||||
if(decrypted != length - offset) throw new RuntimeException();
|
|
||||||
} catch(GeneralSecurityException badCipher) {
|
|
||||||
throw new RuntimeException(badCipher);
|
|
||||||
}
|
|
||||||
s.setLength(length - offset);
|
|
||||||
s.setSegmentNumber(segmentNumber++);
|
|
||||||
return true;
|
|
||||||
} catch(IOException e) {
|
|
||||||
segKey.erase();
|
|
||||||
tagKey.erase();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxSegmentLength() {
|
|
||||||
return maxSegmentLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.ACK_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.MAX_SEGMENT_LENGTH;
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
|
||||||
|
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
|
||||||
import net.sf.briar.api.plugins.SegmentSink;
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
class SegmentedOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
|
|
||||||
|
|
||||||
private final SegmentSink out;
|
|
||||||
private final Cipher tagCipher, segCipher;
|
|
||||||
private final ErasableKey tagKey, segKey;
|
|
||||||
private final boolean tagEverySegment;
|
|
||||||
private final int headerLength, maxSegmentLength;
|
|
||||||
private final Segment segment;
|
|
||||||
private final byte[] iv;
|
|
||||||
|
|
||||||
private long capacity;
|
|
||||||
|
|
||||||
SegmentedOutgoingEncryptionLayer(SegmentSink out, long capacity,
|
|
||||||
Cipher tagCipher, Cipher segCipher, ErasableKey tagKey,
|
|
||||||
ErasableKey segKey, boolean tagEverySegment, boolean ackHeader) {
|
|
||||||
this.out = out;
|
|
||||||
this.capacity = capacity;
|
|
||||||
this.tagCipher = tagCipher;
|
|
||||||
this.segCipher = segCipher;
|
|
||||||
this.tagKey = tagKey;
|
|
||||||
this.segKey = segKey;
|
|
||||||
this.tagEverySegment = tagEverySegment;
|
|
||||||
if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
|
|
||||||
else headerLength = FRAME_HEADER_LENGTH;
|
|
||||||
int length = out.getMaxSegmentLength();
|
|
||||||
if(length < TAG_LENGTH + headerLength + 1 + MAC_LENGTH)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
if(length > MAX_SEGMENT_LENGTH) throw new IllegalArgumentException();
|
|
||||||
maxSegmentLength = length - MAC_LENGTH;
|
|
||||||
segment = new SegmentImpl(length);
|
|
||||||
iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeSegment(Segment s) throws IOException {
|
|
||||||
byte[] plaintext = s.getBuffer(), ciphertext = segment.getBuffer();
|
|
||||||
int length = s.getLength();
|
|
||||||
long segmentNumber = s.getSegmentNumber();
|
|
||||||
int offset = 0;
|
|
||||||
if(tagEverySegment || segmentNumber == 0) {
|
|
||||||
TagEncoder.encodeTag(ciphertext, segmentNumber, tagCipher, tagKey);
|
|
||||||
offset = TAG_LENGTH;
|
|
||||||
}
|
|
||||||
IvEncoder.updateIv(iv, segmentNumber);
|
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
|
||||||
try {
|
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
|
||||||
int encrypted = segCipher.doFinal(plaintext, 0, length,
|
|
||||||
ciphertext, offset);
|
|
||||||
if(encrypted != length) throw new RuntimeException();
|
|
||||||
} catch(GeneralSecurityException badCipher) {
|
|
||||||
throw new RuntimeException(badCipher);
|
|
||||||
}
|
|
||||||
segment.setLength(offset + length);
|
|
||||||
try {
|
|
||||||
out.writeSegment(segment);
|
|
||||||
} catch(IOException e) {
|
|
||||||
segKey.erase();
|
|
||||||
tagKey.erase();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
capacity -= offset + length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void flush() throws IOException {}
|
|
||||||
|
|
||||||
public long getRemainingCapacity() {
|
|
||||||
return capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxSegmentLength() {
|
|
||||||
return maxSegmentLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +1,19 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
import net.sf.briar.util.ByteUtils;
|
|
||||||
|
|
||||||
class TagEncoder {
|
class TagEncoder {
|
||||||
|
|
||||||
static void encodeTag(byte[] tag, long segmentNumber, Cipher tagCipher,
|
static void encodeTag(byte[] tag, Cipher tagCipher, ErasableKey tagKey) {
|
||||||
ErasableKey tagKey) {
|
|
||||||
if(tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
if(tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
||||||
if(segmentNumber < 0 || segmentNumber > MAX_32_BIT_UNSIGNED)
|
// Blank plaintext
|
||||||
throw new IllegalArgumentException();
|
|
||||||
// Clear the tag
|
|
||||||
for(int i = 0; i < TAG_LENGTH; i++) tag[i] = 0;
|
for(int i = 0; i < TAG_LENGTH; i++) tag[i] = 0;
|
||||||
// Encode the segment number as a uint32 at the end of the tag
|
|
||||||
ByteUtils.writeUint32(segmentNumber, tag, TAG_LENGTH - 4);
|
|
||||||
try {
|
try {
|
||||||
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
|
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
|
||||||
int encrypted = tagCipher.doFinal(tag, 0, TAG_LENGTH, tag);
|
int encrypted = tagCipher.doFinal(tag, 0, TAG_LENGTH, tag);
|
||||||
@@ -31,18 +24,18 @@ class TagEncoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static long decodeTag(byte[] tag, Cipher tagCipher, ErasableKey tagKey) {
|
static boolean decodeTag(byte[] tag, Cipher tagCipher, ErasableKey tagKey) {
|
||||||
if(tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
if(tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
||||||
try {
|
try {
|
||||||
tagCipher.init(Cipher.DECRYPT_MODE, tagKey);
|
tagCipher.init(Cipher.DECRYPT_MODE, tagKey);
|
||||||
byte[] plaintext = tagCipher.doFinal(tag, 0, TAG_LENGTH);
|
byte[] plaintext = tagCipher.doFinal(tag, 0, TAG_LENGTH);
|
||||||
if(plaintext.length != TAG_LENGTH)
|
if(plaintext.length != TAG_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
// All but the last four bytes of the plaintext should be blank
|
//The plaintext should be blank
|
||||||
for(int i = 0; i < TAG_LENGTH - 4; i++) {
|
for(int i = 0; i < TAG_LENGTH; i++) {
|
||||||
if(plaintext[i] != 0) return -1;
|
if(plaintext[i] != 0) return false;
|
||||||
}
|
}
|
||||||
return ByteUtils.readUint32(plaintext, TAG_LENGTH - 4);
|
return true;
|
||||||
} catch(GeneralSecurityException e) {
|
} catch(GeneralSecurityException e) {
|
||||||
// Unsuitable cipher or key
|
// Unsuitable cipher or key
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.ACK_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.MAX_FRAME_LENGTH;
|
|
||||||
import net.sf.briar.api.FormatException;
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
/** An erasure decoder that uses k data segments and one parity segment. */
|
|
||||||
class XorErasureDecoder implements ErasureDecoder {
|
|
||||||
|
|
||||||
private final int n, headerLength;
|
|
||||||
|
|
||||||
XorErasureDecoder(int n, boolean ackHeader) {
|
|
||||||
this.n = n;
|
|
||||||
if(ackHeader) headerLength = FRAME_HEADER_LENGTH + ACK_HEADER_LENGTH;
|
|
||||||
else headerLength = FRAME_HEADER_LENGTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean decodeFrame(Frame f, Segment[] set) throws FormatException {
|
|
||||||
// We need at least n - 1 pieces
|
|
||||||
int pieces = 0;
|
|
||||||
for(int i = 0; i < n; i++) if(set[i] != null) pieces++;
|
|
||||||
if(pieces < n - 1) return false;
|
|
||||||
// All the pieces must have the same length - take the minimum
|
|
||||||
int length = MAX_FRAME_LENGTH;
|
|
||||||
for(int i = 0; i < n; i++) {
|
|
||||||
if(set[i] != null) {
|
|
||||||
int len = set[i].getLength();
|
|
||||||
if(len < length) length = len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(length * (n - 1) > MAX_FRAME_LENGTH) throw new FormatException();
|
|
||||||
// Decode the frame
|
|
||||||
byte[] dest = f.getBuffer();
|
|
||||||
int offset = 0;
|
|
||||||
if(pieces == n || set[n - 1] == null) {
|
|
||||||
// We don't need no stinkin' parity segment
|
|
||||||
for(int i = 0; i < n - 1; i++) {
|
|
||||||
byte[] src = set[i].getBuffer();
|
|
||||||
int copyLength = Math.min(length, dest.length - offset);
|
|
||||||
System.arraycopy(src, 0, dest, offset, copyLength);
|
|
||||||
offset += length;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Reconstruct the missing segment
|
|
||||||
byte[] parity = new byte[length];
|
|
||||||
int missingOffset = -1;
|
|
||||||
for(int i = 0; i < n - 1; i++) {
|
|
||||||
if(set[i] == null) {
|
|
||||||
missingOffset = offset;
|
|
||||||
} else {
|
|
||||||
byte[] src = set[i].getBuffer();
|
|
||||||
for(int j = 0; j < length; j++) parity[j] ^= src[j];
|
|
||||||
int copyLength = Math.min(length, dest.length - offset);
|
|
||||||
System.arraycopy(src, 0, dest, offset, copyLength);
|
|
||||||
}
|
|
||||||
offset += length;
|
|
||||||
}
|
|
||||||
byte[] src = set[n - 1].getBuffer();
|
|
||||||
for(int i = 0; i < length; i++) parity[i] ^= src[i];
|
|
||||||
assert missingOffset != -1;
|
|
||||||
int copyLength = Math.min(length, dest.length - missingOffset);
|
|
||||||
System.arraycopy(parity, 0, dest, missingOffset, copyLength);
|
|
||||||
}
|
|
||||||
// The frame length might not be an exact multiple of the segment length
|
|
||||||
int payload = HeaderEncoder.getPayloadLength(dest);
|
|
||||||
int padding = HeaderEncoder.getPaddingLength(dest);
|
|
||||||
int frameLength = headerLength + payload + padding + MAC_LENGTH;
|
|
||||||
if(frameLength > MAX_FRAME_LENGTH) throw new FormatException();
|
|
||||||
f.setLength(frameLength);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
/** An erasure encoder than uses k data segments and one parity segment. */
|
|
||||||
class XorErasureEncoder implements ErasureEncoder {
|
|
||||||
|
|
||||||
private final int n;
|
|
||||||
|
|
||||||
XorErasureEncoder(int n) {
|
|
||||||
this.n = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Segment[] encodeFrame(Frame f) {
|
|
||||||
Segment[] set = new Segment[n];
|
|
||||||
int length = (int) Math.ceil((float) f.getLength() / (n - 1));
|
|
||||||
for(int i = 0; i < n; i++) {
|
|
||||||
set[i] = new SegmentImpl(length);
|
|
||||||
set[i].setLength(length);
|
|
||||||
}
|
|
||||||
byte[] src = f.getBuffer(), parity = set[n - 1].getBuffer();
|
|
||||||
int offset = 0;
|
|
||||||
for(int i = 0; i < n - 1; i++) {
|
|
||||||
int copyLength = Math.min(length, src.length - offset);
|
|
||||||
System.arraycopy(src, offset, set[i].getBuffer(), 0, copyLength);
|
|
||||||
for(int j = 0; j < copyLength; j++) parity[j] ^= src[offset + j];
|
|
||||||
offset += length;
|
|
||||||
}
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -56,16 +56,8 @@
|
|||||||
<test name='net.sf.briar.transport.ConnectionWriterImplTest'/>
|
<test name='net.sf.briar.transport.ConnectionWriterImplTest'/>
|
||||||
<test name='net.sf.briar.transport.ConnectionWriterTest'/>
|
<test name='net.sf.briar.transport.ConnectionWriterTest'/>
|
||||||
<test name='net.sf.briar.transport.FrameReadWriteTest'/>
|
<test name='net.sf.briar.transport.FrameReadWriteTest'/>
|
||||||
<test name='net.sf.briar.transport.FrameWindowImplTest'/>
|
|
||||||
<test name='net.sf.briar.transport.IncomingEncryptionLayerImplTest'/>
|
<test name='net.sf.briar.transport.IncomingEncryptionLayerImplTest'/>
|
||||||
<test name='net.sf.briar.transport.IncomingErrorCorrectionLayerImplTest'/>
|
|
||||||
<test name='net.sf.briar.transport.IncomingReliabilityLayerImplTest'/>
|
|
||||||
<test name='net.sf.briar.transport.OutgoingEncryptionLayerImplTest'/>
|
<test name='net.sf.briar.transport.OutgoingEncryptionLayerImplTest'/>
|
||||||
<test name='net.sf.briar.transport.SegmentedIncomingEncryptionLayerTest'/>
|
|
||||||
<test name='net.sf.briar.transport.SegmentedOutgoingEncryptionLayerTest'/>
|
|
||||||
<test name='net.sf.briar.transport.XorErasureCodeTest'/>
|
|
||||||
<test name='net.sf.briar.transport.XorErasureDecoderTest'/>
|
|
||||||
<test name='net.sf.briar.transport.XorErasureEncoderTest'/>
|
|
||||||
<test name='net.sf.briar.util.ByteUtilsTest'/>
|
<test name='net.sf.briar.util.ByteUtilsTest'/>
|
||||||
<test name='net.sf.briar.util.FileUtilsTest'/>
|
<test name='net.sf.briar.util.FileUtilsTest'/>
|
||||||
<test name='net.sf.briar.util.StringUtilsTest'/>
|
<test name='net.sf.briar.util.StringUtilsTest'/>
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
|||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH));
|
assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH));
|
||||||
ConnectionReader conn = connectionReaderFactory.createConnectionReader(
|
ConnectionReader conn = connectionReaderFactory.createConnectionReader(
|
||||||
in, secret.clone(), tag);
|
in, secret.clone(), true);
|
||||||
InputStream in1 = conn.getInputStream();
|
InputStream in1 = conn.getInputStream();
|
||||||
ProtocolReader reader = protocolReaderFactory.createProtocolReader(in1);
|
ProtocolReader reader = protocolReaderFactory.createProtocolReader(in1);
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ public class KeyDerivationTest extends BriarTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testSixKeysAreDistinct() {
|
public void testSixKeysAreDistinct() {
|
||||||
List<ErasableKey> keys = new ArrayList<ErasableKey>();
|
List<ErasableKey> keys = new ArrayList<ErasableKey>();
|
||||||
keys.add(crypto.deriveSegmentKey(secret, true));
|
keys.add(crypto.deriveFrameKey(secret, true));
|
||||||
keys.add(crypto.deriveSegmentKey(secret, false));
|
keys.add(crypto.deriveFrameKey(secret, false));
|
||||||
keys.add(crypto.deriveTagKey(secret, true));
|
keys.add(crypto.deriveTagKey(secret, true));
|
||||||
keys.add(crypto.deriveTagKey(secret, false));
|
keys.add(crypto.deriveTagKey(secret, false));
|
||||||
keys.add(crypto.deriveMacKey(secret, true));
|
keys.add(crypto.deriveMacKey(secret, true));
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ public class SimplexConnectionReadWriteTest extends BriarTestCase {
|
|||||||
IncomingSimplexConnection batchIn = new IncomingSimplexConnection(
|
IncomingSimplexConnection batchIn = new IncomingSimplexConnection(
|
||||||
new ImmediateExecutor(), new ImmediateExecutor(), db,
|
new ImmediateExecutor(), new ImmediateExecutor(), db,
|
||||||
connRegistry, connFactory, protoFactory, ctx, transportId,
|
connRegistry, connFactory, protoFactory, ctx, transportId,
|
||||||
transport, tag);
|
transport);
|
||||||
// No messages should have been added yet
|
// No messages should have been added yet
|
||||||
assertFalse(listener.messagesAdded);
|
assertFalse(listener.messagesAdded);
|
||||||
// Read whatever needs to be read
|
// Read whatever needs to be read
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import net.sf.briar.api.transport.ConnectionReader;
|
|||||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
// FIXME: This test covers too many classes
|
|
||||||
public class ConnectionReaderImplTest extends TransportTest {
|
public class ConnectionReaderImplTest extends TransportTest {
|
||||||
|
|
||||||
public ConnectionReaderImplTest() throws Exception {
|
public ConnectionReaderImplTest() throws Exception {
|
||||||
@@ -217,14 +216,9 @@ public class ConnectionReaderImplTest extends TransportTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ConnectionReader createConnectionReader(InputStream in) {
|
private ConnectionReader createConnectionReader(InputStream in) {
|
||||||
IncomingEncryptionLayer encryption =
|
FrameReader encryption = new NullIncomingEncryptionLayer(in);
|
||||||
new NullIncomingEncryptionLayer(in);
|
FrameReader authentication = new IncomingAuthenticationLayerImpl(
|
||||||
IncomingErrorCorrectionLayer correction =
|
encryption, mac, macKey);
|
||||||
new NullIncomingErrorCorrectionLayer(encryption);
|
return new ConnectionReaderImpl(authentication);
|
||||||
IncomingAuthenticationLayer authentication =
|
|
||||||
new IncomingAuthenticationLayerImpl(correction, mac, macKey, false);
|
|
||||||
IncomingReliabilityLayer reliability =
|
|
||||||
new NullIncomingReliabilityLayer(authentication);
|
|
||||||
return new ConnectionReaderImpl(reliability, false, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -618,7 +618,7 @@ public class ConnectionRecogniserImplTest extends BriarTestCase {
|
|||||||
ErasableKey tagKey = crypto.deriveTagKey(secret, true);
|
ErasableKey tagKey = crypto.deriveTagKey(secret, true);
|
||||||
Cipher tagCipher = crypto.getTagCipher();
|
Cipher tagCipher = crypto.getTagCipher();
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
|
TagEncoder.encodeTag(tag, tagCipher, tagKey);
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import net.sf.briar.api.transport.ConnectionWriter;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
// FIXME: This test covers too many classes
|
|
||||||
public class ConnectionWriterImplTest extends TransportTest {
|
public class ConnectionWriterImplTest extends TransportTest {
|
||||||
|
|
||||||
public ConnectionWriterImplTest() throws Exception {
|
public ConnectionWriterImplTest() throws Exception {
|
||||||
@@ -107,14 +106,9 @@ public class ConnectionWriterImplTest extends TransportTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ConnectionWriter createConnectionWriter(OutputStream out) {
|
private ConnectionWriter createConnectionWriter(OutputStream out) {
|
||||||
OutgoingEncryptionLayer encryption =
|
FrameWriter encryption = new NullOutgoingEncryptionLayer(out);
|
||||||
new NullOutgoingEncryptionLayer(out);
|
FrameWriter authentication =
|
||||||
OutgoingErrorCorrectionLayer correction =
|
new OutgoingAuthenticationLayerImpl(encryption, mac, macKey);
|
||||||
new NullOutgoingErrorCorrectionLayer(encryption);
|
return new ConnectionWriterImpl(authentication);
|
||||||
OutgoingAuthenticationLayer authentication =
|
|
||||||
new OutgoingAuthenticationLayerImpl(correction, mac, macKey);
|
|
||||||
OutgoingReliabilityLayer reliability =
|
|
||||||
new NullOutgoingReliabilityLayer(authentication);
|
|
||||||
return new ConnectionWriterImpl(reliability, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,25 +27,25 @@ import com.google.inject.Injector;
|
|||||||
public class FrameReadWriteTest extends BriarTestCase {
|
public class FrameReadWriteTest extends BriarTestCase {
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
private final Cipher tagCipher, segCipher;
|
private final Cipher tagCipher, frameCipher;
|
||||||
private final Mac mac;
|
private final Mac mac;
|
||||||
private final Random random;
|
private final Random random;
|
||||||
private final byte[] outSecret;
|
private final byte[] outSecret;
|
||||||
private final ErasableKey tagKey, segKey, macKey;
|
private final ErasableKey tagKey, frameKey, macKey;
|
||||||
|
|
||||||
public FrameReadWriteTest() {
|
public FrameReadWriteTest() {
|
||||||
super();
|
super();
|
||||||
Injector i = Guice.createInjector(new CryptoModule());
|
Injector i = Guice.createInjector(new CryptoModule());
|
||||||
crypto = i.getInstance(CryptoComponent.class);
|
crypto = i.getInstance(CryptoComponent.class);
|
||||||
tagCipher = crypto.getTagCipher();
|
tagCipher = crypto.getTagCipher();
|
||||||
segCipher = crypto.getSegmentCipher();
|
frameCipher = crypto.getFrameCipher();
|
||||||
mac = crypto.getMac();
|
mac = crypto.getMac();
|
||||||
random = new Random();
|
random = new Random();
|
||||||
// Since we're sending frames to ourselves, we only need outgoing keys
|
// Since we're sending frames to ourselves, we only need outgoing keys
|
||||||
outSecret = new byte[32];
|
outSecret = new byte[32];
|
||||||
random.nextBytes(outSecret);
|
random.nextBytes(outSecret);
|
||||||
tagKey = crypto.deriveTagKey(outSecret, true);
|
tagKey = crypto.deriveTagKey(outSecret, true);
|
||||||
segKey = crypto.deriveSegmentKey(outSecret, true);
|
frameKey = crypto.deriveFrameKey(outSecret, true);
|
||||||
macKey = crypto.deriveMacKey(outSecret, true);
|
macKey = crypto.deriveMacKey(outSecret, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ public class FrameReadWriteTest extends BriarTestCase {
|
|||||||
private void testWriteAndRead(boolean initiator) throws Exception {
|
private void testWriteAndRead(boolean initiator) throws Exception {
|
||||||
// Encode the tag
|
// Encode the tag
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
|
TagEncoder.encodeTag(tag, tagCipher, tagKey);
|
||||||
// Generate two random frames
|
// Generate two random frames
|
||||||
byte[] frame = new byte[12345];
|
byte[] frame = new byte[12345];
|
||||||
random.nextBytes(frame);
|
random.nextBytes(frame);
|
||||||
@@ -70,21 +70,15 @@ public class FrameReadWriteTest extends BriarTestCase {
|
|||||||
random.nextBytes(frame1);
|
random.nextBytes(frame1);
|
||||||
// Copy the keys - the copies will be erased
|
// Copy the keys - the copies will be erased
|
||||||
ErasableKey tagCopy = tagKey.copy();
|
ErasableKey tagCopy = tagKey.copy();
|
||||||
ErasableKey segCopy = segKey.copy();
|
ErasableKey frameCopy = frameKey.copy();
|
||||||
ErasableKey macCopy = macKey.copy();
|
ErasableKey macCopy = macKey.copy();
|
||||||
// Write the frames
|
// Write the frames
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
OutgoingEncryptionLayer encryptionOut = new OutgoingEncryptionLayerImpl(
|
FrameWriter encryptionOut = new OutgoingEncryptionLayerImpl(out,
|
||||||
out, Long.MAX_VALUE, tagCipher, segCipher, tagCopy, segCopy,
|
Long.MAX_VALUE, tagCipher, frameCipher, tagCopy, frameCopy);
|
||||||
false);
|
FrameWriter authenticationOut = new OutgoingAuthenticationLayerImpl(
|
||||||
OutgoingErrorCorrectionLayer correctionOut =
|
encryptionOut, mac, macCopy);
|
||||||
new NullOutgoingErrorCorrectionLayer(encryptionOut);
|
ConnectionWriter writer = new ConnectionWriterImpl(authenticationOut);
|
||||||
OutgoingAuthenticationLayer authenticationOut =
|
|
||||||
new OutgoingAuthenticationLayerImpl(correctionOut, mac, macCopy);
|
|
||||||
OutgoingReliabilityLayer reliabilityOut =
|
|
||||||
new NullOutgoingReliabilityLayer(authenticationOut);
|
|
||||||
ConnectionWriter writer = new ConnectionWriterImpl(reliabilityOut,
|
|
||||||
false);
|
|
||||||
OutputStream out1 = writer.getOutputStream();
|
OutputStream out1 = writer.getOutputStream();
|
||||||
out1.write(frame);
|
out1.write(frame);
|
||||||
out1.flush();
|
out1.flush();
|
||||||
@@ -95,20 +89,13 @@ public class FrameReadWriteTest extends BriarTestCase {
|
|||||||
byte[] recoveredTag = new byte[TAG_LENGTH];
|
byte[] recoveredTag = new byte[TAG_LENGTH];
|
||||||
assertEquals(TAG_LENGTH, in.read(recoveredTag));
|
assertEquals(TAG_LENGTH, in.read(recoveredTag));
|
||||||
assertArrayEquals(tag, recoveredTag);
|
assertArrayEquals(tag, recoveredTag);
|
||||||
assertEquals(0L, TagEncoder.decodeTag(tag, tagCipher, tagKey));
|
assertTrue(TagEncoder.decodeTag(tag, tagCipher, tagKey));
|
||||||
// Read the frames back
|
// Read the frames back
|
||||||
IncomingEncryptionLayer encryptionIn = new IncomingEncryptionLayerImpl(
|
FrameReader encryptionIn = new IncomingEncryptionLayerImpl(in,
|
||||||
in, tagCipher, segCipher, tagKey, segKey, false, false,
|
tagCipher, frameCipher, tagKey, frameKey, false);
|
||||||
recoveredTag);
|
FrameReader authenticationIn = new IncomingAuthenticationLayerImpl(
|
||||||
IncomingErrorCorrectionLayer correctionIn =
|
encryptionIn, mac, macKey);
|
||||||
new NullIncomingErrorCorrectionLayer(encryptionIn);
|
ConnectionReader reader = new ConnectionReaderImpl(authenticationIn);
|
||||||
IncomingAuthenticationLayer authenticationIn =
|
|
||||||
new IncomingAuthenticationLayerImpl(correctionIn, mac, macKey,
|
|
||||||
false);
|
|
||||||
IncomingReliabilityLayer reliabilityIn =
|
|
||||||
new NullIncomingReliabilityLayer(authenticationIn);
|
|
||||||
ConnectionReader reader = new ConnectionReaderImpl(reliabilityIn, false,
|
|
||||||
false);
|
|
||||||
InputStream in1 = reader.getInputStream();
|
InputStream in1 = reader.getInputStream();
|
||||||
byte[] recovered = new byte[frame.length];
|
byte[] recovered = new byte[frame.length];
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.FRAME_WINDOW_SIZE;
|
|
||||||
import net.sf.briar.BriarTestCase;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class FrameWindowImplTest extends BriarTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWindowSliding() {
|
|
||||||
FrameWindow w = new FrameWindowImpl();
|
|
||||||
for(int i = 0; i < 100; i++) {
|
|
||||||
assertTrue(w.contains(i));
|
|
||||||
assertTrue(w.remove(i));
|
|
||||||
assertFalse(w.contains(i));
|
|
||||||
}
|
|
||||||
for(int i = 100; i < 100 + FRAME_WINDOW_SIZE; i++) {
|
|
||||||
assertTrue(w.contains(i));
|
|
||||||
assertFalse(w.isTooHigh(i));
|
|
||||||
}
|
|
||||||
assertFalse(w.contains(100 + FRAME_WINDOW_SIZE));
|
|
||||||
assertTrue(w.isTooHigh(100 + FRAME_WINDOW_SIZE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWindowJumping() {
|
|
||||||
FrameWindow w = new FrameWindowImpl();
|
|
||||||
// Base of the window is 0
|
|
||||||
for(int i = 0; i < FRAME_WINDOW_SIZE; i++) assertTrue(w.contains(i));
|
|
||||||
assertFalse(w.contains(FRAME_WINDOW_SIZE));
|
|
||||||
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE - 1));
|
|
||||||
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE));
|
|
||||||
// Remove all numbers except 0 and 5
|
|
||||||
for(int i = 1; i < 5; i++) assertTrue(w.remove(i));
|
|
||||||
for(int i = 6; i < FRAME_WINDOW_SIZE; i++) assertTrue(w.remove(i));
|
|
||||||
// Base of the window should still be 0
|
|
||||||
assertTrue(w.contains(0));
|
|
||||||
for(int i = 1; i < 5; i++) assertFalse(w.contains(i));
|
|
||||||
assertTrue(w.contains(5));
|
|
||||||
for(int i = 6; i < FRAME_WINDOW_SIZE; i++) assertFalse(w.contains(i));
|
|
||||||
assertFalse(w.contains(FRAME_WINDOW_SIZE));
|
|
||||||
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE - 1));
|
|
||||||
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE));
|
|
||||||
// Remove 0
|
|
||||||
assertTrue(w.remove(0));
|
|
||||||
// Base of the window should now be 5
|
|
||||||
for(int i = 0; i < 5; i++) assertFalse(w.contains(i));
|
|
||||||
assertTrue(w.contains(5));
|
|
||||||
for(int i = 6; i < FRAME_WINDOW_SIZE; i++) assertFalse(w.contains(i));
|
|
||||||
for(int i = FRAME_WINDOW_SIZE; i < FRAME_WINDOW_SIZE + 5; i++) {
|
|
||||||
assertTrue(w.contains(i));
|
|
||||||
}
|
|
||||||
assertFalse(w.contains(FRAME_WINDOW_SIZE + 5));
|
|
||||||
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE + 4));
|
|
||||||
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE + 5));
|
|
||||||
// Remove all numbers except 5
|
|
||||||
for(int i = FRAME_WINDOW_SIZE; i < FRAME_WINDOW_SIZE + 5; i++) {
|
|
||||||
assertTrue(w.remove(i));
|
|
||||||
}
|
|
||||||
// Base of the window should still be 5
|
|
||||||
assertTrue(w.contains(5));
|
|
||||||
for(int i = 6; i < FRAME_WINDOW_SIZE + 5; i++) {
|
|
||||||
assertFalse(w.contains(i));
|
|
||||||
}
|
|
||||||
assertFalse(w.contains(FRAME_WINDOW_SIZE + 5));
|
|
||||||
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE + 4));
|
|
||||||
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE + 5));
|
|
||||||
// Remove 5
|
|
||||||
assertTrue(w.remove(5));
|
|
||||||
// Base of the window should now be FRAME_WINDOW_SIZE + 5
|
|
||||||
for(int i = 0; i < FRAME_WINDOW_SIZE + 5; i++) {
|
|
||||||
assertFalse(w.contains(i));
|
|
||||||
}
|
|
||||||
for(int i = FRAME_WINDOW_SIZE + 5; i < FRAME_WINDOW_SIZE * 2 + 5; i++) {
|
|
||||||
assertTrue(w.contains(i));
|
|
||||||
}
|
|
||||||
assertFalse(w.contains(FRAME_WINDOW_SIZE * 2 + 5));
|
|
||||||
assertFalse(w.isTooHigh(FRAME_WINDOW_SIZE * 2 + 4));
|
|
||||||
assertTrue(w.isTooHigh(FRAME_WINDOW_SIZE * 2 + 5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,6 @@ import javax.crypto.spec.IvParameterSpec;
|
|||||||
import net.sf.briar.BriarTestCase;
|
import net.sf.briar.BriarTestCase;
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
import net.sf.briar.api.crypto.CryptoComponent;
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
import net.sf.briar.crypto.CryptoModule;
|
import net.sf.briar.crypto.CryptoModule;
|
||||||
|
|
||||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||||
@@ -23,112 +22,106 @@ import com.google.inject.Injector;
|
|||||||
|
|
||||||
public class IncomingEncryptionLayerImplTest extends BriarTestCase {
|
public class IncomingEncryptionLayerImplTest extends BriarTestCase {
|
||||||
|
|
||||||
private final Cipher tagCipher, segCipher;
|
private final Cipher tagCipher, frameCipher;
|
||||||
private final ErasableKey tagKey, segKey;
|
private final ErasableKey tagKey, frameKey;
|
||||||
|
|
||||||
public IncomingEncryptionLayerImplTest() {
|
public IncomingEncryptionLayerImplTest() {
|
||||||
super();
|
super();
|
||||||
Injector i = Guice.createInjector(new CryptoModule());
|
Injector i = Guice.createInjector(new CryptoModule());
|
||||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||||
tagCipher = crypto.getTagCipher();
|
tagCipher = crypto.getTagCipher();
|
||||||
segCipher = crypto.getSegmentCipher();
|
frameCipher = crypto.getFrameCipher();
|
||||||
tagKey = crypto.generateTestKey();
|
tagKey = crypto.generateTestKey();
|
||||||
segKey = crypto.generateTestKey();
|
frameKey = crypto.generateTestKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDecryptionWithFirstSegmentTagged() throws Exception {
|
public void testDecryptionWithTag() throws Exception {
|
||||||
// Calculate the tag for the first segment
|
// Calculate the tag
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
|
TagEncoder.encodeTag(tag, tagCipher, tagKey);
|
||||||
// Calculate the ciphertext for the first segment
|
// Calculate the ciphertext for the first frame
|
||||||
byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
|
byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
|
||||||
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
|
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
|
||||||
byte[] iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
|
byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
|
||||||
byte[] ciphertext = segCipher.doFinal(plaintext, 0, plaintext.length);
|
byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length);
|
||||||
// Calculate the ciphertext for the second segment
|
// Calculate the ciphertext for the second frame
|
||||||
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
|
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
|
||||||
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
|
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
|
||||||
IvEncoder.updateIv(iv, 1L);
|
IvEncoder.updateIv(iv, 1L);
|
||||||
ivSpec = new IvParameterSpec(iv);
|
ivSpec = new IvParameterSpec(iv);
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
|
||||||
byte[] ciphertext1 = segCipher.doFinal(plaintext1, 0,
|
byte[] ciphertext1 = frameCipher.doFinal(plaintext1, 0,
|
||||||
plaintext1.length);
|
plaintext1.length);
|
||||||
// Concatenate the ciphertexts, excluding the first tag
|
// Concatenate the ciphertexts, including the tag
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
out.write(tag);
|
||||||
out.write(ciphertext);
|
out.write(ciphertext);
|
||||||
out.write(ciphertext1);
|
out.write(ciphertext1);
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||||
// Use the encryption layer to decrypt the ciphertext
|
// Use the encryption layer to decrypt the ciphertext
|
||||||
IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
|
FrameReader decrypter = new IncomingEncryptionLayerImpl(in, tagCipher,
|
||||||
tagCipher, segCipher, tagKey, segKey, false, false, tag);
|
frameCipher, tagKey, frameKey, true);
|
||||||
// First segment
|
// First frame
|
||||||
Segment s = new SegmentImpl();
|
Frame f = new Frame();
|
||||||
assertTrue(decrypter.readSegment(s));
|
assertTrue(decrypter.readFrame(f));
|
||||||
assertEquals(plaintext.length, s.getLength());
|
assertEquals(plaintext.length, f.getLength());
|
||||||
assertEquals(0L, s.getSegmentNumber());
|
byte[] decrypted = f.getBuffer();
|
||||||
byte[] decrypted = s.getBuffer();
|
assertEquals(0L, HeaderEncoder.getFrameNumber(decrypted));
|
||||||
for(int i = 0; i < plaintext.length; i++) {
|
for(int i = 0; i < plaintext.length; i++) {
|
||||||
assertEquals(plaintext[i], decrypted[i]);
|
assertEquals(plaintext[i], decrypted[i]);
|
||||||
}
|
}
|
||||||
// Second segment
|
// Second frame
|
||||||
assertTrue(decrypter.readSegment(s));
|
assertTrue(decrypter.readFrame(f));
|
||||||
assertEquals(plaintext1.length, s.getLength());
|
assertEquals(plaintext1.length, f.getLength());
|
||||||
assertEquals(1L, s.getSegmentNumber());
|
decrypted = f.getBuffer();
|
||||||
decrypted = s.getBuffer();
|
assertEquals(1L, HeaderEncoder.getFrameNumber(decrypted));
|
||||||
for(int i = 0; i < plaintext1.length; i++) {
|
for(int i = 0; i < plaintext1.length; i++) {
|
||||||
assertEquals(plaintext1[i], decrypted[i]);
|
assertEquals(plaintext1[i], decrypted[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDecryptionWithEverySegmentTagged() throws Exception {
|
public void testDecryptionWithoutTag() throws Exception {
|
||||||
// Calculate the tag for the first segment
|
// Calculate the ciphertext for the first frame
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
|
||||||
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
|
|
||||||
// Calculate the ciphertext for the first segment
|
|
||||||
byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
|
byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
|
||||||
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
|
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
|
||||||
byte[] iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
|
byte[] iv = IvEncoder.encodeIv(0L, frameCipher.getBlockSize());
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
|
||||||
byte[] ciphertext = segCipher.doFinal(plaintext, 0, plaintext.length);
|
byte[] ciphertext = frameCipher.doFinal(plaintext, 0, plaintext.length);
|
||||||
// Calculate the tag for the second segment
|
// Calculate the ciphertext for the second frame
|
||||||
byte[] tag1 = new byte[TAG_LENGTH];
|
|
||||||
TagEncoder.encodeTag(tag1, 1L, tagCipher, tagKey);
|
|
||||||
// Calculate the ciphertext for the second segment
|
|
||||||
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
|
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
|
||||||
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
|
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
|
||||||
IvEncoder.updateIv(iv, 1L);
|
IvEncoder.updateIv(iv, 1L);
|
||||||
ivSpec = new IvParameterSpec(iv);
|
ivSpec = new IvParameterSpec(iv);
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
|
||||||
byte[] ciphertext1 = segCipher.doFinal(plaintext1, 0,
|
byte[] ciphertext1 = frameCipher.doFinal(plaintext1, 0,
|
||||||
plaintext1.length);
|
plaintext1.length);
|
||||||
// Concatenate the ciphertexts, excluding the first tag
|
// Concatenate the ciphertexts, excluding the tag
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
out.write(ciphertext);
|
out.write(ciphertext);
|
||||||
out.write(tag1);
|
|
||||||
out.write(ciphertext1);
|
out.write(ciphertext1);
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||||
// Use the encryption layer to decrypt the ciphertext
|
// Use the encryption layer to decrypt the ciphertext
|
||||||
IncomingEncryptionLayer decrypter = new IncomingEncryptionLayerImpl(in,
|
FrameReader decrypter = new IncomingEncryptionLayerImpl(in, tagCipher,
|
||||||
tagCipher, segCipher, tagKey, segKey, true, false, tag);
|
frameCipher, tagKey, frameKey, false);
|
||||||
// First segment
|
// First frame
|
||||||
Segment s = new SegmentImpl();
|
Frame f = new Frame();
|
||||||
assertTrue(decrypter.readSegment(s));
|
assertTrue(decrypter.readFrame(f));
|
||||||
assertEquals(plaintext.length, s.getLength());
|
assertEquals(plaintext.length, f.getLength());
|
||||||
assertEquals(0L, s.getSegmentNumber());
|
byte[] decrypted = f.getBuffer();
|
||||||
byte[] decrypted = s.getBuffer();
|
assertEquals(0L, HeaderEncoder.getFrameNumber(decrypted));
|
||||||
for(int i = 0; i < plaintext.length; i++) {
|
for(int i = 0; i < plaintext.length; i++) {
|
||||||
assertEquals(plaintext[i], decrypted[i]);
|
assertEquals(plaintext[i], decrypted[i]);
|
||||||
}
|
}
|
||||||
// Second segment
|
// Second frame
|
||||||
assertTrue(decrypter.readSegment(s));
|
assertTrue(decrypter.readFrame(f));
|
||||||
assertEquals(plaintext1.length, s.getLength());
|
assertEquals(plaintext1.length, f.getLength());
|
||||||
assertEquals(1L, s.getSegmentNumber());
|
assertEquals(1L, HeaderEncoder.getFrameNumber(decrypted));
|
||||||
decrypted = s.getBuffer();
|
decrypted = f.getBuffer();
|
||||||
for(int i = 0; i < plaintext1.length; i++) {
|
for(int i = 0; i < plaintext1.length; i++) {
|
||||||
assertEquals(plaintext1[i], decrypted[i]);
|
assertEquals(plaintext1[i], decrypted[i]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,168 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import net.sf.briar.BriarTestCase;
|
|
||||||
import net.sf.briar.api.FormatException;
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
import org.jmock.Expectations;
|
|
||||||
import org.jmock.Mockery;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class IncomingErrorCorrectionLayerImplTest extends BriarTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDiscardedSegmentsAreCounted() throws Exception {
|
|
||||||
LinkedList<Long> segmentNumbers = new LinkedList<Long>();
|
|
||||||
segmentNumbers.add(123L); // 123 / 3 = frame number 41
|
|
||||||
segmentNumbers.add(456L); // 456 / 3 = frame number 152
|
|
||||||
segmentNumbers.add(0L); // 0 / 3 = frame number 0
|
|
||||||
IncomingEncryptionLayer in = new TestIncomingEncryptionLayer(
|
|
||||||
segmentNumbers, 1234);
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final ErasureDecoder decoder = context.mock(ErasureDecoder.class);
|
|
||||||
final FrameWindow window = context.mock(FrameWindow.class);
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
// First segment
|
|
||||||
one(window).contains(41L);
|
|
||||||
will(returnValue(false));
|
|
||||||
one(window).isTooHigh(41L);
|
|
||||||
will(returnValue(true));
|
|
||||||
// Second segment
|
|
||||||
one(window).contains(152L);
|
|
||||||
will(returnValue(false));
|
|
||||||
one(window).isTooHigh(152L);
|
|
||||||
will(returnValue(true));
|
|
||||||
// Third segment
|
|
||||||
one(window).contains(0L);
|
|
||||||
will(returnValue(true));
|
|
||||||
one(decoder).decodeFrame(with(any(Frame.class)),
|
|
||||||
with(any(Segment[].class)));
|
|
||||||
will(returnValue(false));
|
|
||||||
}});
|
|
||||||
|
|
||||||
IncomingErrorCorrectionLayerImpl err =
|
|
||||||
new IncomingErrorCorrectionLayerImpl(in, decoder, 3, 2);
|
|
||||||
Frame f = new Frame();
|
|
||||||
assertFalse(err.readFrame(f, window));
|
|
||||||
Map<Long, Integer> discardCounts = err.getDiscardCounts();
|
|
||||||
assertEquals(2, discardCounts.size());
|
|
||||||
assertEquals(Integer.valueOf(1), discardCounts.get(41L));
|
|
||||||
assertEquals(Integer.valueOf(1), discardCounts.get(152L));
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTooManyDiscardedSegmentsCauseException() throws Exception {
|
|
||||||
LinkedList<Long> segmentNumbers = new LinkedList<Long>();
|
|
||||||
segmentNumbers.add(123L); // 123 / 3 = frame number 41
|
|
||||||
segmentNumbers.add(124L); // 124 / 3 = frame number 41
|
|
||||||
IncomingEncryptionLayer in = new TestIncomingEncryptionLayer(
|
|
||||||
segmentNumbers, 1234);
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final ErasureDecoder decoder = context.mock(ErasureDecoder.class);
|
|
||||||
final FrameWindow window = context.mock(FrameWindow.class);
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
// First segment
|
|
||||||
one(window).contains(41L);
|
|
||||||
will(returnValue(false));
|
|
||||||
one(window).isTooHigh(41L);
|
|
||||||
will(returnValue(true));
|
|
||||||
// Second segment
|
|
||||||
one(window).contains(41L);
|
|
||||||
will(returnValue(false));
|
|
||||||
one(window).isTooHigh(41L);
|
|
||||||
will(returnValue(true));
|
|
||||||
}});
|
|
||||||
IncomingErrorCorrectionLayerImpl err =
|
|
||||||
new IncomingErrorCorrectionLayerImpl(in, decoder, 3, 2);
|
|
||||||
Frame f = new Frame();
|
|
||||||
try {
|
|
||||||
err.readFrame(f, window);
|
|
||||||
fail();
|
|
||||||
} catch(FormatException expected) {}
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetsAndDiscardedSegmentsAreFreed() throws Exception {
|
|
||||||
LinkedList<Long> segmentNumbers = new LinkedList<Long>();
|
|
||||||
segmentNumbers.add(96L); // 96 / 3 = frame number 32
|
|
||||||
segmentNumbers.add(0L); // 0 / 3 = frame number 0
|
|
||||||
segmentNumbers.add(1L); // 1 / 3 = frame number 0
|
|
||||||
IncomingEncryptionLayer in = new TestIncomingEncryptionLayer(
|
|
||||||
segmentNumbers, 1234);
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final ErasureDecoder decoder = context.mock(ErasureDecoder.class);
|
|
||||||
final FrameWindow window = context.mock(FrameWindow.class);
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
// First segment
|
|
||||||
one(window).contains(32L);
|
|
||||||
will(returnValue(false));
|
|
||||||
one(window).isTooHigh(32L);
|
|
||||||
will(returnValue(true));
|
|
||||||
// Second segment
|
|
||||||
one(window).contains(0L);
|
|
||||||
will(returnValue(true));
|
|
||||||
one(decoder).decodeFrame(with(any(Frame.class)),
|
|
||||||
with(any(Segment[].class)));
|
|
||||||
will(returnValue(false));
|
|
||||||
// Third segment
|
|
||||||
one(window).contains(0L);
|
|
||||||
will(returnValue(true));
|
|
||||||
one(decoder).decodeFrame(with(any(Frame.class)),
|
|
||||||
with(any(Segment[].class)));
|
|
||||||
will(returnValue(true));
|
|
||||||
// Second call, new window
|
|
||||||
one(window).contains(0L);
|
|
||||||
will(returnValue(false));
|
|
||||||
one(window).isTooHigh(32L);
|
|
||||||
will(returnValue(false));
|
|
||||||
}});
|
|
||||||
IncomingErrorCorrectionLayerImpl err =
|
|
||||||
new IncomingErrorCorrectionLayerImpl(in, decoder, 3, 2);
|
|
||||||
Frame f = new Frame();
|
|
||||||
// The first call discards one segment and decodes two
|
|
||||||
assertTrue(err.readFrame(f, window));
|
|
||||||
// The second call reaches EOF
|
|
||||||
assertFalse(err.readFrame(f, window));
|
|
||||||
// The segment set and discard count should have been freed
|
|
||||||
Map<Long, Segment[]> segmentSets = err.getSegmentSets();
|
|
||||||
assertTrue(segmentSets.isEmpty());
|
|
||||||
Map<Long, Integer> discardCounts = err.getDiscardCounts();
|
|
||||||
assertTrue(discardCounts.isEmpty());
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TestIncomingEncryptionLayer
|
|
||||||
implements IncomingEncryptionLayer {
|
|
||||||
|
|
||||||
private final LinkedList<Long> segmentNumbers;
|
|
||||||
private final int length;
|
|
||||||
|
|
||||||
private TestIncomingEncryptionLayer(LinkedList<Long> segmentNumbers,
|
|
||||||
int length) {
|
|
||||||
this.segmentNumbers = segmentNumbers;
|
|
||||||
this.length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean readSegment(Segment s) throws IOException,
|
|
||||||
InvalidDataException {
|
|
||||||
Long segmentNumber = segmentNumbers.poll();
|
|
||||||
if(segmentNumber == null) return false;
|
|
||||||
s.setSegmentNumber(segmentNumber);
|
|
||||||
s.setLength(length);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxSegmentLength() {
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
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_WINDOW_SIZE;
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.sf.briar.BriarTestCase;
|
|
||||||
import net.sf.briar.api.transport.ConnectionReader;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class IncomingReliabilityLayerImplTest extends BriarTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNoReordering() throws Exception {
|
|
||||||
List<Integer> frameNumbers = new ArrayList<Integer>();
|
|
||||||
// Receive FRAME_WINDOW_SIZE * 2 frames in the correct order
|
|
||||||
for(int i = 0; i < FRAME_WINDOW_SIZE * 2; i++) frameNumbers.add(i);
|
|
||||||
IncomingAuthenticationLayer authentication =
|
|
||||||
new TestIncomingAuthenticationLayer(frameNumbers);
|
|
||||||
IncomingReliabilityLayerImpl reliability =
|
|
||||||
new IncomingReliabilityLayerImpl(authentication);
|
|
||||||
ConnectionReader reader = new ConnectionReaderImpl(reliability, false,
|
|
||||||
false);
|
|
||||||
InputStream in = reader.getInputStream();
|
|
||||||
for(int i = 0; i < FRAME_WINDOW_SIZE * 2; i++) {
|
|
||||||
for(int j = 0; j < 100; j++) assertEquals(i, in.read());
|
|
||||||
}
|
|
||||||
assertEquals(-1, in.read());
|
|
||||||
// No free frames should be cached
|
|
||||||
assertEquals(0, reliability.getFreeFramesCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReordering() throws Exception {
|
|
||||||
List<Integer> frameNumbers = new ArrayList<Integer>();
|
|
||||||
// Receive the first FRAME_WINDOW_SIZE frames in a random order
|
|
||||||
for(int i = 0; i < FRAME_WINDOW_SIZE; i++) frameNumbers.add(i);
|
|
||||||
Collections.shuffle(frameNumbers);
|
|
||||||
// Receive the next FRAME_WINDOW_SIZE frames in the correct order
|
|
||||||
for(int i = FRAME_WINDOW_SIZE; i < FRAME_WINDOW_SIZE * 2; i++) {
|
|
||||||
frameNumbers.add(i);
|
|
||||||
}
|
|
||||||
// The reliability layer should reorder the frames
|
|
||||||
IncomingAuthenticationLayer authentication =
|
|
||||||
new TestIncomingAuthenticationLayer(frameNumbers);
|
|
||||||
IncomingReliabilityLayerImpl reliability =
|
|
||||||
new IncomingReliabilityLayerImpl(authentication);
|
|
||||||
ConnectionReader reader = new ConnectionReaderImpl(reliability, false,
|
|
||||||
false);
|
|
||||||
InputStream in = reader.getInputStream();
|
|
||||||
for(int i = 0; i < FRAME_WINDOW_SIZE * 2; i++) {
|
|
||||||
for(int j = 0; j < 100; j++) assertEquals(i, in.read());
|
|
||||||
}
|
|
||||||
assertEquals(-1, in.read());
|
|
||||||
// Fewer than FRAME_WINDOW_SIZE free frames should be cached
|
|
||||||
assertTrue(reliability.getFreeFramesCount() < 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TestIncomingAuthenticationLayer
|
|
||||||
implements IncomingAuthenticationLayer {
|
|
||||||
|
|
||||||
private final List<Integer> frameNumbers;
|
|
||||||
|
|
||||||
private int index;
|
|
||||||
|
|
||||||
private TestIncomingAuthenticationLayer(List<Integer> frameNumbers) {
|
|
||||||
this.frameNumbers = frameNumbers;
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean readFrame(Frame f, FrameWindow window) {
|
|
||||||
if(index >= frameNumbers.size()) return false;
|
|
||||||
int frameNumber = frameNumbers.get(index);
|
|
||||||
assertTrue(window.contains(frameNumber));
|
|
||||||
index++;
|
|
||||||
byte[] buf = f.getBuffer();
|
|
||||||
HeaderEncoder.encodeHeader(buf, frameNumber, 100, 0);
|
|
||||||
for(int i = 0; i < 100; i++) {
|
|
||||||
buf[FRAME_HEADER_LENGTH + i] = (byte) frameNumber;
|
|
||||||
}
|
|
||||||
f.setLength(FRAME_HEADER_LENGTH + 100 + MAC_LENGTH);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
|
||||||
return FRAME_HEADER_LENGTH + 100 + MAC_LENGTH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,29 +3,24 @@ 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;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import net.sf.briar.api.FormatException;
|
import net.sf.briar.api.FormatException;
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
/** An encryption layer that performs no encryption. */
|
/** An encryption layer that performs no encryption. */
|
||||||
class NullIncomingEncryptionLayer implements IncomingEncryptionLayer {
|
class NullIncomingEncryptionLayer implements FrameReader {
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
|
|
||||||
private long segmentNumber = 0L;
|
|
||||||
|
|
||||||
NullIncomingEncryptionLayer(InputStream in) {
|
NullIncomingEncryptionLayer(InputStream in) {
|
||||||
this.in = in;
|
this.in = in;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean readSegment(Segment s) throws IOException {
|
public boolean readFrame(Frame f) throws IOException {
|
||||||
byte[] buf = s.getBuffer();
|
byte[] buf = f.getBuffer();
|
||||||
// Read the frame header
|
// Read the frame header
|
||||||
int offset = 0, length = FRAME_HEADER_LENGTH;
|
int offset = 0, length = FRAME_HEADER_LENGTH;
|
||||||
while(offset < length) {
|
while(offset < length) {
|
||||||
@@ -47,12 +42,7 @@ class NullIncomingEncryptionLayer implements IncomingEncryptionLayer {
|
|||||||
if(read == -1) throw new EOFException();
|
if(read == -1) throw new EOFException();
|
||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
s.setLength(length);
|
f.setLength(length);
|
||||||
s.setSegmentNumber(segmentNumber++);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxSegmentLength() {
|
|
||||||
return MAX_SEGMENT_LENGTH - TAG_LENGTH;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
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;
|
||||||
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
/** An encryption layer that performs no encryption. */
|
/** An encryption layer that performs no encryption. */
|
||||||
class NullOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
|
class NullOutgoingEncryptionLayer implements FrameWriter {
|
||||||
|
|
||||||
private final OutputStream out;
|
private final OutputStream out;
|
||||||
|
|
||||||
@@ -25,9 +20,9 @@ class NullOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
|
|||||||
this.capacity = capacity;
|
this.capacity = capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeSegment(Segment s) throws IOException {
|
public void writeFrame(Frame f) throws IOException {
|
||||||
out.write(s.getBuffer(), 0, s.getLength());
|
out.write(f.getBuffer(), 0, f.getLength());
|
||||||
capacity -= s.getLength();
|
capacity -= f.getLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush() throws IOException {
|
public void flush() throws IOException {
|
||||||
@@ -37,8 +32,4 @@ class NullOutgoingEncryptionLayer implements OutgoingEncryptionLayer {
|
|||||||
public long getRemainingCapacity() {
|
public long getRemainingCapacity() {
|
||||||
return capacity;
|
return capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxSegmentLength() {
|
|
||||||
return MAX_SEGMENT_LENGTH - TAG_LENGTH;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import javax.crypto.spec.IvParameterSpec;
|
|||||||
import net.sf.briar.BriarTestCase;
|
import net.sf.briar.BriarTestCase;
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
import net.sf.briar.api.crypto.CryptoComponent;
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
import net.sf.briar.crypto.CryptoModule;
|
import net.sf.briar.crypto.CryptoModule;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -23,36 +22,36 @@ public class OutgoingEncryptionLayerImplTest extends BriarTestCase {
|
|||||||
|
|
||||||
private static final int MAC_LENGTH = 32;
|
private static final int MAC_LENGTH = 32;
|
||||||
|
|
||||||
private final Cipher tagCipher, segCipher;
|
private final Cipher tagCipher, frameCipher;
|
||||||
private final ErasableKey tagKey, segKey;
|
private final ErasableKey tagKey, frameKey;
|
||||||
|
|
||||||
public OutgoingEncryptionLayerImplTest() {
|
public OutgoingEncryptionLayerImplTest() {
|
||||||
super();
|
super();
|
||||||
Injector i = Guice.createInjector(new CryptoModule());
|
Injector i = Guice.createInjector(new CryptoModule());
|
||||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||||
tagCipher = crypto.getTagCipher();
|
tagCipher = crypto.getTagCipher();
|
||||||
segCipher = crypto.getSegmentCipher();
|
frameCipher = crypto.getFrameCipher();
|
||||||
tagKey = crypto.generateTestKey();
|
tagKey = crypto.generateTestKey();
|
||||||
segKey = crypto.generateTestKey();
|
frameKey = crypto.generateTestKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncryptionWithFirstSegmentTagged() throws Exception {
|
public void testEncryptionWithTag() throws Exception {
|
||||||
// Calculate the expected tag
|
// Calculate the expected tag
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
|
TagEncoder.encodeTag(tag, tagCipher, tagKey);
|
||||||
// Calculate the expected ciphertext for the first segment
|
// Calculate the expected ciphertext for the first frame
|
||||||
byte[] iv = new byte[segCipher.getBlockSize()];
|
byte[] iv = new byte[frameCipher.getBlockSize()];
|
||||||
byte[] plaintext = new byte[123 + MAC_LENGTH];
|
byte[] plaintext = new byte[123 + MAC_LENGTH];
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
|
||||||
byte[] ciphertext = segCipher.doFinal(plaintext);
|
byte[] ciphertext = frameCipher.doFinal(plaintext);
|
||||||
// Calculate the expected ciphertext for the second segment
|
// Calculate the expected ciphertext for the second frame
|
||||||
byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
|
byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
|
||||||
IvEncoder.updateIv(iv, 1L);
|
IvEncoder.updateIv(iv, 1L);
|
||||||
ivSpec = new IvParameterSpec(iv);
|
ivSpec = new IvParameterSpec(iv);
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec);
|
||||||
byte[] ciphertext1 = segCipher.doFinal(plaintext1);
|
byte[] ciphertext1 = frameCipher.doFinal(plaintext1);
|
||||||
// Concatenate the ciphertexts
|
// Concatenate the ciphertexts
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
out.write(tag);
|
out.write(tag);
|
||||||
@@ -61,65 +60,15 @@ public class OutgoingEncryptionLayerImplTest extends BriarTestCase {
|
|||||||
byte[] expected = out.toByteArray();
|
byte[] expected = out.toByteArray();
|
||||||
// Use the encryption layer to encrypt the plaintext
|
// Use the encryption layer to encrypt the plaintext
|
||||||
out.reset();
|
out.reset();
|
||||||
OutgoingEncryptionLayer encrypter = new OutgoingEncryptionLayerImpl(out,
|
FrameWriter encrypter = new OutgoingEncryptionLayerImpl(out,
|
||||||
Long.MAX_VALUE, tagCipher, segCipher, tagKey, segKey,
|
Long.MAX_VALUE, tagCipher, frameCipher, tagKey, frameKey);
|
||||||
false);
|
Frame f = new Frame();
|
||||||
Segment s = new SegmentImpl();
|
System.arraycopy(plaintext, 0, f.getBuffer(), 0, plaintext.length);
|
||||||
System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
|
f.setLength(plaintext.length);
|
||||||
s.setLength(plaintext.length);
|
encrypter.writeFrame(f);
|
||||||
s.setSegmentNumber(0L);
|
System.arraycopy(plaintext1, 0, f.getBuffer(), 0, plaintext1.length);
|
||||||
encrypter.writeSegment(s);
|
f.setLength(plaintext1.length);
|
||||||
System.arraycopy(plaintext1, 0, s.getBuffer(), 0, plaintext1.length);
|
encrypter.writeFrame(f);
|
||||||
s.setLength(plaintext1.length);
|
|
||||||
s.setSegmentNumber(1L);
|
|
||||||
encrypter.writeSegment(s);
|
|
||||||
byte[] actual = out.toByteArray();
|
|
||||||
// Check that the actual ciphertext matches the expected ciphertext
|
|
||||||
assertArrayEquals(expected, actual);
|
|
||||||
assertEquals(Long.MAX_VALUE - actual.length,
|
|
||||||
encrypter.getRemainingCapacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncryptionWithEverySegmentTagged() throws Exception {
|
|
||||||
// Calculate the expected tag for the first segment
|
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
|
||||||
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
|
|
||||||
// Calculate the expected ciphertext for the first segment
|
|
||||||
byte[] iv = new byte[segCipher.getBlockSize()];
|
|
||||||
byte[] plaintext = new byte[123 + MAC_LENGTH];
|
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
|
||||||
byte[] ciphertext = segCipher.doFinal(plaintext);
|
|
||||||
// Calculate the expected tag for the second segment
|
|
||||||
byte[] tag1 = new byte[TAG_LENGTH];
|
|
||||||
TagEncoder.encodeTag(tag1, 1L, tagCipher, tagKey);
|
|
||||||
// Calculate the expected ciphertext for the second segment
|
|
||||||
byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
|
|
||||||
IvEncoder.updateIv(iv, 1L);
|
|
||||||
ivSpec = new IvParameterSpec(iv);
|
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
|
||||||
byte[] ciphertext1 = segCipher.doFinal(plaintext1);
|
|
||||||
// Concatenate the ciphertexts
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
out.write(tag);
|
|
||||||
out.write(ciphertext);
|
|
||||||
out.write(tag1);
|
|
||||||
out.write(ciphertext1);
|
|
||||||
byte[] expected = out.toByteArray();
|
|
||||||
// Use the encryption layer to encrypt the plaintext
|
|
||||||
out.reset();
|
|
||||||
OutgoingEncryptionLayer encrypter = new OutgoingEncryptionLayerImpl(out,
|
|
||||||
Long.MAX_VALUE, tagCipher, segCipher, tagKey, segKey, true);
|
|
||||||
Segment s = new SegmentImpl();
|
|
||||||
System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
|
|
||||||
s.setLength(plaintext.length);
|
|
||||||
s.setSegmentNumber(0L);
|
|
||||||
encrypter.writeSegment(s);
|
|
||||||
System.arraycopy(plaintext1, 0, s.getBuffer(), 0, plaintext1.length);
|
|
||||||
s.setLength(plaintext1.length);
|
|
||||||
s.setSegmentNumber(1L);
|
|
||||||
encrypter.writeSegment(s);
|
|
||||||
byte[] actual = out.toByteArray();
|
byte[] actual = out.toByteArray();
|
||||||
// Check that the actual ciphertext matches the expected ciphertext
|
// Check that the actual ciphertext matches the expected ciphertext
|
||||||
assertArrayEquals(expected, actual);
|
assertArrayEquals(expected, actual);
|
||||||
|
|||||||
@@ -1,158 +0,0 @@
|
|||||||
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 java.io.IOException;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
|
||||||
|
|
||||||
import net.sf.briar.BriarTestCase;
|
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
|
||||||
import net.sf.briar.api.plugins.SegmentSource;
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
import net.sf.briar.crypto.CryptoModule;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.google.inject.Guice;
|
|
||||||
import com.google.inject.Injector;
|
|
||||||
|
|
||||||
public class SegmentedIncomingEncryptionLayerTest extends BriarTestCase {
|
|
||||||
|
|
||||||
private final Cipher tagCipher, segCipher;
|
|
||||||
private final ErasableKey tagKey, segKey;
|
|
||||||
|
|
||||||
public SegmentedIncomingEncryptionLayerTest() {
|
|
||||||
super();
|
|
||||||
Injector i = Guice.createInjector(new CryptoModule());
|
|
||||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
|
||||||
tagCipher = crypto.getTagCipher();
|
|
||||||
segCipher = crypto.getSegmentCipher();
|
|
||||||
tagKey = crypto.generateTestKey();
|
|
||||||
segKey = crypto.generateTestKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDecryptionWithFirstSegmentTagged() throws Exception {
|
|
||||||
// Calculate the ciphertext for the first segment, including its tag
|
|
||||||
byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
|
|
||||||
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
|
|
||||||
byte[] ciphertext = new byte[TAG_LENGTH + plaintext.length];
|
|
||||||
TagEncoder.encodeTag(ciphertext, 0L, tagCipher, tagKey);
|
|
||||||
byte[] iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
|
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
|
||||||
segCipher.doFinal(plaintext, 0, plaintext.length, ciphertext,
|
|
||||||
TAG_LENGTH);
|
|
||||||
// Calculate the ciphertext for the second segment
|
|
||||||
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
|
|
||||||
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
|
|
||||||
IvEncoder.updateIv(iv, 1L);
|
|
||||||
ivSpec = new IvParameterSpec(iv);
|
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
|
||||||
byte[] ciphertext1 = segCipher.doFinal(plaintext1, 0,
|
|
||||||
plaintext1.length);
|
|
||||||
// Buffer the first segment and create a source for the second
|
|
||||||
Segment buffered = new SegmentImpl();
|
|
||||||
System.arraycopy(ciphertext, 0, buffered.getBuffer(), 0,
|
|
||||||
ciphertext.length);
|
|
||||||
buffered.setLength(ciphertext.length);
|
|
||||||
SegmentSource in = new ByteArraySegmentSource(ciphertext1);
|
|
||||||
// Use the encryption layer to decrypt the ciphertext
|
|
||||||
IncomingEncryptionLayer decrypter =
|
|
||||||
new SegmentedIncomingEncryptionLayer(in, tagCipher, segCipher,
|
|
||||||
tagKey, segKey, false, false, buffered);
|
|
||||||
// First segment
|
|
||||||
Segment s = new SegmentImpl();
|
|
||||||
assertTrue(decrypter.readSegment(s));
|
|
||||||
assertEquals(plaintext.length, s.getLength());
|
|
||||||
assertEquals(0L, s.getSegmentNumber());
|
|
||||||
byte[] decrypted = s.getBuffer();
|
|
||||||
for(int i = 0; i < plaintext.length; i++) {
|
|
||||||
assertEquals(plaintext[i], decrypted[i]);
|
|
||||||
}
|
|
||||||
// Second segment
|
|
||||||
assertTrue(decrypter.readSegment(s));
|
|
||||||
assertEquals(plaintext1.length, s.getLength());
|
|
||||||
assertEquals(1L, s.getSegmentNumber());
|
|
||||||
decrypted = s.getBuffer();
|
|
||||||
for(int i = 0; i < plaintext1.length; i++) {
|
|
||||||
assertEquals(plaintext1[i], decrypted[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDecryptionWithEverySegmentTagged() throws Exception {
|
|
||||||
// Calculate the ciphertext for the first segment, including its tag
|
|
||||||
byte[] plaintext = new byte[FRAME_HEADER_LENGTH + 123 + MAC_LENGTH];
|
|
||||||
HeaderEncoder.encodeHeader(plaintext, 0L, 123, 0);
|
|
||||||
byte[] ciphertext = new byte[TAG_LENGTH + plaintext.length];
|
|
||||||
TagEncoder.encodeTag(ciphertext, 0L, tagCipher, tagKey);
|
|
||||||
byte[] iv = IvEncoder.encodeIv(0L, segCipher.getBlockSize());
|
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
|
||||||
segCipher.doFinal(plaintext, 0, plaintext.length, ciphertext,
|
|
||||||
TAG_LENGTH);
|
|
||||||
// Calculate the ciphertext for the second frame, including its tag
|
|
||||||
byte[] plaintext1 = new byte[FRAME_HEADER_LENGTH + 1234 + MAC_LENGTH];
|
|
||||||
HeaderEncoder.encodeHeader(plaintext1, 1L, 1234, 0);
|
|
||||||
byte[] ciphertext1 = new byte[TAG_LENGTH + plaintext1.length];
|
|
||||||
TagEncoder.encodeTag(ciphertext1, 1L, tagCipher, tagKey);
|
|
||||||
IvEncoder.updateIv(iv, 1L);
|
|
||||||
ivSpec = new IvParameterSpec(iv);
|
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
|
||||||
segCipher.doFinal(plaintext1, 0, plaintext1.length, ciphertext1,
|
|
||||||
TAG_LENGTH);
|
|
||||||
// Buffer the first segment and create a source for the second
|
|
||||||
Segment buffered = new SegmentImpl();
|
|
||||||
System.arraycopy(ciphertext, 0, buffered.getBuffer(), 0,
|
|
||||||
ciphertext.length);
|
|
||||||
buffered.setLength(ciphertext.length);
|
|
||||||
SegmentSource in = new ByteArraySegmentSource(ciphertext1);
|
|
||||||
// Use the encryption layer to decrypt the ciphertext
|
|
||||||
IncomingEncryptionLayer decrypter =
|
|
||||||
new SegmentedIncomingEncryptionLayer(in, tagCipher, segCipher,
|
|
||||||
tagKey, segKey, true, false, buffered);
|
|
||||||
// First segment
|
|
||||||
Segment s = new SegmentImpl();
|
|
||||||
assertTrue(decrypter.readSegment(s));
|
|
||||||
assertEquals(plaintext.length, s.getLength());
|
|
||||||
assertEquals(0L, s.getSegmentNumber());
|
|
||||||
byte[] decrypted = s.getBuffer();
|
|
||||||
for(int i = 0; i < plaintext.length; i++) {
|
|
||||||
assertEquals(plaintext[i], decrypted[i]);
|
|
||||||
}
|
|
||||||
// Second segment
|
|
||||||
assertTrue(decrypter.readSegment(s));
|
|
||||||
assertEquals(plaintext1.length, s.getLength());
|
|
||||||
assertEquals(1L, s.getSegmentNumber());
|
|
||||||
decrypted = s.getBuffer();
|
|
||||||
for(int i = 0; i < plaintext1.length; i++) {
|
|
||||||
assertEquals(plaintext1[i], decrypted[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ByteArraySegmentSource implements SegmentSource {
|
|
||||||
|
|
||||||
private final byte[] segment;
|
|
||||||
|
|
||||||
private ByteArraySegmentSource(byte[] segment) {
|
|
||||||
this.segment = segment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean readSegment(Segment s) throws IOException {
|
|
||||||
System.arraycopy(segment, 0, s.getBuffer(), 0, segment.length);
|
|
||||||
s.setLength(segment.length);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxSegmentLength() {
|
|
||||||
return MAX_SEGMENT_LENGTH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
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 org.junit.Assert.assertArrayEquals;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
|
||||||
|
|
||||||
import net.sf.briar.BriarTestCase;
|
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
|
||||||
import net.sf.briar.api.plugins.SegmentSink;
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
import net.sf.briar.crypto.CryptoModule;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.google.inject.Guice;
|
|
||||||
import com.google.inject.Injector;
|
|
||||||
|
|
||||||
public class SegmentedOutgoingEncryptionLayerTest extends BriarTestCase {
|
|
||||||
|
|
||||||
private static final int MAC_LENGTH = 32;
|
|
||||||
|
|
||||||
private final Cipher tagCipher, segCipher;
|
|
||||||
private final ErasableKey tagKey, segKey;
|
|
||||||
|
|
||||||
public SegmentedOutgoingEncryptionLayerTest() {
|
|
||||||
super();
|
|
||||||
Injector i = Guice.createInjector(new CryptoModule());
|
|
||||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
|
||||||
tagCipher = crypto.getTagCipher();
|
|
||||||
segCipher = crypto.getSegmentCipher();
|
|
||||||
tagKey = crypto.generateTestKey();
|
|
||||||
segKey = crypto.generateTestKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncryptionWithFirstSegmentTagged() throws Exception {
|
|
||||||
// Calculate the expected tag
|
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
|
||||||
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
|
|
||||||
// Calculate the expected ciphertext for the first segment
|
|
||||||
byte[] iv = new byte[segCipher.getBlockSize()];
|
|
||||||
byte[] plaintext = new byte[123 + MAC_LENGTH];
|
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
|
||||||
byte[] ciphertext = segCipher.doFinal(plaintext);
|
|
||||||
// Calculate the expected ciphertext for the second segment
|
|
||||||
byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
|
|
||||||
IvEncoder.updateIv(iv, 1L);
|
|
||||||
ivSpec = new IvParameterSpec(iv);
|
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
|
||||||
byte[] ciphertext1 = segCipher.doFinal(plaintext1);
|
|
||||||
// Concatenate the ciphertexts
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
out.write(tag);
|
|
||||||
out.write(ciphertext);
|
|
||||||
out.write(ciphertext1);
|
|
||||||
byte[] expected = out.toByteArray();
|
|
||||||
// Use the encryption layer to encrypt the plaintext
|
|
||||||
ByteArraySegmentSink sink = new ByteArraySegmentSink();
|
|
||||||
OutgoingEncryptionLayer encrypter =
|
|
||||||
new SegmentedOutgoingEncryptionLayer(sink, Long.MAX_VALUE,
|
|
||||||
tagCipher, segCipher, tagKey, segKey, false, false);
|
|
||||||
Segment s = new SegmentImpl();
|
|
||||||
System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
|
|
||||||
s.setLength(plaintext.length);
|
|
||||||
s.setSegmentNumber(0L);
|
|
||||||
encrypter.writeSegment(s);
|
|
||||||
System.arraycopy(plaintext1, 0, s.getBuffer(), 0, plaintext1.length);
|
|
||||||
s.setLength(plaintext1.length);
|
|
||||||
s.setSegmentNumber(1L);
|
|
||||||
encrypter.writeSegment(s);
|
|
||||||
byte[] actual = out.toByteArray();
|
|
||||||
// Check that the actual ciphertext matches the expected ciphertext
|
|
||||||
assertArrayEquals(expected, actual);
|
|
||||||
assertEquals(Long.MAX_VALUE - actual.length,
|
|
||||||
encrypter.getRemainingCapacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncryptionWithEverySegmentTagged() throws Exception {
|
|
||||||
// Calculate the expected tag for the first segment
|
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
|
||||||
TagEncoder.encodeTag(tag, 0L, tagCipher, tagKey);
|
|
||||||
// Calculate the expected ciphertext for the first segment
|
|
||||||
byte[] iv = new byte[segCipher.getBlockSize()];
|
|
||||||
byte[] plaintext = new byte[123 + MAC_LENGTH];
|
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
|
||||||
byte[] ciphertext = segCipher.doFinal(plaintext);
|
|
||||||
// Calculate the expected tag for the second segment
|
|
||||||
byte[] tag1 = new byte[TAG_LENGTH];
|
|
||||||
TagEncoder.encodeTag(tag1, 1L, tagCipher, tagKey);
|
|
||||||
// Calculate the expected ciphertext for the second segment
|
|
||||||
byte[] plaintext1 = new byte[1234 + MAC_LENGTH];
|
|
||||||
IvEncoder.updateIv(iv, 1L);
|
|
||||||
ivSpec = new IvParameterSpec(iv);
|
|
||||||
segCipher.init(Cipher.ENCRYPT_MODE, segKey, ivSpec);
|
|
||||||
byte[] ciphertext1 = segCipher.doFinal(plaintext1);
|
|
||||||
// Concatenate the ciphertexts
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
out.write(tag);
|
|
||||||
out.write(ciphertext);
|
|
||||||
out.write(tag1);
|
|
||||||
out.write(ciphertext1);
|
|
||||||
byte[] expected = out.toByteArray();
|
|
||||||
// Use the encryption layer to encrypt the plaintext
|
|
||||||
SegmentSink sink = new ByteArraySegmentSink();
|
|
||||||
OutgoingEncryptionLayer encrypter =
|
|
||||||
new SegmentedOutgoingEncryptionLayer(sink, Long.MAX_VALUE,
|
|
||||||
tagCipher, segCipher, tagKey, segKey, true, false);
|
|
||||||
Segment s = new SegmentImpl();
|
|
||||||
System.arraycopy(plaintext, 0, s.getBuffer(), 0, plaintext.length);
|
|
||||||
s.setLength(plaintext.length);
|
|
||||||
s.setSegmentNumber(0L);
|
|
||||||
encrypter.writeSegment(s);
|
|
||||||
System.arraycopy(plaintext1, 0, s.getBuffer(), 0, plaintext1.length);
|
|
||||||
s.setLength(plaintext1.length);
|
|
||||||
s.setSegmentNumber(1L);
|
|
||||||
encrypter.writeSegment(s);
|
|
||||||
byte[] actual = out.toByteArray();
|
|
||||||
// Check that the actual ciphertext matches the expected ciphertext
|
|
||||||
assertArrayEquals(expected, actual);
|
|
||||||
assertEquals(Long.MAX_VALUE - actual.length,
|
|
||||||
encrypter.getRemainingCapacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ByteArraySegmentSink extends ByteArrayOutputStream
|
|
||||||
implements SegmentSink {
|
|
||||||
|
|
||||||
public void writeSegment(Segment s) throws IOException {
|
|
||||||
write(s.getBuffer(), 0, s.getLength());
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxSegmentLength() {
|
|
||||||
return MAX_SEGMENT_LENGTH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
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 org.junit.Assert.assertArrayEquals;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import net.sf.briar.BriarTestCase;
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class XorErasureCodeTest extends BriarTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodingAndDecodingWithAllSegments() throws Exception {
|
|
||||||
XorErasureEncoder e = new XorErasureEncoder(5);
|
|
||||||
XorErasureDecoder d = new XorErasureDecoder(5, false);
|
|
||||||
Frame f = new Frame(1234);
|
|
||||||
new Random().nextBytes(f.getBuffer());
|
|
||||||
int payload = 1234 - FRAME_HEADER_LENGTH - MAC_LENGTH;
|
|
||||||
HeaderEncoder.encodeHeader(f.getBuffer(), 0L, payload, 0);
|
|
||||||
f.setLength(1234);
|
|
||||||
Segment[] set = e.encodeFrame(f);
|
|
||||||
assertEquals(5, set.length);
|
|
||||||
Frame f1 = new Frame(1234);
|
|
||||||
assertTrue(d.decodeFrame(f1, set));
|
|
||||||
assertArrayEquals(f.getBuffer(), f1.getBuffer());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodingAndDecodingWithMissingSegment() throws Exception {
|
|
||||||
XorErasureEncoder e = new XorErasureEncoder(5);
|
|
||||||
XorErasureDecoder d = new XorErasureDecoder(5, false);
|
|
||||||
Frame f = new Frame(1234);
|
|
||||||
new Random().nextBytes(f.getBuffer());
|
|
||||||
int payload = 1234 - FRAME_HEADER_LENGTH - MAC_LENGTH;
|
|
||||||
HeaderEncoder.encodeHeader(f.getBuffer(), 0L, payload, 0);
|
|
||||||
f.setLength(1234);
|
|
||||||
for(int i = 0; i < 5; i++) {
|
|
||||||
Segment[] set = e.encodeFrame(f);
|
|
||||||
assertEquals(5, set.length);
|
|
||||||
set[i] = null;
|
|
||||||
Frame f1 = new Frame(1234);
|
|
||||||
assertTrue(d.decodeFrame(f1, set));
|
|
||||||
assertArrayEquals(f.getBuffer(), f1.getBuffer());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
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 org.junit.Assert.assertArrayEquals;
|
|
||||||
import net.sf.briar.BriarTestCase;
|
|
||||||
import net.sf.briar.api.FormatException;
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class XorErasureDecoderTest extends BriarTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMaximumLength() throws Exception {
|
|
||||||
XorErasureDecoder d = new XorErasureDecoder(5, false);
|
|
||||||
// A frame of the maximum length should be decoded successfully
|
|
||||||
Segment[] set = encodeEmptyFrame(MAX_FRAME_LENGTH / 4, 5);
|
|
||||||
Frame f = new Frame();
|
|
||||||
assertTrue(d.decodeFrame(f, set));
|
|
||||||
checkFrame(f, MAX_FRAME_LENGTH);
|
|
||||||
// A frame larger than the maximum length should not be decoded
|
|
||||||
set = encodeEmptyFrame(MAX_FRAME_LENGTH / 4 + 1, 5);
|
|
||||||
f = new Frame();
|
|
||||||
try {
|
|
||||||
d.decodeFrame(f, set);
|
|
||||||
} catch(FormatException expected) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMinimumLengthIsUsed() throws Exception {
|
|
||||||
Segment[] set = encodeEmptyFrame(250, 4);
|
|
||||||
// Replace one of the pieces with a longer piece
|
|
||||||
byte[] b = set[1].getBuffer();
|
|
||||||
assertArrayEquals(new byte[250], b);
|
|
||||||
set[1] = new SegmentImpl(251);
|
|
||||||
set[1].setLength(251);
|
|
||||||
// The frame should be decoded successfully
|
|
||||||
XorErasureDecoder d = new XorErasureDecoder(4, false);
|
|
||||||
Frame f = new Frame(750);
|
|
||||||
assertTrue(d.decodeFrame(f, set));
|
|
||||||
// The minimum of the segments' lengths should have been used
|
|
||||||
assertEquals(750, f.getLength());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDecodingWithMissingSegment() throws Exception {
|
|
||||||
XorErasureDecoder d = new XorErasureDecoder(4, false);
|
|
||||||
for(int i = 0; i < 4; i++) {
|
|
||||||
Segment[] set = encodeEmptyFrame(250, 4);
|
|
||||||
set[i] = null;
|
|
||||||
// The frame should be decoded successfully
|
|
||||||
Frame f = new Frame(750);
|
|
||||||
assertTrue(d.decodeFrame(f, set));
|
|
||||||
checkFrame(f, 750);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDecodingWithTwoMissingSegments() throws Exception {
|
|
||||||
XorErasureDecoder d = new XorErasureDecoder(4, false);
|
|
||||||
Segment[] set = encodeEmptyFrame(250, 4);
|
|
||||||
set[0] = null;
|
|
||||||
set[1] = null;
|
|
||||||
Frame f = new Frame(750);
|
|
||||||
assertFalse(d.decodeFrame(f, set));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Segment[] encodeEmptyFrame(int length, int n) {
|
|
||||||
Segment[] set = new Segment[n];
|
|
||||||
for(int i = 0; i < n; i++) {
|
|
||||||
set[i] = new SegmentImpl(length);
|
|
||||||
set[i].setLength(length);
|
|
||||||
}
|
|
||||||
int payload = length * (n - 1) - FRAME_HEADER_LENGTH - MAC_LENGTH;
|
|
||||||
HeaderEncoder.encodeHeader(set[0].getBuffer(), 0L, payload, 0);
|
|
||||||
HeaderEncoder.encodeHeader(set[n - 1].getBuffer(), 0L, payload, 0);
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkFrame(Frame f, int length) {
|
|
||||||
byte[] b = f.getBuffer();
|
|
||||||
assertEquals(0L, HeaderEncoder.getFrameNumber(b));
|
|
||||||
int payload = length - FRAME_HEADER_LENGTH - MAC_LENGTH;
|
|
||||||
assertEquals(payload, HeaderEncoder.getPayloadLength(b));
|
|
||||||
assertEquals(0, HeaderEncoder.getPaddingLength(b));
|
|
||||||
// Check the body
|
|
||||||
assertEquals(length, f.getLength());
|
|
||||||
for(int i = FRAME_HEADER_LENGTH; i < length; i++) {
|
|
||||||
assertEquals("" + i, 0, b[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import net.sf.briar.BriarTestCase;
|
|
||||||
import net.sf.briar.api.transport.Segment;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class XorErasureEncoderTest extends BriarTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncoding() {
|
|
||||||
// Create a frame
|
|
||||||
Frame f = new Frame();
|
|
||||||
f.setLength(200);
|
|
||||||
byte[] b = f.getBuffer();
|
|
||||||
for(int i = 0; i < 200; i++) b[i] = (byte) i;
|
|
||||||
// Encode the frame
|
|
||||||
XorErasureEncoder e = new XorErasureEncoder(4);
|
|
||||||
Segment[] set = e.encodeFrame(f);
|
|
||||||
// There should be four pieces of 67 bytes each
|
|
||||||
assertEquals(4, set.length);
|
|
||||||
for(int i = 0; i < 4; i++) assertEquals(67, set[i].getLength());
|
|
||||||
// The first three pieces should contain the data plus on padding byte
|
|
||||||
byte[] b1 = set[0].getBuffer();
|
|
||||||
for(int i = 0; i < 67; i++) assertEquals((byte) i, b1[i]);
|
|
||||||
byte[] b2 = set[1].getBuffer();
|
|
||||||
for(int i = 0; i < 67; i++) assertEquals((byte) (i + 67), b2[i]);
|
|
||||||
byte[] b3 = set[2].getBuffer();
|
|
||||||
for(int i = 0; i < 66; i++) assertEquals((byte) (i + 134), b3[i]);
|
|
||||||
assertEquals(0, b3[66]);
|
|
||||||
// The fourth piece should be the XOR of the other three
|
|
||||||
byte[] b4 = set[3].getBuffer();
|
|
||||||
byte[] expected = new byte[67];
|
|
||||||
for(int i = 0; i < 67; i++) {
|
|
||||||
expected[i] = (byte) (b1[i] ^ b2[i] ^ b3[i]);
|
|
||||||
}
|
|
||||||
assertArrayEquals(expected, b4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user