Moved classes from messaging package to sync package.

This commit is contained in:
akwizgran
2015-12-15 14:36:45 +00:00
parent d99df73380
commit e370cafb12
94 changed files with 905 additions and 801 deletions

View File

@@ -0,0 +1,243 @@
package org.briarproject.sync;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.briarproject.BriarTestCase;
import org.briarproject.TestDatabaseModule;
import org.briarproject.TestLifecycleModule;
import org.briarproject.TestSystemModule;
import org.briarproject.TestUtils;
import org.briarproject.api.Author;
import org.briarproject.api.AuthorFactory;
import org.briarproject.api.TransportId;
import org.briarproject.api.TransportProperties;
import org.briarproject.api.UniqueId;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyPair;
import org.briarproject.api.crypto.PrivateKey;
import org.briarproject.api.crypto.Signature;
import org.briarproject.api.sync.Ack;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupFactory;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageFactory;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.Offer;
import org.briarproject.api.sync.PacketWriter;
import org.briarproject.api.sync.PacketWriterFactory;
import org.briarproject.api.sync.Request;
import org.briarproject.api.sync.SubscriptionUpdate;
import org.briarproject.api.sync.TransportUpdate;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.data.DataModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;
import static org.briarproject.api.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.api.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
import static org.briarproject.api.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
import static org.briarproject.api.TransportPropertyConstants.MAX_TRANSPORT_ID_LENGTH;
import static org.briarproject.api.sync.MessagingConstants.MAX_BODY_LENGTH;
import static org.briarproject.api.sync.MessagingConstants.MAX_CONTENT_TYPE_LENGTH;
import static org.briarproject.api.sync.MessagingConstants.MAX_GROUP_NAME_LENGTH;
import static org.briarproject.api.sync.MessagingConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.api.sync.MessagingConstants.MAX_SUBSCRIPTIONS;
import static org.junit.Assert.assertTrue;
public class ConstantsTest extends BriarTestCase {
private final CryptoComponent crypto;
private final GroupFactory groupFactory;
private final AuthorFactory authorFactory;
private final MessageFactory messageFactory;
private final PacketWriterFactory packetWriterFactory;
public ConstantsTest() throws Exception {
Injector i = Guice.createInjector(new TestDatabaseModule(),
new TestLifecycleModule(), new TestSystemModule(),
new CryptoModule(), new DatabaseModule(), new EventModule(),
new org.briarproject.sync.MessagingModule(), new DataModule());
crypto = i.getInstance(CryptoComponent.class);
groupFactory = i.getInstance(GroupFactory.class);
authorFactory = i.getInstance(AuthorFactory.class);
messageFactory = i.getInstance(MessageFactory.class);
packetWriterFactory = i.getInstance(PacketWriterFactory.class);
}
@Test
public void testAgreementPublicKeys() throws Exception {
// Generate 10 agreement key pairs
for (int i = 0; i < 10; i++) {
KeyPair keyPair = crypto.generateSignatureKeyPair();
// Check the length of the public key
byte[] publicKey = keyPair.getPublic().getEncoded();
assertTrue(publicKey.length <= MAX_PUBLIC_KEY_LENGTH);
}
}
@Test
public void testSignaturePublicKeys() throws Exception {
Random random = new Random();
Signature sig = crypto.getSignature();
// Generate 10 signature key pairs
for (int i = 0; i < 10; i++) {
KeyPair keyPair = crypto.generateSignatureKeyPair();
// Check the length of the public key
byte[] publicKey = keyPair.getPublic().getEncoded();
assertTrue(publicKey.length <= MAX_PUBLIC_KEY_LENGTH);
// Sign some random data and check the length of the signature
byte[] toBeSigned = new byte[1234];
random.nextBytes(toBeSigned);
sig.initSign(keyPair.getPrivate());
sig.update(toBeSigned);
byte[] signature = sig.sign();
assertTrue("Length " + signature.length,
signature.length <= MAX_SIGNATURE_LENGTH);
}
}
@Test
public void testMessageIdsFitIntoLargeAck() throws Exception {
testMessageIdsFitIntoAck(MAX_PAYLOAD_LENGTH);
}
@Test
public void testMessageIdsFitIntoSmallAck() throws Exception {
testMessageIdsFitIntoAck(1000);
}
@Test
public void testMessageFitsIntoPacket() throws Exception {
MessageId parent = new MessageId(TestUtils.getRandomId());
// Create a maximum-length group
String groupName = TestUtils.createRandomString(MAX_GROUP_NAME_LENGTH);
Group group = groupFactory.createGroup(groupName);
// Create a maximum-length author
String authorName =
TestUtils.createRandomString(MAX_AUTHOR_NAME_LENGTH);
byte[] authorPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
Author author = authorFactory.createAuthor(authorName, authorPublic);
// Create a maximum-length message
PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate();
String contentType =
TestUtils.createRandomString(MAX_CONTENT_TYPE_LENGTH);
long timestamp = Long.MAX_VALUE;
byte[] body = new byte[MAX_BODY_LENGTH];
Message message = messageFactory.createPseudonymousMessage(parent,
group, author, privateKey, contentType, timestamp, body);
// Check the size of the serialised message
int length = message.getSerialised().length;
assertTrue(length > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH
+ MAX_PUBLIC_KEY_LENGTH + MAX_AUTHOR_NAME_LENGTH
+ MAX_PUBLIC_KEY_LENGTH + MAX_CONTENT_TYPE_LENGTH
+ MAX_BODY_LENGTH);
assertTrue(length <= MAX_PAYLOAD_LENGTH);
}
@Test
public void testMessageIdsFitIntoLargeOffer() throws Exception {
testMessageIdsFitIntoOffer(MAX_PAYLOAD_LENGTH);
}
@Test
public void testMessageIdsFitIntoSmallOffer() throws Exception {
testMessageIdsFitIntoOffer(1000);
}
@Test
public void testMessageIdsFitIntoLargeRequest() throws Exception {
testMessageIdsFitIntoRequest(MAX_PAYLOAD_LENGTH);
}
@Test
public void testMessageIdsFitIntoSmallRequest() throws Exception {
testMessageIdsFitIntoRequest(1000);
}
@Test
public void testPropertiesFitIntoTransportUpdate() throws Exception {
// Create the maximum number of properties with the maximum length
TransportProperties p = new TransportProperties();
for (int i = 0; i < MAX_PROPERTIES_PER_TRANSPORT; i++) {
String key = TestUtils.createRandomString(MAX_PROPERTY_LENGTH);
String value = TestUtils.createRandomString(MAX_PROPERTY_LENGTH);
p.put(key, value);
}
// Create a maximum-length transport update
String idString = TestUtils.createRandomString(MAX_TRANSPORT_ID_LENGTH);
TransportId id = new TransportId(idString);
TransportUpdate u = new TransportUpdate(id, p, Long.MAX_VALUE);
// Serialise the update
ByteArrayOutputStream out = new ByteArrayOutputStream();
PacketWriter writer = packetWriterFactory.createPacketWriter(out);
writer.writeTransportUpdate(u);
// Check the size of the serialised transport update
assertTrue(out.size() <= MAX_PAYLOAD_LENGTH);
}
@Test
public void testGroupsFitIntoSubscriptionUpdate() throws Exception {
// Create the maximum number of maximum-length groups
Collection<Group> groups = new ArrayList<Group>();
for (int i = 0; i < MAX_SUBSCRIPTIONS; i++) {
String name = TestUtils.createRandomString(MAX_GROUP_NAME_LENGTH);
groups.add(groupFactory.createGroup(name));
}
// Create a maximum-length subscription update
SubscriptionUpdate u = new SubscriptionUpdate(groups, Long.MAX_VALUE);
// Serialise the update
ByteArrayOutputStream out = new ByteArrayOutputStream();
PacketWriter writer = packetWriterFactory.createPacketWriter(out);
writer.writeSubscriptionUpdate(u);
// Check the size of the serialised subscription update
assertTrue(out.size() <= MAX_PAYLOAD_LENGTH);
}
private void testMessageIdsFitIntoAck(int length) throws Exception {
// Create an ack with as many message IDs as possible
ByteArrayOutputStream out = new ByteArrayOutputStream(length);
PacketWriter writer = packetWriterFactory.createPacketWriter(out);
int maxMessages = writer.getMaxMessagesForAck(length);
Collection<MessageId> ids = new ArrayList<MessageId>();
for (int i = 0; i < maxMessages; i++)
ids.add(new MessageId(TestUtils.getRandomId()));
writer.writeAck(new Ack(ids));
// Check the size of the serialised ack
assertTrue(out.size() <= length);
}
private void testMessageIdsFitIntoRequest(int length) throws Exception {
// Create a request with as many message IDs as possible
ByteArrayOutputStream out = new ByteArrayOutputStream(length);
PacketWriter writer = packetWriterFactory.createPacketWriter(out);
int maxMessages = writer.getMaxMessagesForRequest(length);
Collection<MessageId> ids = new ArrayList<MessageId>();
for (int i = 0; i < maxMessages; i++)
ids.add(new MessageId(TestUtils.getRandomId()));
writer.writeRequest(new Request(ids));
// Check the size of the serialised request
assertTrue(out.size() <= length);
}
private void testMessageIdsFitIntoOffer(int length) throws Exception {
// Create an offer with as many message IDs as possible
ByteArrayOutputStream out = new ByteArrayOutputStream(length);
PacketWriter writer = packetWriterFactory.createPacketWriter(out);
int maxMessages = writer.getMaxMessagesForOffer(length);
Collection<MessageId> ids = new ArrayList<MessageId>();
for (int i = 0; i < maxMessages; i++)
ids.add(new MessageId(TestUtils.getRandomId()));
writer.writeOffer(new Offer(ids));
// Check the size of the serialised offer
assertTrue(out.size() <= length);
}
}

