mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 13:19:52 +01:00
Decoupled ProtocolReader (which belongs in the protocol component)
from PacketReader (which belongs in the transport component).
This commit is contained in:
24
api/net/sf/briar/api/protocol/ProtocolReader.java
Normal file
24
api/net/sf/briar/api/protocol/ProtocolReader.java
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package net.sf.briar.api.protocol;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface ProtocolReader {
|
||||||
|
|
||||||
|
boolean hasAck() throws IOException;
|
||||||
|
Ack readAck() throws IOException;
|
||||||
|
|
||||||
|
boolean hasBatch() throws IOException;
|
||||||
|
Batch readBatch() throws IOException;
|
||||||
|
|
||||||
|
boolean hasOffer() throws IOException;
|
||||||
|
Offer readOffer() throws IOException;
|
||||||
|
|
||||||
|
boolean hasRequest() throws IOException;
|
||||||
|
Request readRequest() throws IOException;
|
||||||
|
|
||||||
|
boolean hasSubscriptionUpdate() throws IOException;
|
||||||
|
SubscriptionUpdate readSubscriptionUpdate() throws IOException;
|
||||||
|
|
||||||
|
boolean hasTransportUpdate() throws IOException;
|
||||||
|
TransportUpdate readTransportUpdate() throws IOException;
|
||||||
|
}
|
||||||
8
api/net/sf/briar/api/protocol/ProtocolReaderFactory.java
Normal file
8
api/net/sf/briar/api/protocol/ProtocolReaderFactory.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package net.sf.briar.api.protocol;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public interface ProtocolReaderFactory {
|
||||||
|
|
||||||
|
ProtocolReader createProtocolReader(InputStream in);
|
||||||
|
}
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
package net.sf.briar.api.transport;
|
package net.sf.briar.api.transport;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import net.sf.briar.api.protocol.Ack;
|
import java.security.GeneralSecurityException;
|
||||||
import net.sf.briar.api.protocol.Batch;
|
|
||||||
import net.sf.briar.api.protocol.Offer;
|
|
||||||
import net.sf.briar.api.protocol.Request;
|
|
||||||
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
|
||||||
import net.sf.briar.api.protocol.TransportUpdate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads encrypted packets from an underlying input stream, decrypts and
|
* Reads encrypted packets from an underlying input stream, decrypts and
|
||||||
@@ -15,21 +10,17 @@ import net.sf.briar.api.protocol.TransportUpdate;
|
|||||||
*/
|
*/
|
||||||
public interface PacketReader {
|
public interface PacketReader {
|
||||||
|
|
||||||
boolean hasAck() throws IOException;
|
/**
|
||||||
Ack readAck() throws IOException;
|
* Returns the input stream from which packets should be read. (Note that
|
||||||
|
* this is not the underlying input stream.)
|
||||||
|
*/
|
||||||
|
InputStream getInputStream();
|
||||||
|
|
||||||
boolean hasBatch() throws IOException;
|
/**
|
||||||
Batch readBatch() throws IOException;
|
* Finishes reading the current packet (if any), authenticates the packet
|
||||||
|
* and prepares to read the next packet. If this method is called twice in
|
||||||
boolean hasOffer() throws IOException;
|
* succession without any intervening reads, the underlying input stream
|
||||||
Offer readOffer() throws IOException;
|
* will be unaffected.
|
||||||
|
*/
|
||||||
boolean hasRequest() throws IOException;
|
void finishPacket() throws IOException, GeneralSecurityException;
|
||||||
Request readRequest() throws IOException;
|
|
||||||
|
|
||||||
boolean hasSubscriptionUpdate() throws IOException;
|
|
||||||
SubscriptionUpdate readSubscriptionUpdate() throws IOException;
|
|
||||||
|
|
||||||
boolean hasTransportUpdate() throws IOException;
|
|
||||||
TransportUpdate readTransportUpdate() throws IOException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,5 +20,5 @@ public interface PacketWriter {
|
|||||||
* next packet. If this method is called twice in succession without any
|
* next packet. If this method is called twice in succession without any
|
||||||
* intervening writes, the underlying output stream will be unaffected.
|
* intervening writes, the underlying output stream will be unaffected.
|
||||||
*/
|
*/
|
||||||
void nextPacket() throws IOException;
|
void finishPacket() throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import net.sf.briar.api.protocol.Message;
|
|||||||
import net.sf.briar.api.protocol.MessageEncoder;
|
import net.sf.briar.api.protocol.MessageEncoder;
|
||||||
import net.sf.briar.api.protocol.MessageId;
|
import net.sf.briar.api.protocol.MessageId;
|
||||||
import net.sf.briar.api.protocol.Offer;
|
import net.sf.briar.api.protocol.Offer;
|
||||||
|
import net.sf.briar.api.protocol.ProtocolReaderFactory;
|
||||||
import net.sf.briar.api.protocol.Request;
|
import net.sf.briar.api.protocol.Request;
|
||||||
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
||||||
import net.sf.briar.api.protocol.TransportUpdate;
|
import net.sf.briar.api.protocol.TransportUpdate;
|
||||||
@@ -28,11 +29,12 @@ public class ProtocolModule extends AbstractModule {
|
|||||||
bind(AuthorFactory.class).to(AuthorFactoryImpl.class);
|
bind(AuthorFactory.class).to(AuthorFactoryImpl.class);
|
||||||
bind(BatchFactory.class).to(BatchFactoryImpl.class);
|
bind(BatchFactory.class).to(BatchFactoryImpl.class);
|
||||||
bind(GroupFactory.class).to(GroupFactoryImpl.class);
|
bind(GroupFactory.class).to(GroupFactoryImpl.class);
|
||||||
|
bind(MessageEncoder.class).to(MessageEncoderImpl.class);
|
||||||
bind(OfferFactory.class).to(OfferFactoryImpl.class);
|
bind(OfferFactory.class).to(OfferFactoryImpl.class);
|
||||||
|
bind(ProtocolReaderFactory.class).to(ProtocolReaderFactoryImpl.class);
|
||||||
bind(RequestFactory.class).to(RequestFactoryImpl.class);
|
bind(RequestFactory.class).to(RequestFactoryImpl.class);
|
||||||
bind(SubscriptionFactory.class).to(SubscriptionFactoryImpl.class);
|
bind(SubscriptionFactory.class).to(SubscriptionFactoryImpl.class);
|
||||||
bind(TransportFactory.class).to(TransportFactoryImpl.class);
|
bind(TransportFactory.class).to(TransportFactoryImpl.class);
|
||||||
bind(MessageEncoder.class).to(MessageEncoderImpl.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import net.sf.briar.api.protocol.Ack;
|
||||||
|
import net.sf.briar.api.protocol.Batch;
|
||||||
|
import net.sf.briar.api.protocol.Offer;
|
||||||
|
import net.sf.briar.api.protocol.ProtocolReader;
|
||||||
|
import net.sf.briar.api.protocol.ProtocolReaderFactory;
|
||||||
|
import net.sf.briar.api.protocol.Request;
|
||||||
|
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
||||||
|
import net.sf.briar.api.protocol.TransportUpdate;
|
||||||
|
import net.sf.briar.api.serial.ObjectReader;
|
||||||
|
import net.sf.briar.api.serial.ReaderFactory;
|
||||||
|
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
|
||||||
|
class ProtocolReaderFactoryImpl implements ProtocolReaderFactory {
|
||||||
|
|
||||||
|
private final ReaderFactory readerFactory;
|
||||||
|
private final Provider<ObjectReader<Ack>> ackProvider;
|
||||||
|
private final Provider<ObjectReader<Batch>> batchProvider;
|
||||||
|
private final Provider<ObjectReader<Offer>> offerProvider;
|
||||||
|
private final Provider<ObjectReader<Request>> requestProvider;
|
||||||
|
private final Provider<ObjectReader<SubscriptionUpdate>> subscriptionProvider;
|
||||||
|
private final Provider<ObjectReader<TransportUpdate>> transportProvider;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ProtocolReaderFactoryImpl(ReaderFactory readerFactory,
|
||||||
|
Provider<ObjectReader<Ack>> ackProvider,
|
||||||
|
Provider<ObjectReader<Batch>> batchProvider,
|
||||||
|
Provider<ObjectReader<Offer>> offerProvider,
|
||||||
|
Provider<ObjectReader<Request>> requestProvider,
|
||||||
|
Provider<ObjectReader<SubscriptionUpdate>> subscriptionProvider,
|
||||||
|
Provider<ObjectReader<TransportUpdate>> transportProvider) {
|
||||||
|
this.readerFactory = readerFactory;
|
||||||
|
this.ackProvider = ackProvider;
|
||||||
|
this.batchProvider = batchProvider;
|
||||||
|
this.offerProvider = offerProvider;
|
||||||
|
this.requestProvider = requestProvider;
|
||||||
|
this.subscriptionProvider = subscriptionProvider;
|
||||||
|
this.transportProvider = transportProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProtocolReader createProtocolReader(InputStream in) {
|
||||||
|
return new ProtocolReaderImpl(in, readerFactory, ackProvider.get(),
|
||||||
|
batchProvider.get(), offerProvider.get(), requestProvider.get(),
|
||||||
|
subscriptionProvider.get(), transportProvider.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
85
components/net/sf/briar/protocol/ProtocolReaderImpl.java
Normal file
85
components/net/sf/briar/protocol/ProtocolReaderImpl.java
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import net.sf.briar.api.protocol.Ack;
|
||||||
|
import net.sf.briar.api.protocol.Batch;
|
||||||
|
import net.sf.briar.api.protocol.Offer;
|
||||||
|
import net.sf.briar.api.protocol.ProtocolReader;
|
||||||
|
import net.sf.briar.api.protocol.Request;
|
||||||
|
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
||||||
|
import net.sf.briar.api.protocol.Tags;
|
||||||
|
import net.sf.briar.api.protocol.TransportUpdate;
|
||||||
|
import net.sf.briar.api.serial.ObjectReader;
|
||||||
|
import net.sf.briar.api.serial.Reader;
|
||||||
|
import net.sf.briar.api.serial.ReaderFactory;
|
||||||
|
|
||||||
|
class ProtocolReaderImpl implements ProtocolReader {
|
||||||
|
|
||||||
|
private final Reader reader;
|
||||||
|
|
||||||
|
ProtocolReaderImpl(InputStream in, ReaderFactory readerFactory,
|
||||||
|
ObjectReader<Ack> ackReader, ObjectReader<Batch> batchReader,
|
||||||
|
ObjectReader<Offer> offerReader,
|
||||||
|
ObjectReader<Request> requestReader,
|
||||||
|
ObjectReader<SubscriptionUpdate> subscriptionReader,
|
||||||
|
ObjectReader<TransportUpdate> transportReader) {
|
||||||
|
reader = readerFactory.createReader(in);
|
||||||
|
reader.addObjectReader(Tags.ACK, ackReader);
|
||||||
|
reader.addObjectReader(Tags.BATCH, batchReader);
|
||||||
|
reader.addObjectReader(Tags.OFFER, offerReader);
|
||||||
|
reader.addObjectReader(Tags.REQUEST, requestReader);
|
||||||
|
reader.addObjectReader(Tags.SUBSCRIPTIONS, subscriptionReader);
|
||||||
|
reader.addObjectReader(Tags.TRANSPORTS, transportReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAck() throws IOException {
|
||||||
|
return reader.hasUserDefined(Tags.ACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ack readAck() throws IOException {
|
||||||
|
return reader.readUserDefined(Tags.ACK, Ack.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasBatch() throws IOException {
|
||||||
|
return reader.hasUserDefined(Tags.BATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Batch readBatch() throws IOException {
|
||||||
|
return reader.readUserDefined(Tags.BATCH, Batch.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasOffer() throws IOException {
|
||||||
|
return reader.hasUserDefined(Tags.OFFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Offer readOffer() throws IOException {
|
||||||
|
return reader.readUserDefined(Tags.OFFER, Offer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasRequest() throws IOException {
|
||||||
|
return reader.hasUserDefined(Tags.REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Request readRequest() throws IOException {
|
||||||
|
return reader.readUserDefined(Tags.REQUEST, Request.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSubscriptionUpdate() throws IOException {
|
||||||
|
return reader.hasUserDefined(Tags.SUBSCRIPTIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubscriptionUpdate readSubscriptionUpdate() throws IOException {
|
||||||
|
return reader.readUserDefined(Tags.SUBSCRIPTIONS,
|
||||||
|
SubscriptionUpdate.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasTransportUpdate() throws IOException {
|
||||||
|
return reader.hasUserDefined(Tags.TRANSPORTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransportUpdate readTransportUpdate() throws IOException {
|
||||||
|
return reader.readUserDefined(Tags.TRANSPORTS, TransportUpdate.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,10 @@ interface PacketDecrypter {
|
|||||||
/** Returns the input stream from which packets should be read. */
|
/** Returns the input stream from which packets should be read. */
|
||||||
InputStream getInputStream();
|
InputStream getInputStream();
|
||||||
|
|
||||||
/** Reads, decrypts and returns a tag from the underlying input stream. */
|
/**
|
||||||
|
* Reads, decrypts and returns a tag from the underlying input stream.
|
||||||
|
* Returns null if the end of the input stream is reached before any bytes
|
||||||
|
* are read.
|
||||||
|
*/
|
||||||
byte[] readTag() throws IOException;
|
byte[] readTag() throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,9 +54,11 @@ class PacketDecrypterImpl extends FilterInputStream implements PacketDecrypter {
|
|||||||
bufOff = bufLen = 0;
|
bufOff = bufLen = 0;
|
||||||
while(offset < tag.length) {
|
while(offset < tag.length) {
|
||||||
int read = in.read(tag, offset, tag.length - offset);
|
int read = in.read(tag, offset, tag.length - offset);
|
||||||
if(read == -1) throw new EOFException();
|
if(read == -1) break;
|
||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
|
if(offset == 0) return null; // EOF between packets is acceptable
|
||||||
|
if(offset < tag.length) throw new EOFException();
|
||||||
betweenPackets = false;
|
betweenPackets = false;
|
||||||
try {
|
try {
|
||||||
byte[] decryptedTag = tagCipher.doFinal(tag);
|
byte[] decryptedTag = tagCipher.doFinal(tag);
|
||||||
|
|||||||
@@ -8,47 +8,18 @@ import javax.crypto.Mac;
|
|||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
import net.sf.briar.api.crypto.CryptoComponent;
|
||||||
import net.sf.briar.api.protocol.Ack;
|
|
||||||
import net.sf.briar.api.protocol.Batch;
|
|
||||||
import net.sf.briar.api.protocol.Offer;
|
|
||||||
import net.sf.briar.api.protocol.Request;
|
|
||||||
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
|
||||||
import net.sf.briar.api.protocol.TransportUpdate;
|
|
||||||
import net.sf.briar.api.serial.ObjectReader;
|
|
||||||
import net.sf.briar.api.serial.ReaderFactory;
|
|
||||||
import net.sf.briar.api.transport.PacketReader;
|
import net.sf.briar.api.transport.PacketReader;
|
||||||
import net.sf.briar.api.transport.PacketReaderFactory;
|
import net.sf.briar.api.transport.PacketReaderFactory;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
class PacketReaderFactoryImpl implements PacketReaderFactory {
|
class PacketReaderFactoryImpl implements PacketReaderFactory {
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
private final ReaderFactory readerFactory;
|
|
||||||
private final Provider<ObjectReader<Ack>> ackProvider;
|
|
||||||
private final Provider<ObjectReader<Batch>> batchProvider;
|
|
||||||
private final Provider<ObjectReader<Offer>> offerProvider;
|
|
||||||
private final Provider<ObjectReader<Request>> requestProvider;
|
|
||||||
private final Provider<ObjectReader<SubscriptionUpdate>> subscriptionProvider;
|
|
||||||
private final Provider<ObjectReader<TransportUpdate>> transportProvider;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
PacketReaderFactoryImpl(CryptoComponent crypto, ReaderFactory readerFactory,
|
PacketReaderFactoryImpl(CryptoComponent crypto) {
|
||||||
Provider<ObjectReader<Ack>> ackProvider,
|
|
||||||
Provider<ObjectReader<Batch>> batchProvider,
|
|
||||||
Provider<ObjectReader<Offer>> offerProvider,
|
|
||||||
Provider<ObjectReader<Request>> requestProvider,
|
|
||||||
Provider<ObjectReader<SubscriptionUpdate>> subscriptionProvider,
|
|
||||||
Provider<ObjectReader<TransportUpdate>> transportProvider) {
|
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
this.readerFactory = readerFactory;
|
|
||||||
this.ackProvider = ackProvider;
|
|
||||||
this.batchProvider = batchProvider;
|
|
||||||
this.offerProvider = offerProvider;
|
|
||||||
this.requestProvider = requestProvider;
|
|
||||||
this.subscriptionProvider = subscriptionProvider;
|
|
||||||
this.transportProvider = transportProvider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketReader createPacketReader(byte[] firstTag, InputStream in,
|
public PacketReader createPacketReader(byte[] firstTag, InputStream in,
|
||||||
@@ -66,9 +37,6 @@ class PacketReaderFactoryImpl implements PacketReaderFactory {
|
|||||||
}
|
}
|
||||||
PacketDecrypter decrypter = new PacketDecrypterImpl(firstTag, in,
|
PacketDecrypter decrypter = new PacketDecrypterImpl(firstTag, in,
|
||||||
tagCipher, packetCipher, tagKey, packetKey);
|
tagCipher, packetCipher, tagKey, packetKey);
|
||||||
return new PacketReaderImpl(firstTag, readerFactory, ackProvider.get(),
|
return new PacketReaderImpl(decrypter, mac, transportId, connection);
|
||||||
batchProvider.get(), offerProvider.get(), requestProvider.get(),
|
|
||||||
subscriptionProvider.get(), transportProvider.get(),
|
|
||||||
decrypter, mac, transportId, connection);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,18 @@
|
|||||||
package net.sf.briar.transport;
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
|
import java.io.FilterInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
|
|
||||||
import net.sf.briar.api.protocol.Ack;
|
|
||||||
import net.sf.briar.api.protocol.Batch;
|
|
||||||
import net.sf.briar.api.protocol.Offer;
|
|
||||||
import net.sf.briar.api.protocol.Request;
|
|
||||||
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
|
||||||
import net.sf.briar.api.protocol.Tags;
|
|
||||||
import net.sf.briar.api.protocol.TransportUpdate;
|
|
||||||
import net.sf.briar.api.serial.FormatException;
|
import net.sf.briar.api.serial.FormatException;
|
||||||
import net.sf.briar.api.serial.ObjectReader;
|
|
||||||
import net.sf.briar.api.serial.Reader;
|
|
||||||
import net.sf.briar.api.serial.ReaderFactory;
|
|
||||||
import net.sf.briar.api.transport.PacketReader;
|
import net.sf.briar.api.transport.PacketReader;
|
||||||
|
|
||||||
class PacketReaderImpl implements PacketReader {
|
class PacketReaderImpl extends FilterInputStream implements PacketReader {
|
||||||
|
|
||||||
private final Reader reader;
|
|
||||||
private final PacketDecrypter decrypter;
|
private final PacketDecrypter decrypter;
|
||||||
private final Mac mac;
|
private final Mac mac;
|
||||||
private final int macLength, transportId;
|
private final int macLength, transportId;
|
||||||
@@ -30,23 +21,9 @@ class PacketReaderImpl implements PacketReader {
|
|||||||
private long packet = 0L;
|
private long packet = 0L;
|
||||||
private boolean betweenPackets = true;
|
private boolean betweenPackets = true;
|
||||||
|
|
||||||
PacketReaderImpl(byte[] firstTag, ReaderFactory readerFactory,
|
PacketReaderImpl(PacketDecrypter decrypter, Mac mac, int transportId,
|
||||||
ObjectReader<Ack> ackReader, ObjectReader<Batch> batchReader,
|
|
||||||
ObjectReader<Offer> offerReader,
|
|
||||||
ObjectReader<Request> requestReader,
|
|
||||||
ObjectReader<SubscriptionUpdate> subscriptionReader,
|
|
||||||
ObjectReader<TransportUpdate> transportReader,
|
|
||||||
PacketDecrypter decrypter, Mac mac, int transportId,
|
|
||||||
long connection) {
|
long connection) {
|
||||||
InputStream in = decrypter.getInputStream();
|
super(decrypter.getInputStream());
|
||||||
reader = readerFactory.createReader(in);
|
|
||||||
reader.addObjectReader(Tags.ACK, ackReader);
|
|
||||||
reader.addObjectReader(Tags.BATCH, batchReader);
|
|
||||||
reader.addObjectReader(Tags.OFFER, offerReader);
|
|
||||||
reader.addObjectReader(Tags.REQUEST, requestReader);
|
|
||||||
reader.addObjectReader(Tags.SUBSCRIPTIONS, subscriptionReader);
|
|
||||||
reader.addObjectReader(Tags.TRANSPORTS, transportReader);
|
|
||||||
reader.addConsumer(new MacConsumer(mac));
|
|
||||||
this.decrypter = decrypter;
|
this.decrypter = decrypter;
|
||||||
this.mac = mac;
|
this.mac = mac;
|
||||||
macLength = mac.getMacLength();
|
macLength = mac.getMacLength();
|
||||||
@@ -54,32 +31,36 @@ class PacketReaderImpl implements PacketReader {
|
|||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasAck() throws IOException {
|
public InputStream getInputStream() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finishPacket() throws IOException, GeneralSecurityException {
|
||||||
|
if(!betweenPackets) readMac();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
if(betweenPackets) readTag();
|
if(betweenPackets) readTag();
|
||||||
return reader.hasUserDefined(Tags.ACK);
|
int i = in.read();
|
||||||
|
if(i != -1) mac.update((byte) i);
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readTag() throws IOException {
|
@Override
|
||||||
assert betweenPackets;
|
public int read(byte[] b) throws IOException {
|
||||||
if(packet > Constants.MAX_32_BIT_UNSIGNED)
|
return read(b, 0, b.length);
|
||||||
throw new IllegalStateException();
|
|
||||||
byte[] tag = decrypter.readTag();
|
|
||||||
if(!TagDecoder.decodeTag(tag, transportId, connection, packet))
|
|
||||||
throw new FormatException();
|
|
||||||
mac.update(tag);
|
|
||||||
packet++;
|
|
||||||
betweenPackets = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Ack readAck() throws IOException {
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
if(betweenPackets) readTag();
|
if(betweenPackets) readTag();
|
||||||
Ack a = reader.readUserDefined(Tags.ACK, Ack.class);
|
int i = in.read(b, off, len);
|
||||||
readMac();
|
if(i != -1) mac.update(b, off, i);
|
||||||
betweenPackets = true;
|
return i;
|
||||||
return a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readMac() throws IOException {
|
private void readMac() throws IOException, GeneralSecurityException {
|
||||||
byte[] expectedMac = mac.doFinal();
|
byte[] expectedMac = mac.doFinal();
|
||||||
byte[] actualMac = new byte[macLength];
|
byte[] actualMac = new byte[macLength];
|
||||||
InputStream in = decrypter.getInputStream();
|
InputStream in = decrypter.getInputStream();
|
||||||
@@ -89,74 +70,22 @@ class PacketReaderImpl implements PacketReader {
|
|||||||
if(read == -1) break;
|
if(read == -1) break;
|
||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
if(offset < macLength) throw new FormatException();
|
if(offset < macLength) throw new GeneralSecurityException();
|
||||||
if(!Arrays.equals(expectedMac, actualMac)) throw new FormatException();
|
if(!Arrays.equals(expectedMac, actualMac))
|
||||||
}
|
throw new GeneralSecurityException();
|
||||||
|
|
||||||
public boolean hasBatch() throws IOException {
|
|
||||||
if(betweenPackets) readTag();
|
|
||||||
return reader.hasUserDefined(Tags.BATCH);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Batch readBatch() throws IOException {
|
|
||||||
if(betweenPackets) readTag();
|
|
||||||
Batch b = reader.readUserDefined(Tags.BATCH, Batch.class);
|
|
||||||
readMac();
|
|
||||||
betweenPackets = true;
|
betweenPackets = true;
|
||||||
return b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasOffer() throws IOException {
|
private void readTag() throws IOException {
|
||||||
if(betweenPackets) readTag();
|
assert betweenPackets;
|
||||||
return reader.hasUserDefined(Tags.OFFER);
|
if(packet > Constants.MAX_32_BIT_UNSIGNED)
|
||||||
}
|
throw new IllegalStateException();
|
||||||
|
byte[] tag = decrypter.readTag();
|
||||||
public Offer readOffer() throws IOException {
|
if(tag == null) return; // EOF
|
||||||
if(betweenPackets) readTag();
|
if(!TagDecoder.decodeTag(tag, transportId, connection, packet))
|
||||||
Offer o = reader.readUserDefined(Tags.OFFER, Offer.class);
|
throw new FormatException();
|
||||||
readMac();
|
mac.update(tag);
|
||||||
betweenPackets = true;
|
packet++;
|
||||||
return o;
|
betweenPackets = false;
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasRequest() throws IOException {
|
|
||||||
if(betweenPackets) readTag();
|
|
||||||
return reader.hasUserDefined(Tags.REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Request readRequest() throws IOException {
|
|
||||||
if(betweenPackets) readTag();
|
|
||||||
Request r = reader.readUserDefined(Tags.REQUEST, Request.class);
|
|
||||||
readMac();
|
|
||||||
betweenPackets = true;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasSubscriptionUpdate() throws IOException {
|
|
||||||
if(betweenPackets) readTag();
|
|
||||||
return reader.hasUserDefined(Tags.SUBSCRIPTIONS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubscriptionUpdate readSubscriptionUpdate() throws IOException {
|
|
||||||
if(betweenPackets) readTag();
|
|
||||||
SubscriptionUpdate s = reader.readUserDefined(Tags.SUBSCRIPTIONS,
|
|
||||||
SubscriptionUpdate.class);
|
|
||||||
readMac();
|
|
||||||
betweenPackets = true;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasTransportUpdate() throws IOException {
|
|
||||||
if(betweenPackets) readTag();
|
|
||||||
return reader.hasUserDefined(Tags.TRANSPORTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransportUpdate readTransportUpdate() throws IOException {
|
|
||||||
if(betweenPackets) readTag();
|
|
||||||
TransportUpdate t = reader.readUserDefined(Tags.TRANSPORTS,
|
|
||||||
TransportUpdate.class);
|
|
||||||
readMac();
|
|
||||||
betweenPackets = true;
|
|
||||||
return t;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class PacketWriterImpl extends FilterOutputStream implements PacketWriter {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void nextPacket() throws IOException {
|
public void finishPacket() throws IOException {
|
||||||
if(!betweenPackets) writeMac();
|
if(!betweenPackets) writeMac();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,9 +50,7 @@ class PacketWriterImpl extends FilterOutputStream implements PacketWriter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] b) throws IOException {
|
public void write(byte[] b) throws IOException {
|
||||||
if(betweenPackets) writeTag();
|
write(b, 0, b.length);
|
||||||
out.write(b);
|
|
||||||
mac.update(b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -36,6 +36,8 @@
|
|||||||
<test name='net.sf.briar.transport.ConnectionWindowImplTest'/>
|
<test name='net.sf.briar.transport.ConnectionWindowImplTest'/>
|
||||||
<test name='net.sf.briar.transport.PacketDecrypterImplTest'/>
|
<test name='net.sf.briar.transport.PacketDecrypterImplTest'/>
|
||||||
<test name='net.sf.briar.transport.PacketEncrypterImplTest'/>
|
<test name='net.sf.briar.transport.PacketEncrypterImplTest'/>
|
||||||
|
<test name='net.sf.briar.transport.PacketReaderImplTest'/>
|
||||||
|
<test name='net.sf.briar.transport.PacketReadWriteTest'/>
|
||||||
<test name='net.sf.briar.transport.PacketWriterImplTest'/>
|
<test name='net.sf.briar.transport.PacketWriterImplTest'/>
|
||||||
<test name='net.sf.briar.transport.TagDecoderTest'/>
|
<test name='net.sf.briar.transport.TagDecoderTest'/>
|
||||||
<test name='net.sf.briar.transport.TagEncoderTest'/>
|
<test name='net.sf.briar.transport.TagEncoderTest'/>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package net.sf.briar;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -26,6 +27,8 @@ import net.sf.briar.api.protocol.Message;
|
|||||||
import net.sf.briar.api.protocol.MessageEncoder;
|
import net.sf.briar.api.protocol.MessageEncoder;
|
||||||
import net.sf.briar.api.protocol.MessageId;
|
import net.sf.briar.api.protocol.MessageId;
|
||||||
import net.sf.briar.api.protocol.Offer;
|
import net.sf.briar.api.protocol.Offer;
|
||||||
|
import net.sf.briar.api.protocol.ProtocolReader;
|
||||||
|
import net.sf.briar.api.protocol.ProtocolReaderFactory;
|
||||||
import net.sf.briar.api.protocol.Request;
|
import net.sf.briar.api.protocol.Request;
|
||||||
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
||||||
import net.sf.briar.api.protocol.TransportUpdate;
|
import net.sf.briar.api.protocol.TransportUpdate;
|
||||||
@@ -64,6 +67,7 @@ public class FileReadWriteTest extends TestCase {
|
|||||||
|
|
||||||
private final PacketReaderFactory packetReaderFactory;
|
private final PacketReaderFactory packetReaderFactory;
|
||||||
private final PacketWriterFactory packetWriterFactory;
|
private final PacketWriterFactory packetWriterFactory;
|
||||||
|
private final ProtocolReaderFactory protocolReaderFactory;
|
||||||
private final ProtocolWriterFactory protocolWriterFactory;
|
private final ProtocolWriterFactory protocolWriterFactory;
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
private final byte[] secret = new byte[45];
|
private final byte[] secret = new byte[45];
|
||||||
@@ -83,6 +87,7 @@ public class FileReadWriteTest extends TestCase {
|
|||||||
new WritersModule());
|
new WritersModule());
|
||||||
packetReaderFactory = i.getInstance(PacketReaderFactory.class);
|
packetReaderFactory = i.getInstance(PacketReaderFactory.class);
|
||||||
packetWriterFactory = i.getInstance(PacketWriterFactory.class);
|
packetWriterFactory = i.getInstance(PacketWriterFactory.class);
|
||||||
|
protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class);
|
||||||
protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class);
|
protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class);
|
||||||
crypto = i.getInstance(CryptoComponent.class);
|
crypto = i.getInstance(CryptoComponent.class);
|
||||||
assertEquals(crypto.getMessageDigest().getDigestLength(),
|
assertEquals(crypto.getMessageDigest().getDigestLength(),
|
||||||
@@ -121,14 +126,14 @@ public class FileReadWriteTest extends TestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testWriteFile() throws Exception {
|
public void testWriteFile() throws Exception {
|
||||||
OutputStream out = new FileOutputStream(file);
|
OutputStream out = new FileOutputStream(file);
|
||||||
PacketWriter p = packetWriterFactory.createPacketWriter(out,
|
PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out,
|
||||||
transportId, connection, secret);
|
transportId, connection, secret);
|
||||||
out = p.getOutputStream();
|
out = packetWriter.getOutputStream();
|
||||||
|
|
||||||
AckWriter a = protocolWriterFactory.createAckWriter(out);
|
AckWriter a = protocolWriterFactory.createAckWriter(out);
|
||||||
assertTrue(a.writeBatchId(ack));
|
assertTrue(a.writeBatchId(ack));
|
||||||
a.finish();
|
a.finish();
|
||||||
p.nextPacket();
|
packetWriter.finishPacket();
|
||||||
|
|
||||||
BatchWriter b = protocolWriterFactory.createBatchWriter(out);
|
BatchWriter b = protocolWriterFactory.createBatchWriter(out);
|
||||||
assertTrue(b.writeMessage(message.getBytes()));
|
assertTrue(b.writeMessage(message.getBytes()));
|
||||||
@@ -136,7 +141,7 @@ public class FileReadWriteTest extends TestCase {
|
|||||||
assertTrue(b.writeMessage(message2.getBytes()));
|
assertTrue(b.writeMessage(message2.getBytes()));
|
||||||
assertTrue(b.writeMessage(message3.getBytes()));
|
assertTrue(b.writeMessage(message3.getBytes()));
|
||||||
b.finish();
|
b.finish();
|
||||||
p.nextPacket();
|
packetWriter.finishPacket();
|
||||||
|
|
||||||
OfferWriter o = protocolWriterFactory.createOfferWriter(out);
|
OfferWriter o = protocolWriterFactory.createOfferWriter(out);
|
||||||
assertTrue(o.writeMessageId(message.getId()));
|
assertTrue(o.writeMessageId(message.getId()));
|
||||||
@@ -144,14 +149,14 @@ public class FileReadWriteTest extends TestCase {
|
|||||||
assertTrue(o.writeMessageId(message2.getId()));
|
assertTrue(o.writeMessageId(message2.getId()));
|
||||||
assertTrue(o.writeMessageId(message3.getId()));
|
assertTrue(o.writeMessageId(message3.getId()));
|
||||||
o.finish();
|
o.finish();
|
||||||
p.nextPacket();
|
packetWriter.finishPacket();
|
||||||
|
|
||||||
RequestWriter r = protocolWriterFactory.createRequestWriter(out);
|
RequestWriter r = protocolWriterFactory.createRequestWriter(out);
|
||||||
BitSet requested = new BitSet(4);
|
BitSet requested = new BitSet(4);
|
||||||
requested.set(1);
|
requested.set(1);
|
||||||
requested.set(3);
|
requested.set(3);
|
||||||
r.writeBitmap(requested, 4);
|
r.writeBitmap(requested, 4);
|
||||||
p.nextPacket();
|
packetWriter.finishPacket();
|
||||||
|
|
||||||
SubscriptionWriter s =
|
SubscriptionWriter s =
|
||||||
protocolWriterFactory.createSubscriptionWriter(out);
|
protocolWriterFactory.createSubscriptionWriter(out);
|
||||||
@@ -160,11 +165,11 @@ public class FileReadWriteTest extends TestCase {
|
|||||||
subs.put(group, 0L);
|
subs.put(group, 0L);
|
||||||
subs.put(group1, 0L);
|
subs.put(group1, 0L);
|
||||||
s.writeSubscriptions(subs);
|
s.writeSubscriptions(subs);
|
||||||
p.nextPacket();
|
packetWriter.finishPacket();
|
||||||
|
|
||||||
TransportWriter t = protocolWriterFactory.createTransportWriter(out);
|
TransportWriter t = protocolWriterFactory.createTransportWriter(out);
|
||||||
t.writeTransports(transports);
|
t.writeTransports(transports);
|
||||||
p.nextPacket();
|
packetWriter.finishPacket();
|
||||||
|
|
||||||
out.flush();
|
out.flush();
|
||||||
out.close();
|
out.close();
|
||||||
@@ -177,7 +182,7 @@ public class FileReadWriteTest extends TestCase {
|
|||||||
|
|
||||||
testWriteFile();
|
testWriteFile();
|
||||||
|
|
||||||
FileInputStream in = new FileInputStream(file);
|
InputStream in = new FileInputStream(file);
|
||||||
byte[] firstTag = new byte[16];
|
byte[] firstTag = new byte[16];
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
while(offset < 16) {
|
while(offset < 16) {
|
||||||
@@ -186,17 +191,22 @@ public class FileReadWriteTest extends TestCase {
|
|||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
assertEquals(16, offset);
|
assertEquals(16, offset);
|
||||||
PacketReader p = packetReaderFactory.createPacketReader(firstTag, in,
|
PacketReader packetReader = packetReaderFactory.createPacketReader(
|
||||||
transportId, connection, secret);
|
firstTag, in, transportId, connection, secret);
|
||||||
|
in = packetReader.getInputStream();
|
||||||
|
ProtocolReader protocolReader =
|
||||||
|
protocolReaderFactory.createProtocolReader(in);
|
||||||
|
|
||||||
// Read the ack
|
// Read the ack
|
||||||
assertTrue(p.hasAck());
|
assertTrue(protocolReader.hasAck());
|
||||||
Ack a = p.readAck();
|
Ack a = protocolReader.readAck();
|
||||||
|
packetReader.finishPacket();
|
||||||
assertEquals(Collections.singletonList(ack), a.getBatchIds());
|
assertEquals(Collections.singletonList(ack), a.getBatchIds());
|
||||||
|
|
||||||
// Read the batch
|
// Read the batch
|
||||||
assertTrue(p.hasBatch());
|
assertTrue(protocolReader.hasBatch());
|
||||||
Batch b = p.readBatch();
|
Batch b = protocolReader.readBatch();
|
||||||
|
packetReader.finishPacket();
|
||||||
Collection<Message> messages = b.getMessages();
|
Collection<Message> messages = b.getMessages();
|
||||||
assertEquals(4, messages.size());
|
assertEquals(4, messages.size());
|
||||||
Iterator<Message> it = messages.iterator();
|
Iterator<Message> it = messages.iterator();
|
||||||
@@ -206,8 +216,9 @@ public class FileReadWriteTest extends TestCase {
|
|||||||
checkMessageEquality(message3, it.next());
|
checkMessageEquality(message3, it.next());
|
||||||
|
|
||||||
// Read the offer
|
// Read the offer
|
||||||
assertTrue(p.hasOffer());
|
assertTrue(protocolReader.hasOffer());
|
||||||
Offer o = p.readOffer();
|
Offer o = protocolReader.readOffer();
|
||||||
|
packetReader.finishPacket();
|
||||||
Collection<MessageId> offered = o.getMessageIds();
|
Collection<MessageId> offered = o.getMessageIds();
|
||||||
assertEquals(4, offered.size());
|
assertEquals(4, offered.size());
|
||||||
Iterator<MessageId> it1 = offered.iterator();
|
Iterator<MessageId> it1 = offered.iterator();
|
||||||
@@ -217,8 +228,9 @@ public class FileReadWriteTest extends TestCase {
|
|||||||
assertEquals(message3.getId(), it1.next());
|
assertEquals(message3.getId(), it1.next());
|
||||||
|
|
||||||
// Read the request
|
// Read the request
|
||||||
assertTrue(p.hasRequest());
|
assertTrue(protocolReader.hasRequest());
|
||||||
Request r = p.readRequest();
|
Request r = protocolReader.readRequest();
|
||||||
|
packetReader.finishPacket();
|
||||||
BitSet requested = r.getBitmap();
|
BitSet requested = r.getBitmap();
|
||||||
assertFalse(requested.get(0));
|
assertFalse(requested.get(0));
|
||||||
assertTrue(requested.get(1));
|
assertTrue(requested.get(1));
|
||||||
@@ -228,8 +240,9 @@ public class FileReadWriteTest extends TestCase {
|
|||||||
assertEquals(2, requested.cardinality());
|
assertEquals(2, requested.cardinality());
|
||||||
|
|
||||||
// Read the subscription update
|
// Read the subscription update
|
||||||
assertTrue(p.hasSubscriptionUpdate());
|
assertTrue(protocolReader.hasSubscriptionUpdate());
|
||||||
SubscriptionUpdate s = p.readSubscriptionUpdate();
|
SubscriptionUpdate s = protocolReader.readSubscriptionUpdate();
|
||||||
|
packetReader.finishPacket();
|
||||||
Map<Group, Long> subs = s.getSubscriptions();
|
Map<Group, Long> subs = s.getSubscriptions();
|
||||||
assertEquals(2, subs.size());
|
assertEquals(2, subs.size());
|
||||||
assertEquals(Long.valueOf(0L), subs.get(group));
|
assertEquals(Long.valueOf(0L), subs.get(group));
|
||||||
@@ -238,11 +251,14 @@ public class FileReadWriteTest extends TestCase {
|
|||||||
assertTrue(s.getTimestamp() <= System.currentTimeMillis());
|
assertTrue(s.getTimestamp() <= System.currentTimeMillis());
|
||||||
|
|
||||||
// Read the transport update
|
// Read the transport update
|
||||||
assertTrue(p.hasTransportUpdate());
|
assertTrue(protocolReader.hasTransportUpdate());
|
||||||
TransportUpdate t = p.readTransportUpdate();
|
TransportUpdate t = protocolReader.readTransportUpdate();
|
||||||
|
packetReader.finishPacket();
|
||||||
assertEquals(transports, t.getTransports());
|
assertEquals(transports, t.getTransports());
|
||||||
assertTrue(t.getTimestamp() > start);
|
assertTrue(t.getTimestamp() > start);
|
||||||
assertTrue(t.getTimestamp() <= System.currentTimeMillis());
|
assertTrue(t.getTimestamp() <= System.currentTimeMillis());
|
||||||
|
|
||||||
|
in.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
98
test/net/sf/briar/transport/PacketReadWriteTest.java
Normal file
98
test/net/sf/briar/transport/PacketReadWriteTest.java
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import net.sf.briar.api.crypto.CryptoComponent;
|
||||||
|
import net.sf.briar.api.transport.PacketReader;
|
||||||
|
import net.sf.briar.api.transport.PacketWriter;
|
||||||
|
import net.sf.briar.crypto.CryptoModule;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
|
||||||
|
public class PacketReadWriteTest extends TestCase {
|
||||||
|
|
||||||
|
private final CryptoComponent crypto;
|
||||||
|
private final Cipher tagCipher, packetCipher;
|
||||||
|
private final SecretKey macKey, tagKey, packetKey;
|
||||||
|
private final Mac mac;
|
||||||
|
private final Random random;
|
||||||
|
private final byte[] secret = new byte[100];
|
||||||
|
private final int transportId = 999;
|
||||||
|
private final long connection = 1234L;
|
||||||
|
|
||||||
|
public PacketReadWriteTest() {
|
||||||
|
super();
|
||||||
|
Injector i = Guice.createInjector(new CryptoModule());
|
||||||
|
crypto = i.getInstance(CryptoComponent.class);
|
||||||
|
tagCipher = crypto.getTagCipher();
|
||||||
|
packetCipher = crypto.getPacketCipher();
|
||||||
|
macKey = crypto.deriveMacKey(secret);
|
||||||
|
tagKey = crypto.deriveTagKey(secret);
|
||||||
|
packetKey = crypto.derivePacketKey(secret);
|
||||||
|
mac = crypto.getMac();
|
||||||
|
random = new Random();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteAndRead() throws Exception {
|
||||||
|
// Generate two random packets
|
||||||
|
byte[] packet = new byte[12345];
|
||||||
|
random.nextBytes(packet);
|
||||||
|
byte[] packet1 = new byte[321];
|
||||||
|
random.nextBytes(packet1);
|
||||||
|
// Write the packets
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
PacketEncrypter encrypter = new PacketEncrypterImpl(out, tagCipher,
|
||||||
|
packetCipher, tagKey, packetKey);
|
||||||
|
mac.init(macKey);
|
||||||
|
PacketWriter writer = new PacketWriterImpl(encrypter, mac, transportId,
|
||||||
|
connection);
|
||||||
|
OutputStream out1 = writer.getOutputStream();
|
||||||
|
out1.write(packet);
|
||||||
|
writer.finishPacket();
|
||||||
|
out1.write(packet1);
|
||||||
|
writer.finishPacket();
|
||||||
|
// Read the packets back
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||||
|
byte[] firstTag = new byte[Constants.TAG_BYTES];
|
||||||
|
assertEquals(Constants.TAG_BYTES, in.read(firstTag));
|
||||||
|
PacketDecrypter decrypter = new PacketDecrypterImpl(firstTag, in,
|
||||||
|
tagCipher, packetCipher, tagKey, packetKey);
|
||||||
|
PacketReader reader = new PacketReaderImpl(decrypter, mac, transportId,
|
||||||
|
connection);
|
||||||
|
InputStream in1 = reader.getInputStream();
|
||||||
|
byte[] recovered = new byte[packet.length];
|
||||||
|
int offset = 0;
|
||||||
|
while(offset < recovered.length) {
|
||||||
|
int read = in1.read(recovered, offset, recovered.length - offset);
|
||||||
|
if(read == -1) break;
|
||||||
|
offset += read;
|
||||||
|
}
|
||||||
|
assertEquals(recovered.length, offset);
|
||||||
|
reader.finishPacket();
|
||||||
|
assertTrue(Arrays.equals(packet, recovered));
|
||||||
|
byte[] recovered1 = new byte[packet1.length];
|
||||||
|
offset = 0;
|
||||||
|
while(offset < recovered1.length) {
|
||||||
|
int read = in1.read(recovered1, offset, recovered1.length - offset);
|
||||||
|
if(read == -1) break;
|
||||||
|
offset += read;
|
||||||
|
}
|
||||||
|
assertEquals(recovered1.length, offset);
|
||||||
|
reader.finishPacket();
|
||||||
|
assertTrue(Arrays.equals(packet1, recovered1));
|
||||||
|
}
|
||||||
|
}
|
||||||
187
test/net/sf/briar/transport/PacketReaderImplTest.java
Normal file
187
test/net/sf/briar/transport/PacketReaderImplTest.java
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import net.sf.briar.api.crypto.CryptoComponent;
|
||||||
|
import net.sf.briar.api.transport.PacketReader;
|
||||||
|
import net.sf.briar.crypto.CryptoModule;
|
||||||
|
import net.sf.briar.util.StringUtils;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
|
||||||
|
public class PacketReaderImplTest extends TestCase {
|
||||||
|
|
||||||
|
private final Mac mac;
|
||||||
|
|
||||||
|
public PacketReaderImplTest() throws Exception {
|
||||||
|
super();
|
||||||
|
Injector i = Guice.createInjector(new CryptoModule());
|
||||||
|
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||||
|
mac = crypto.getMac();
|
||||||
|
mac.init(crypto.generateSecretKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFirstReadTriggersTag() throws Exception {
|
||||||
|
// TAG_BYTES for the tag, 1 byte for the packet
|
||||||
|
byte[] b = new byte[Constants.TAG_BYTES + 1];
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||||
|
PacketDecrypter d = new NullPacketDecrypter(in);
|
||||||
|
PacketReader p = new PacketReaderImpl(d, mac, 0, 0L);
|
||||||
|
// There should be one byte available before EOF
|
||||||
|
assertEquals(0, p.getInputStream().read());
|
||||||
|
assertEquals(-1, p.getInputStream().read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFinishPacketAfterReadTriggersMac() throws Exception {
|
||||||
|
// TAG_BYTES for the tag, 1 byte for the packet
|
||||||
|
byte[] b = new byte[Constants.TAG_BYTES + 1];
|
||||||
|
// Calculate the MAC and append it to the packet
|
||||||
|
mac.update(b);
|
||||||
|
byte[] macBytes = mac.doFinal();
|
||||||
|
byte[] b1 = Arrays.copyOf(b, b.length + macBytes.length);
|
||||||
|
System.arraycopy(macBytes, 0, b1, b.length, macBytes.length);
|
||||||
|
// Check that the PacketReader reads and verifies the MAC
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(b1);
|
||||||
|
PacketDecrypter d = new NullPacketDecrypter(in);
|
||||||
|
PacketReader p = new PacketReaderImpl(d, mac, 0, 0L);
|
||||||
|
assertEquals(0, p.getInputStream().read());
|
||||||
|
p.finishPacket();
|
||||||
|
// Reading the MAC should take us to EOF
|
||||||
|
assertEquals(-1, p.getInputStream().read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifyingPacketInvalidatesMac() throws Exception {
|
||||||
|
// TAG_BYTES for the tag, 1 byte for the packet
|
||||||
|
byte[] b = new byte[Constants.TAG_BYTES + 1];
|
||||||
|
// Calculate the MAC and append it to the packet
|
||||||
|
mac.update(b);
|
||||||
|
byte[] macBytes = mac.doFinal();
|
||||||
|
byte[] b1 = Arrays.copyOf(b, b.length + macBytes.length);
|
||||||
|
System.arraycopy(macBytes, 0, b1, b.length, macBytes.length);
|
||||||
|
// Modify the packet
|
||||||
|
b1[Constants.TAG_BYTES] = (byte) 1;
|
||||||
|
// Check that the PacketReader reads and fails to verify the MAC
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(b1);
|
||||||
|
PacketDecrypter d = new NullPacketDecrypter(in);
|
||||||
|
PacketReader p = new PacketReaderImpl(d, mac, 0, 0L);
|
||||||
|
assertEquals(1, p.getInputStream().read());
|
||||||
|
try {
|
||||||
|
p.finishPacket();
|
||||||
|
fail();
|
||||||
|
} catch(GeneralSecurityException expected) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtraCallsToFinishPacketDoNothing() throws Exception {
|
||||||
|
// TAG_BYTES for the tag, 1 byte for the packet
|
||||||
|
byte[] b = new byte[Constants.TAG_BYTES + 1];
|
||||||
|
// Calculate the MAC and append it to the packet
|
||||||
|
mac.update(b);
|
||||||
|
byte[] macBytes = mac.doFinal();
|
||||||
|
byte[] b1 = Arrays.copyOf(b, b.length + macBytes.length);
|
||||||
|
System.arraycopy(macBytes, 0, b1, b.length, macBytes.length);
|
||||||
|
// Check that the PacketReader reads and verifies the MAC
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(b1);
|
||||||
|
PacketDecrypter d = new NullPacketDecrypter(in);
|
||||||
|
PacketReader p = new PacketReaderImpl(d, mac, 0, 0L);
|
||||||
|
// Initial calls to finishPacket() should have no effect
|
||||||
|
p.finishPacket();
|
||||||
|
p.finishPacket();
|
||||||
|
p.finishPacket();
|
||||||
|
assertEquals(0, p.getInputStream().read());
|
||||||
|
p.finishPacket();
|
||||||
|
// Extra calls to finishPacket() should have no effect
|
||||||
|
p.finishPacket();
|
||||||
|
p.finishPacket();
|
||||||
|
p.finishPacket();
|
||||||
|
// Reading the MAC should take us to EOF
|
||||||
|
assertEquals(-1, p.getInputStream().read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPacketNumberIsIncremented() throws Exception {
|
||||||
|
byte[] tag = StringUtils.fromHexString(
|
||||||
|
"0000" // 16 bits reserved
|
||||||
|
+ "F00D" // 16 bits for the transport ID
|
||||||
|
+ "DEADBEEF" // 32 bits for the connection number
|
||||||
|
+ "00000000" // 32 bits for the packet number
|
||||||
|
+ "00000000" // 32 bits for the block number
|
||||||
|
);
|
||||||
|
assertEquals(Constants.TAG_BYTES, tag.length);
|
||||||
|
byte[] tag1 = StringUtils.fromHexString(
|
||||||
|
"0000" // 16 bits reserved
|
||||||
|
+ "F00D" // 16 bits for the transport ID
|
||||||
|
+ "DEADBEEF" // 32 bits for the connection number
|
||||||
|
+ "00000001" // 32 bits for the packet number
|
||||||
|
+ "00000000" // 32 bits for the block number
|
||||||
|
);
|
||||||
|
assertEquals(Constants.TAG_BYTES, tag1.length);
|
||||||
|
// Calculate the MAC on the first packet and append it to the packet
|
||||||
|
mac.update(tag);
|
||||||
|
mac.update((byte) 0);
|
||||||
|
byte[] macBytes = mac.doFinal();
|
||||||
|
byte[] b = Arrays.copyOf(tag, tag.length + 1 + macBytes.length);
|
||||||
|
System.arraycopy(macBytes, 0, b, tag.length + 1, macBytes.length);
|
||||||
|
// Calculate the MAC on the second packet and append it to the packet
|
||||||
|
mac.update(tag1);
|
||||||
|
mac.update((byte) 0);
|
||||||
|
byte[] macBytes1 = mac.doFinal();
|
||||||
|
byte[] b1 = Arrays.copyOf(tag1, tag1.length + 1 + macBytes1.length);
|
||||||
|
System.arraycopy(macBytes1, 0, b1, tag.length + 1, macBytes1.length);
|
||||||
|
// Check that the PacketReader accepts the correct tags and MACs
|
||||||
|
byte[] b2 = Arrays.copyOf(b, b.length + b1.length);
|
||||||
|
System.arraycopy(b1, 0, b2, b.length, b1.length);
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(b2);
|
||||||
|
PacketDecrypter d = new NullPacketDecrypter(in);
|
||||||
|
PacketReader p = new PacketReaderImpl(d, mac, 0xF00D, 0xDEADBEEFL);
|
||||||
|
// Packet one
|
||||||
|
assertEquals(0, p.getInputStream().read());
|
||||||
|
p.finishPacket();
|
||||||
|
// Packet two
|
||||||
|
assertEquals(0, p.getInputStream().read());
|
||||||
|
p.finishPacket();
|
||||||
|
// We should be at EOF
|
||||||
|
assertEquals(-1, p.getInputStream().read());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A PacketDecrypter that performs no decryption. */
|
||||||
|
private static class NullPacketDecrypter implements PacketDecrypter {
|
||||||
|
|
||||||
|
private final InputStream in;
|
||||||
|
|
||||||
|
private NullPacketDecrypter(InputStream in) {
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] readTag() throws IOException {
|
||||||
|
byte[] tag = new byte[Constants.TAG_BYTES];
|
||||||
|
int offset = 0;
|
||||||
|
while(offset < tag.length) {
|
||||||
|
int read = in.read(tag, offset, tag.length - offset);
|
||||||
|
if(read == -1) break;
|
||||||
|
offset += read;
|
||||||
|
}
|
||||||
|
if(offset == 0) return null; // EOF between packets is acceptable
|
||||||
|
if(offset < tag.length) throw new EOFException();
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,52 +36,56 @@ public class PacketWriterImplTest extends TestCase {
|
|||||||
PacketEncrypter e = new NullPacketEncrypter(out);
|
PacketEncrypter e = new NullPacketEncrypter(out);
|
||||||
PacketWriter p = new PacketWriterImpl(e, mac, 0, 0L);
|
PacketWriter p = new PacketWriterImpl(e, mac, 0, 0L);
|
||||||
p.getOutputStream().write(0);
|
p.getOutputStream().write(0);
|
||||||
// There should be TAG_BYTES bytes for the tag, 1 byte for the write
|
// There should be TAG_BYTES bytes for the tag, 1 byte for the packet
|
||||||
assertTrue(Arrays.equals(new byte[Constants.TAG_BYTES + 1],
|
assertTrue(Arrays.equals(new byte[Constants.TAG_BYTES + 1],
|
||||||
out.toByteArray()));
|
out.toByteArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNextPacketAfterWriteTriggersMac() throws Exception {
|
public void testFinishPacketAfterWriteTriggersMac() throws Exception {
|
||||||
// Calculate what the MAC should be
|
// Calculate what the MAC should be
|
||||||
mac.update(new byte[17]);
|
mac.update(new byte[Constants.TAG_BYTES + 1]);
|
||||||
byte[] expectedMac = mac.doFinal();
|
byte[] expectedMac = mac.doFinal();
|
||||||
// Check that the PacketWriter calculates and writes the correct MAC
|
// Check that the PacketWriter calculates and writes the correct MAC
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
PacketEncrypter e = new NullPacketEncrypter(out);
|
PacketEncrypter e = new NullPacketEncrypter(out);
|
||||||
PacketWriter p = new PacketWriterImpl(e, mac, 0, 0L);
|
PacketWriter p = new PacketWriterImpl(e, mac, 0, 0L);
|
||||||
p.getOutputStream().write(0);
|
p.getOutputStream().write(0);
|
||||||
p.nextPacket();
|
p.finishPacket();
|
||||||
byte[] written = out.toByteArray();
|
byte[] written = out.toByteArray();
|
||||||
assertEquals(17 + expectedMac.length, written.length);
|
assertEquals(Constants.TAG_BYTES + 1 + expectedMac.length,
|
||||||
|
written.length);
|
||||||
byte[] actualMac = new byte[expectedMac.length];
|
byte[] actualMac = new byte[expectedMac.length];
|
||||||
System.arraycopy(written, 17, actualMac, 0, actualMac.length);
|
System.arraycopy(written, Constants.TAG_BYTES + 1, actualMac, 0,
|
||||||
|
actualMac.length);
|
||||||
assertTrue(Arrays.equals(expectedMac, actualMac));
|
assertTrue(Arrays.equals(expectedMac, actualMac));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExtraCallsToNextPacketDoNothing() throws Exception {
|
public void testExtraCallsToFinishPacketDoNothing() throws Exception {
|
||||||
// Calculate what the MAC should be
|
// Calculate what the MAC should be
|
||||||
mac.update(new byte[17]);
|
mac.update(new byte[Constants.TAG_BYTES + 1]);
|
||||||
byte[] expectedMac = mac.doFinal();
|
byte[] expectedMac = mac.doFinal();
|
||||||
// Check that the PacketWriter calculates and writes the correct MAC
|
// Check that the PacketWriter calculates and writes the correct MAC
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
PacketEncrypter e = new NullPacketEncrypter(out);
|
PacketEncrypter e = new NullPacketEncrypter(out);
|
||||||
PacketWriter p = new PacketWriterImpl(e, mac, 0, 0L);
|
PacketWriter p = new PacketWriterImpl(e, mac, 0, 0L);
|
||||||
// Initial calls to nextPacket() should have no effect
|
// Initial calls to finishPacket() should have no effect
|
||||||
p.nextPacket();
|
p.finishPacket();
|
||||||
p.nextPacket();
|
p.finishPacket();
|
||||||
p.nextPacket();
|
p.finishPacket();
|
||||||
p.getOutputStream().write(0);
|
p.getOutputStream().write(0);
|
||||||
p.nextPacket();
|
p.finishPacket();
|
||||||
// Extra calls to nextPacket() should have no effect
|
// Extra calls to finishPacket() should have no effect
|
||||||
p.nextPacket();
|
p.finishPacket();
|
||||||
p.nextPacket();
|
p.finishPacket();
|
||||||
p.nextPacket();
|
p.finishPacket();
|
||||||
byte[] written = out.toByteArray();
|
byte[] written = out.toByteArray();
|
||||||
assertEquals(17 + expectedMac.length, written.length);
|
assertEquals(Constants.TAG_BYTES + 1 + expectedMac.length,
|
||||||
|
written.length);
|
||||||
byte[] actualMac = new byte[expectedMac.length];
|
byte[] actualMac = new byte[expectedMac.length];
|
||||||
System.arraycopy(written, 17, actualMac, 0, actualMac.length);
|
System.arraycopy(written, Constants.TAG_BYTES + 1, actualMac, 0,
|
||||||
|
actualMac.length);
|
||||||
assertTrue(Arrays.equals(expectedMac, actualMac));
|
assertTrue(Arrays.equals(expectedMac, actualMac));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,10 +121,10 @@ public class PacketWriterImplTest extends TestCase {
|
|||||||
PacketWriter p = new PacketWriterImpl(e, mac, 0xF00D, 0xDEADBEEFL);
|
PacketWriter p = new PacketWriterImpl(e, mac, 0xF00D, 0xDEADBEEFL);
|
||||||
// Packet one
|
// Packet one
|
||||||
p.getOutputStream().write(0);
|
p.getOutputStream().write(0);
|
||||||
p.nextPacket();
|
p.finishPacket();
|
||||||
// Packet two
|
// Packet two
|
||||||
p.getOutputStream().write(0);
|
p.getOutputStream().write(0);
|
||||||
p.nextPacket();
|
p.finishPacket();
|
||||||
byte[] written = out.toByteArray();
|
byte[] written = out.toByteArray();
|
||||||
assertEquals(Constants.TAG_BYTES + 1 + expectedMac.length
|
assertEquals(Constants.TAG_BYTES + 1 + expectedMac.length
|
||||||
+ Constants.TAG_BYTES + 1 + expectedMac1.length,
|
+ Constants.TAG_BYTES + 1 + expectedMac1.length,
|
||||||
@@ -146,6 +150,7 @@ public class PacketWriterImplTest extends TestCase {
|
|||||||
assertTrue(Arrays.equals(expectedMac1, actualMac1));
|
assertTrue(Arrays.equals(expectedMac1, actualMac1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A PacketEncrypter that performs no encryption. */
|
||||||
private static class NullPacketEncrypter implements PacketEncrypter {
|
private static class NullPacketEncrypter implements PacketEncrypter {
|
||||||
|
|
||||||
private final OutputStream out;
|
private final OutputStream out;
|
||||||
|
|||||||
Reference in New Issue
Block a user