View File

@@ -0,0 +1,108 @@
package org.briarproject.sync;
import org.briarproject.BriarTestCase;
import org.briarproject.api.FormatException;
import org.briarproject.api.crypto.MessageDigest;
import org.junit.Test;
import java.security.GeneralSecurityException;
import java.util.Random;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class ConsumersTest extends BriarTestCase {
@Test
public void testDigestingConsumer() throws Exception {
byte[] data = new byte[1234];
// Generate some random data and digest it
new Random().nextBytes(data);
MessageDigest messageDigest = new TestMessageDigest();
messageDigest.update(data);
byte[] dig = messageDigest.digest();
// Check that feeding a DigestingConsumer generates the same digest
org.briarproject.sync.DigestingConsumer
dc = new org.briarproject.sync.DigestingConsumer(messageDigest);
dc.write(data[0]);
dc.write(data, 1, data.length - 2);
dc.write(data[data.length - 1]);
byte[] dig1 = messageDigest.digest();
assertArrayEquals(dig, dig1);
}
@Test
public void testCountingConsumer() throws Exception {
byte[] data = new byte[1234];
org.briarproject.sync.CountingConsumer
cc = new org.briarproject.sync.CountingConsumer(data.length);
cc.write(data[0]);
cc.write(data, 1, data.length - 2);
cc.write(data[data.length - 1]);
assertEquals(data.length, cc.getCount());
try {
cc.write((byte) 0);
fail();
} catch (FormatException expected) {
// Expected
}
}
@Test
public void testCopyingConsumer() throws Exception {
byte[] data = new byte[1234];
new Random().nextBytes(data);
// Check that a CopyingConsumer creates a faithful copy
org.briarproject.sync.CopyingConsumer
cc = new org.briarproject.sync.CopyingConsumer();
cc.write(data[0]);
cc.write(data, 1, data.length - 2);
cc.write(data[data.length - 1]);
assertArrayEquals(data, cc.getCopy());
}
private static class TestMessageDigest implements MessageDigest {
private final java.security.MessageDigest delegate;
private TestMessageDigest() throws GeneralSecurityException {
delegate = java.security.MessageDigest.getInstance("SHA-256");
}
public byte[] digest() {
return delegate.digest();
}
public byte[] digest(byte[] input) {
return delegate.digest(input);
}
public int digest(byte[] buf, int offset, int len) {
byte[] digest = digest();
len = Math.min(len, digest.length);
System.arraycopy(digest, 0, buf, offset, len);
return len;
}
public int getDigestLength() {
return delegate.getDigestLength();
}
public void reset() {
delegate.reset();
}
public void update(byte input) {
delegate.update(input);
}
public void update(byte[] input) {
delegate.update(input);
}
public void update(byte[] input, int offset, int len) {
delegate.update(input, offset, len);
}
}
}

View File

@@ -0,0 +1,263 @@
package org.briarproject.sync;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils;
import org.briarproject.api.FormatException;
import org.briarproject.api.data.ReaderFactory;
import org.briarproject.api.data.Writer;
import org.briarproject.api.data.WriterFactory;
import org.briarproject.data.DataModule;
import org.briarproject.util.ByteUtils;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import static org.briarproject.api.data.DataConstants.LIST_END_LENGTH;
import static org.briarproject.api.data.DataConstants.UNIQUE_ID_LENGTH;
import static org.briarproject.api.sync.MessagingConstants.HEADER_LENGTH;
import static org.briarproject.api.sync.MessagingConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.api.sync.PacketTypes.ACK;
import static org.briarproject.api.sync.PacketTypes.OFFER;
import static org.briarproject.api.sync.PacketTypes.REQUEST;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class PacketReaderImplTest extends BriarTestCase {
// FIXME: This is an integration test, not a unit test
private final ReaderFactory readerFactory;
private final WriterFactory writerFactory;
public PacketReaderImplTest() throws Exception {
Injector i = Guice.createInjector(new DataModule());
readerFactory = i.getInstance(ReaderFactory.class);
writerFactory = i.getInstance(WriterFactory.class);
}
@Test
public void testFormatExceptionIfAckIsTooLarge() throws Exception {
byte[] b = createAck(true);
ByteArrayInputStream in = new ByteArrayInputStream(b);
org.briarproject.sync.PacketReaderImpl
reader = new org.briarproject.sync.PacketReaderImpl(readerFactory, null,
null, in);
try {
reader.readAck();
fail();
} catch (FormatException expected) {
// Expected
}
}
@Test
public void testNoFormatExceptionIfAckIsMaximumSize() throws Exception {
byte[] b = createAck(false);
ByteArrayInputStream in = new ByteArrayInputStream(b);
org.briarproject.sync.PacketReaderImpl
reader = new org.briarproject.sync.PacketReaderImpl(readerFactory, null,
null, in);
reader.readAck();
}
@Test
public void testEmptyAck() throws Exception {
byte[] b = createEmptyAck();
ByteArrayInputStream in = new ByteArrayInputStream(b);
org.briarproject.sync.PacketReaderImpl
reader = new org.briarproject.sync.PacketReaderImpl(readerFactory, null,
null, in);
try {
reader.readAck();
fail();
} catch (FormatException expected) {
// Expected
}
}
@Test
public void testFormatExceptionIfOfferIsTooLarge() throws Exception {
byte[] b = createOffer(true);
ByteArrayInputStream in = new ByteArrayInputStream(b);
org.briarproject.sync.PacketReaderImpl
reader = new org.briarproject.sync.PacketReaderImpl(readerFactory, null,
null, in);
try {
reader.readOffer();
fail();
} catch (FormatException expected) {
// Expected
}
}
@Test
public void testNoFormatExceptionIfOfferIsMaximumSize() throws Exception {
byte[] b = createOffer(false);
ByteArrayInputStream in = new ByteArrayInputStream(b);
org.briarproject.sync.PacketReaderImpl
reader = new org.briarproject.sync.PacketReaderImpl(readerFactory, null,
null, in);
reader.readOffer();
}
@Test
public void testEmptyOffer() throws Exception {
byte[] b = createEmptyOffer();
ByteArrayInputStream in = new ByteArrayInputStream(b);
org.briarproject.sync.PacketReaderImpl
reader = new org.briarproject.sync.PacketReaderImpl(readerFactory, null,
null, in);
try {
reader.readOffer();
fail();
} catch (FormatException expected) {
// Expected
}
}
@Test
public void testFormatExceptionIfRequestIsTooLarge() throws Exception {
byte[] b = createRequest(true);
ByteArrayInputStream in = new ByteArrayInputStream(b);
org.briarproject.sync.PacketReaderImpl
reader = new org.briarproject.sync.PacketReaderImpl(readerFactory, null,
null, in);
try {
reader.readRequest();
fail();
} catch (FormatException expected) {
// Expected
}
}
@Test
public void testNoFormatExceptionIfRequestIsMaximumSize() throws Exception {
byte[] b = createRequest(false);
ByteArrayInputStream in = new ByteArrayInputStream(b);
org.briarproject.sync.PacketReaderImpl
reader = new org.briarproject.sync.PacketReaderImpl(readerFactory, null,
null, in);
reader.readRequest();
}
@Test
public void testEmptyRequest() throws Exception {
byte[] b = createEmptyRequest();
ByteArrayInputStream in = new ByteArrayInputStream(b);
org.briarproject.sync.PacketReaderImpl
reader = new org.briarproject.sync.PacketReaderImpl(readerFactory, null,
null, in);
try {
reader.readRequest();
fail();
} catch (FormatException expected) {
// Expected
}
}
private byte[] createAck(boolean tooBig) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(new byte[HEADER_LENGTH]);
Writer w = writerFactory.createWriter(out);
w.writeListStart();
w.writeListStart();
while (out.size() + UNIQUE_ID_LENGTH + LIST_END_LENGTH * 2
< HEADER_LENGTH + MAX_PAYLOAD_LENGTH) {
w.writeRaw(TestUtils.getRandomId());
}
if (tooBig) w.writeRaw(TestUtils.getRandomId());
w.writeListEnd();
w.writeListEnd();
assertEquals(tooBig, out.size() > HEADER_LENGTH + MAX_PAYLOAD_LENGTH);
byte[] packet = out.toByteArray();
packet[1] = ACK;
ByteUtils.writeUint16(packet.length - HEADER_LENGTH, packet, 2);
return packet;
}
private byte[] createEmptyAck() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(new byte[HEADER_LENGTH]);
Writer w = writerFactory.createWriter(out);
w.writeListStart();
w.writeListStart();
w.writeListEnd();
w.writeListEnd();
byte[] packet = out.toByteArray();
packet[1] = ACK;
ByteUtils.writeUint16(packet.length - HEADER_LENGTH, packet, 2);
return packet;
}
private byte[] createOffer(boolean tooBig) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(new byte[HEADER_LENGTH]);
Writer w = writerFactory.createWriter(out);
w.writeListStart();
w.writeListStart();
while (out.size() + UNIQUE_ID_LENGTH + LIST_END_LENGTH * 2
< HEADER_LENGTH + MAX_PAYLOAD_LENGTH) {
w.writeRaw(TestUtils.getRandomId());
}
if (tooBig) w.writeRaw(TestUtils.getRandomId());
w.writeListEnd();
w.writeListEnd();
assertEquals(tooBig, out.size() > HEADER_LENGTH + MAX_PAYLOAD_LENGTH);
byte[] packet = out.toByteArray();
packet[1] = OFFER;
ByteUtils.writeUint16(packet.length - HEADER_LENGTH, packet, 2);
return packet;
}
private byte[] createEmptyOffer() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(new byte[HEADER_LENGTH]);
Writer w = writerFactory.createWriter(out);
w.writeListStart();
w.writeListStart();
w.writeListEnd();
w.writeListEnd();
byte[] packet = out.toByteArray();
packet[1] = OFFER;
ByteUtils.writeUint16(packet.length - HEADER_LENGTH, packet, 2);
return packet;
}
private byte[] createRequest(boolean tooBig) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(new byte[HEADER_LENGTH]);
Writer w = writerFactory.createWriter(out);
w.writeListStart();
w.writeListStart();
while (out.size() + UNIQUE_ID_LENGTH + LIST_END_LENGTH * 2
< HEADER_LENGTH + MAX_PAYLOAD_LENGTH) {
w.writeRaw(TestUtils.getRandomId());
}
if (tooBig) w.writeRaw(TestUtils.getRandomId());
w.writeListEnd();
w.writeListEnd();
assertEquals(tooBig, out.size() > HEADER_LENGTH + MAX_PAYLOAD_LENGTH);
byte[] packet = out.toByteArray();
packet[1] = REQUEST;
ByteUtils.writeUint16(packet.length - HEADER_LENGTH, packet, 2);
return packet;
}
private byte[] createEmptyRequest() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(new byte[HEADER_LENGTH]);
Writer w = writerFactory.createWriter(out);
w.writeListStart();
w.writeListStart();
w.writeListEnd();
w.writeListEnd();
byte[] packet = out.toByteArray();
packet[1] = REQUEST;
ByteUtils.writeUint16(packet.length - HEADER_LENGTH, packet, 2);
return packet;
}
}

View File

@@ -0,0 +1,247 @@
package org.briarproject.sync;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.briarproject.BriarTestCase;
import org.briarproject.TestDatabaseModule;
import org.briarproject.TestLifecycleModule;
import org.briarproject.TestSystemModule;
import org.briarproject.TestUtils;
import org.briarproject.api.Author;
import org.briarproject.api.AuthorId;
import org.briarproject.api.ContactId;
import org.briarproject.api.LocalAuthor;
import org.briarproject.api.TransportId;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.MessageAddedEvent;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupFactory;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageFactory;
import org.briarproject.api.sync.MessageVerifier;
import org.briarproject.api.sync.MessagingSession;
import org.briarproject.api.sync.PacketReader;
import org.briarproject.api.sync.PacketReaderFactory;
import org.briarproject.api.sync.PacketWriter;
import org.briarproject.api.sync.PacketWriterFactory;
import org.briarproject.api.transport.KeyManager;
import org.briarproject.api.transport.StreamContext;
import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory;
import org.briarproject.api.transport.TransportKeys;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.data.DataModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
import org.briarproject.plugins.ImmediateExecutor;
import org.briarproject.transport.TransportModule;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import static org.briarproject.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.sync.MessagingConstants.GROUP_SALT_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class SimplexMessagingIntegrationTest extends BriarTestCase {
private static final int MAX_LATENCY = 2 * 60 * 1000; // 2 minutes
private static final long ROTATION_PERIOD_LENGTH =
MAX_LATENCY + MAX_CLOCK_DIFFERENCE;
private final File testDir = TestUtils.getTestDirectory();
private final File aliceDir = new File(testDir, "alice");
private final File bobDir = new File(testDir, "bob");
private final TransportId transportId = new TransportId("id");
private final SecretKey master = TestUtils.createSecretKey();
private final long timestamp = System.currentTimeMillis();
private Injector alice, bob;
@Before
public void setUp() {
testDir.mkdirs();
alice = createInjector(aliceDir);
bob = createInjector(bobDir);
}
private Injector createInjector(File dir) {
return Guice.createInjector(new TestDatabaseModule(dir),
new TestLifecycleModule(), new TestSystemModule(),
new CryptoModule(), new DatabaseModule(), new EventModule(),
new org.briarproject.sync.MessagingModule(), new DataModule(),
new TransportModule());
}
@Test
public void testWriteAndRead() throws Exception {
read(write());
}
private byte[] write() throws Exception {
// Open Alice's database
DatabaseComponent db = alice.getInstance(DatabaseComponent.class);
assertFalse(db.open());
// Start Alice's key manager
KeyManager keyManager = alice.getInstance(KeyManager.class);
keyManager.start();
// Add a local pseudonym for Alice
AuthorId aliceId = new AuthorId(TestUtils.getRandomId());
LocalAuthor aliceAuthor = new LocalAuthor(aliceId, "Alice",
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100], 1234);
db.addLocalAuthor(aliceAuthor);
// Add Bob as a contact
AuthorId bobId = new AuthorId(TestUtils.getRandomId());
Author bobAuthor = new Author(bobId, "Bob",
new byte[MAX_PUBLIC_KEY_LENGTH]);
ContactId contactId = db.addContact(bobAuthor, aliceId);
// Add the inbox group
GroupFactory gf = alice.getInstance(GroupFactory.class);
Group group = gf.createGroup("Group", new byte[GROUP_SALT_LENGTH]);
db.addGroup(group);
db.setInboxGroup(contactId, group);
// Add the transport
db.addTransport(transportId, MAX_LATENCY);
// Derive and store the transport keys
long rotationPeriod = timestamp / ROTATION_PERIOD_LENGTH;
CryptoComponent crypto = alice.getInstance(CryptoComponent.class);
TransportKeys keys = crypto.deriveTransportKeys(transportId, master,
rotationPeriod, true);
db.addTransportKeys(contactId, keys);
keyManager.contactAdded(contactId, Collections.singletonList(keys));
// Send Bob a message
String contentType = "text/plain";
byte[] body = "Hi Bob!".getBytes("UTF-8");
MessageFactory messageFactory = alice.getInstance(MessageFactory.class);
Message message = messageFactory.createAnonymousMessage(null, group,
contentType, timestamp, body);
db.addLocalMessage(message);
// Get a stream context
StreamContext ctx = keyManager.getStreamContext(contactId, transportId);
assertNotNull(ctx);
// Create a stream writer
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamWriterFactory streamWriterFactory =
alice.getInstance(StreamWriterFactory.class);
OutputStream streamWriter =
streamWriterFactory.createStreamWriter(out, ctx);
// Create an outgoing messaging session
EventBus eventBus = alice.getInstance(EventBus.class);
PacketWriterFactory packetWriterFactory =
alice.getInstance(PacketWriterFactory.class);
PacketWriter packetWriter = packetWriterFactory.createPacketWriter(
streamWriter);
MessagingSession session = new org.briarproject.sync.SimplexOutgoingSession(db,
new ImmediateExecutor(), eventBus, contactId, transportId,
MAX_LATENCY, packetWriter);
// Write whatever needs to be written
session.run();
streamWriter.close();
// Clean up
keyManager.stop();
db.close();
// Return the contents of the stream
return out.toByteArray();
}
private void read(byte[] stream) throws Exception {
// Open Bob's database
DatabaseComponent db = bob.getInstance(DatabaseComponent.class);
assertFalse(db.open());
// Start Bob's key manager
KeyManager keyManager = bob.getInstance(KeyManager.class);
keyManager.start();
// Add a local pseudonym for Bob
AuthorId bobId = new AuthorId(TestUtils.getRandomId());
LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob",
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100], 1234);
db.addLocalAuthor(bobAuthor);
// Add Alice as a contact
AuthorId aliceId = new AuthorId(TestUtils.getRandomId());
Author aliceAuthor = new Author(aliceId, "Alice",
new byte[MAX_PUBLIC_KEY_LENGTH]);
ContactId contactId = db.addContact(aliceAuthor, bobId);
// Add the inbox group
GroupFactory gf = bob.getInstance(GroupFactory.class);
Group group = gf.createGroup("Group", new byte[GROUP_SALT_LENGTH]);
db.addGroup(group);
db.setInboxGroup(contactId, group);
// Add the transport
db.addTransport(transportId, MAX_LATENCY);
// Derive and store the transport keys
long rotationPeriod = timestamp / ROTATION_PERIOD_LENGTH;
CryptoComponent crypto = bob.getInstance(CryptoComponent.class);
TransportKeys keys = crypto.deriveTransportKeys(transportId, master,
rotationPeriod, false);
db.addTransportKeys(contactId, keys);
keyManager.contactAdded(contactId, Collections.singletonList(keys));
// Set up an event listener
MessageListener listener = new MessageListener();
bob.getInstance(EventBus.class).addListener(listener);
// Read and recognise the tag
ByteArrayInputStream in = new ByteArrayInputStream(stream);
byte[] tag = new byte[TAG_LENGTH];
int read = in.read(tag);
assertEquals(tag.length, read);
StreamContext ctx = keyManager.recogniseTag(transportId, tag);
assertNotNull(ctx);
// Create a stream reader
StreamReaderFactory streamReaderFactory =
bob.getInstance(StreamReaderFactory.class);
InputStream streamReader =
streamReaderFactory.createStreamReader(in, ctx);
// Create an incoming messaging session
EventBus eventBus = bob.getInstance(EventBus.class);
MessageVerifier messageVerifier =
bob.getInstance(MessageVerifier.class);
PacketReaderFactory packetReaderFactory =
bob.getInstance(PacketReaderFactory.class);
PacketReader packetReader = packetReaderFactory.createPacketReader(
streamReader);
MessagingSession session = new org.briarproject.sync.IncomingSession(db,
new ImmediateExecutor(), new ImmediateExecutor(), eventBus,
messageVerifier, contactId, transportId, packetReader);
// No messages should have been added yet
assertFalse(listener.messageAdded);
// Read whatever needs to be read
session.run();
streamReader.close();
// The private message from Alice should have been added
assertTrue(listener.messageAdded);
// Clean up
keyManager.stop();
db.close();
}
@After
public void tearDown() {
TestUtils.deleteTestDirectory(testDir);
}
private static class MessageListener implements EventListener {
private volatile boolean messageAdded = false;
public void eventOccurred(Event e) {
if (e instanceof MessageAddedEvent) messageAdded = true;
}
}
}

View File

@@ -0,0 +1,149 @@
package org.briarproject.sync;
import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils;
import org.briarproject.api.ContactId;
import org.briarproject.api.TransportId;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.sync.Ack;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.PacketWriter;
import org.briarproject.plugins.ImmediateExecutor;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.Executor;
public class SimplexOutgoingSessionTest extends BriarTestCase {
private static final int MAX_MESSAGES_PER_ACK = 10;
private final Mockery context;
private final DatabaseComponent db;
private final Executor dbExecutor;
private final EventBus eventBus;
private final ContactId contactId;
private final TransportId transportId;
private final MessageId messageId;
private final int maxLatency;
private final PacketWriter packetWriter;
public SimplexOutgoingSessionTest() {
context = new Mockery();
db = context.mock(DatabaseComponent.class);
dbExecutor = new ImmediateExecutor();
eventBus = context.mock(EventBus.class);
packetWriter = context.mock(PacketWriter.class);
contactId = new ContactId(234);
transportId = new TransportId("id");
messageId = new MessageId(TestUtils.getRandomId());
maxLatency = Integer.MAX_VALUE;
}
@Test
public void testNothingToSend() throws Exception {
final org.briarproject.sync.SimplexOutgoingSession
session = new org.briarproject.sync.SimplexOutgoingSession(db,
dbExecutor, eventBus, contactId, transportId, maxLatency,
packetWriter);
context.checking(new Expectations() {{
// Add listener
oneOf(eventBus).addListener(session);
// No transport acks to send
oneOf(db).generateTransportAcks(contactId);
will(returnValue(null));
// No transport updates to send
oneOf(db).generateTransportUpdates(contactId, maxLatency);
will(returnValue(null));
// No subscription ack to send
oneOf(db).generateSubscriptionAck(contactId);
will(returnValue(null));
// No subscription update to send
oneOf(db).generateSubscriptionUpdate(contactId, maxLatency);
will(returnValue(null));
// No retention ack to send
oneOf(db).generateRetentionAck(contactId);
will(returnValue(null));
// No retention update to send
oneOf(db).generateRetentionUpdate(contactId, maxLatency);
will(returnValue(null));
// No acks to send
oneOf(packetWriter).getMaxMessagesForAck(with(any(long.class)));
will(returnValue(MAX_MESSAGES_PER_ACK));
oneOf(db).generateAck(contactId, MAX_MESSAGES_PER_ACK);
will(returnValue(null));
// No messages to send
oneOf(db).generateBatch(with(contactId), with(any(int.class)),
with(maxLatency));
will(returnValue(null));
// Flush the output stream
oneOf(packetWriter).flush();
// Remove listener
oneOf(eventBus).removeListener(session);
}});
session.run();
context.assertIsSatisfied();
}
@Test
public void testSomethingToSend() throws Exception {
final Ack ack = new Ack(Collections.singletonList(messageId));
final byte[] raw = new byte[1234];
final org.briarproject.sync.SimplexOutgoingSession
session = new org.briarproject.sync.SimplexOutgoingSession(db,
dbExecutor, eventBus, contactId, transportId, maxLatency,
packetWriter);
context.checking(new Expectations() {{
// Add listener
oneOf(eventBus).addListener(session);
// No transport acks to send
oneOf(db).generateTransportAcks(contactId);
will(returnValue(null));
// No transport updates to send
oneOf(db).generateTransportUpdates(contactId, maxLatency);
will(returnValue(null));
// No subscription ack to send
oneOf(db).generateSubscriptionAck(contactId);
will(returnValue(null));
// No subscription update to send
oneOf(db).generateSubscriptionUpdate(contactId, maxLatency);
will(returnValue(null));
// No retention ack to send
oneOf(db).generateRetentionAck(contactId);
will(returnValue(null));
// No retention update to send
oneOf(db).generateRetentionUpdate(contactId, maxLatency);
will(returnValue(null));
// One ack to send
oneOf(packetWriter).getMaxMessagesForAck(with(any(long.class)));
will(returnValue(MAX_MESSAGES_PER_ACK));
oneOf(db).generateAck(contactId, MAX_MESSAGES_PER_ACK);
will(returnValue(ack));
oneOf(packetWriter).writeAck(ack);
// No more acks
oneOf(packetWriter).getMaxMessagesForAck(with(any(long.class)));
will(returnValue(MAX_MESSAGES_PER_ACK));
oneOf(db).generateAck(contactId, MAX_MESSAGES_PER_ACK);
will(returnValue(null));
// One message to send
oneOf(db).generateBatch(with(contactId), with(any(int.class)),
with(maxLatency));
will(returnValue(Arrays.asList(raw)));
oneOf(packetWriter).writeMessage(raw);
// No more messages
oneOf(db).generateBatch(with(contactId), with(any(int.class)),
with(maxLatency));
will(returnValue(null));
// Flush the output stream
oneOf(packetWriter).flush();
// Remove listener
oneOf(eventBus).removeListener(session);
}});
session.run();
context.assertIsSatisfied();
}
}