Replace old introduction client with new one

This commit is contained in:
Torsten Grote
2018-04-23 17:11:10 -03:00
parent 1bc29fec06
commit f81ef30b47
80 changed files with 1449 additions and 5924 deletions

View File

@@ -1,424 +0,0 @@
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.introduction.IntroduceeProtocolState;
import org.briarproject.briar.test.BriarTestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.briar.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ACCEPT;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ADDED_CONTACT_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ANSWERED;
import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT;
import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT_ID_1;
import static org.briarproject.briar.api.introduction.IntroductionConstants.EXISTS;
import static org.briarproject.briar.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.GROUP_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.INTRODUCER;
import static org.briarproject.briar.api.introduction.IntroductionConstants.LOCAL_AUTHOR_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_LABEL;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_LENGTH;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_TIME;
import static org.briarproject.briar.api.introduction.IntroductionConstants.NAME;
import static org.briarproject.briar.api.introduction.IntroductionConstants.NONCE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.PUBLIC_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.REMOTE_AUTHOR_IS_US;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE_INTRODUCEE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNATURE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNING_LABEL;
import static org.briarproject.briar.api.introduction.IntroductionConstants.STATE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.STORAGE_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TIME;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TRANSPORT;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ACK;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_RESPONSE;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
import static org.hamcrest.Matchers.array;
import static org.hamcrest.Matchers.samePropertyValuesAs;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class IntroduceeManagerTest extends BriarTestCase {
private final Mockery context;
private final IntroduceeManager introduceeManager;
private final DatabaseComponent db;
private final CryptoComponent cryptoComponent;
private final ClientHelper clientHelper;
private final IntroductionGroupFactory introductionGroupFactory;
private final AuthorFactory authorFactory;
private final ContactManager contactManager;
private final Clock clock;
private final Contact introducer;
private final Contact introducee1;
private final Contact introducee2;
private final Group localGroup1;
private final Group introductionGroup1;
private final Transaction txn;
private final long time = 42L;
private final Message localStateMessage;
private final SessionId sessionId;
private final Message message1;
public IntroduceeManagerTest() {
context = new Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
MessageSender messageSender = context.mock(MessageSender.class);
db = context.mock(DatabaseComponent.class);
cryptoComponent = context.mock(CryptoComponent.class);
clientHelper = context.mock(ClientHelper.class);
clock = context.mock(Clock.class);
introductionGroupFactory =
context.mock(IntroductionGroupFactory.class);
TransportPropertyManager transportPropertyManager =
context.mock(TransportPropertyManager.class);
authorFactory = context.mock(AuthorFactory.class);
contactManager = context.mock(ContactManager.class);
IdentityManager identityManager = context.mock(IdentityManager.class);
introduceeManager = new IntroduceeManager(messageSender, db,
clientHelper, clock, cryptoComponent, transportPropertyManager,
authorFactory, contactManager, identityManager,
introductionGroupFactory);
Author author0 = getAuthor();
AuthorId localAuthorId = new AuthorId(getRandomId());
ContactId contactId0 = new ContactId(234);
introducer =
new Contact(contactId0, author0, localAuthorId, true, true);
Author author1 = getAuthor();
AuthorId localAuthorId1 = new AuthorId(getRandomId());
ContactId contactId1 = new ContactId(234);
introducee1 =
new Contact(contactId1, author1, localAuthorId1, true, true);
Author author2 = getAuthor();
ContactId contactId2 = new ContactId(235);
introducee2 =
new Contact(contactId2, author2, localAuthorId, true, true);
localGroup1 = getGroup(CLIENT_ID);
introductionGroup1 = getGroup(CLIENT_ID);
sessionId = new SessionId(getRandomId());
localStateMessage = new Message(
new MessageId(getRandomId()),
localGroup1.getId(),
time,
getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
);
message1 = new Message(
new MessageId(getRandomId()),
introductionGroup1.getId(),
time,
getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
);
txn = new Transaction(null, false);
}
@Test
public void testIncomingRequestMessage()
throws DbException, FormatException {
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_REQUEST);
msg.put(GROUP_ID, introductionGroup1.getId());
msg.put(SESSION_ID, sessionId);
msg.put(MESSAGE_ID, message1.getId());
msg.put(MESSAGE_TIME, time);
msg.put(NAME, introducee2.getAuthor().getName());
msg.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
BdfDictionary state =
initializeSessionState(txn, introductionGroup1.getId(), msg);
context.checking(new Expectations() {{
oneOf(clientHelper).mergeMessageMetadata(txn,
localStateMessage.getId(), state);
}});
introduceeManager.incomingMessage(txn, state, msg);
context.assertIsSatisfied();
assertFalse(txn.isCommitted());
}
@Test
public void testIncomingResponseMessage()
throws DbException, FormatException {
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_RESPONSE);
msg.put(GROUP_ID, introductionGroup1.getId());
msg.put(SESSION_ID, sessionId);
msg.put(MESSAGE_ID, message1.getId());
msg.put(MESSAGE_TIME, time);
msg.put(NAME, introducee2.getAuthor().getName());
msg.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
BdfDictionary state =
initializeSessionState(txn, introductionGroup1.getId(), msg);
state.put(STATE, IntroduceeProtocolState.AWAIT_RESPONSES.ordinal());
// turn request message into a response
msg.put(ACCEPT, true);
msg.put(TIME, time);
msg.put(E_PUBLIC_KEY, getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES));
msg.put(TRANSPORT, new BdfDictionary());
context.checking(new Expectations() {{
oneOf(clientHelper).mergeMessageMetadata(txn,
localStateMessage.getId(), state);
}});
introduceeManager.incomingMessage(txn, state, msg);
context.assertIsSatisfied();
assertFalse(txn.isCommitted());
}
@Test
public void testDetectReplacedEphemeralPublicKey()
throws DbException, FormatException, GeneralSecurityException {
// TODO MR !237 should use its new default initialization method here
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_RESPONSE);
msg.put(GROUP_ID, introductionGroup1.getId());
msg.put(SESSION_ID, sessionId);
msg.put(MESSAGE_ID, message1.getId());
msg.put(MESSAGE_TIME, time);
msg.put(NAME, introducee2.getAuthor().getName());
msg.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
BdfDictionary state =
initializeSessionState(txn, introductionGroup1.getId(), msg);
// prepare state for incoming ACK
state.put(STATE, IntroduceeProtocolState.AWAIT_ACK.ordinal());
state.put(ADDED_CONTACT_ID, 2);
byte[] nonce = getRandomBytes(42);
state.put(NONCE, nonce);
state.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
// create incoming ACK message
byte[] mac = getRandomBytes(MAC_LENGTH);
byte[] sig = getRandomBytes(MAX_SIGNATURE_LENGTH);
BdfDictionary ack = BdfDictionary.of(
new BdfEntry(TYPE, TYPE_ACK),
new BdfEntry(SESSION_ID, sessionId),
new BdfEntry(GROUP_ID, introductionGroup1.getId()),
new BdfEntry(MAC, mac),
new BdfEntry(SIGNATURE, sig)
);
context.checking(new Expectations() {{
oneOf(cryptoComponent).verifySignature(sig, SIGNING_LABEL, nonce,
introducee2.getAuthor().getPublicKey());
will(returnValue(false));
}});
try {
introduceeManager.incomingMessage(txn, state, ack);
fail();
} catch (DbException e) {
// expected
assertTrue(e.getCause() instanceof GeneralSecurityException);
}
context.assertIsSatisfied();
assertFalse(txn.isCommitted());
}
@Test
public void testSignatureVerification()
throws FormatException, DbException, GeneralSecurityException {
byte[] publicKeyBytes = introducee2.getAuthor().getPublicKey();
byte[] nonce = getRandomBytes(MAC_LENGTH);
byte[] sig = getRandomBytes(MAC_LENGTH);
BdfDictionary state = new BdfDictionary();
state.put(PUBLIC_KEY, publicKeyBytes);
state.put(NONCE, nonce);
state.put(SIGNATURE, sig);
context.checking(new Expectations() {{
oneOf(cryptoComponent).verifySignature(sig, SIGNING_LABEL, nonce,
publicKeyBytes);
will(returnValue(true));
}});
introduceeManager.verifySignature(state);
context.assertIsSatisfied();
}
@Test
public void testMacVerification()
throws FormatException, DbException, GeneralSecurityException {
byte[] publicKeyBytes = introducee2.getAuthor().getPublicKey();
BdfDictionary tp = BdfDictionary.of(new BdfEntry("fake", "fake"));
byte[] ePublicKeyBytes = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
byte[] mac = getRandomBytes(MAC_LENGTH);
SecretKey macKey = getSecretKey();
// move state to where it would be after an ACK arrived
BdfDictionary state = new BdfDictionary();
state.put(PUBLIC_KEY, publicKeyBytes);
state.put(TRANSPORT, tp);
state.put(TIME, time);
state.put(E_PUBLIC_KEY, ePublicKeyBytes);
state.put(MAC, mac);
state.put(MAC_KEY, macKey.getBytes());
byte[] signBytes = getRandomBytes(42);
context.checking(new Expectations() {{
oneOf(clientHelper).toByteArray(
BdfList.of(publicKeyBytes, ePublicKeyBytes, tp, time));
will(returnValue(signBytes));
//noinspection unchecked
oneOf(cryptoComponent).mac(with(MAC_LABEL),
with(samePropertyValuesAs(macKey)),
with(array(equal(signBytes))));
will(returnValue(mac));
}});
introduceeManager.verifyMac(state);
context.assertIsSatisfied();
// now produce wrong MAC
context.checking(new Expectations() {{
oneOf(clientHelper).toByteArray(
BdfList.of(publicKeyBytes, ePublicKeyBytes, tp, time));
will(returnValue(signBytes));
//noinspection unchecked
oneOf(cryptoComponent).mac(with(MAC_LABEL),
with(samePropertyValuesAs(macKey)),
with(array(equal(signBytes))));
will(returnValue(getRandomBytes(MAC_LENGTH)));
}});
try {
introduceeManager.verifyMac(state);
fail();
} catch (GeneralSecurityException e) {
// expected
}
context.assertIsSatisfied();
}
private BdfDictionary initializeSessionState(Transaction txn,
GroupId groupId, BdfDictionary msg)
throws DbException, FormatException {
SecureRandom secureRandom = context.mock(SecureRandom.class);
Bytes salt = new Bytes(new byte[64]);
BdfDictionary groupMetadata = BdfDictionary.of(
new BdfEntry(CONTACT, introducee1.getId().getInt())
);
boolean contactExists = false;
BdfDictionary state = new BdfDictionary();
state.put(STORAGE_ID, localStateMessage.getId());
state.put(STATE, AWAIT_REQUEST.getValue());
state.put(ROLE, ROLE_INTRODUCEE);
state.put(GROUP_ID, groupId);
state.put(INTRODUCER, introducer.getAuthor().getName());
state.put(CONTACT_ID_1, introducer.getId().getInt());
state.put(LOCAL_AUTHOR_ID, introducer.getLocalAuthorId().getBytes());
state.put(NOT_OUR_RESPONSE, localStateMessage.getId());
state.put(ANSWERED, false);
state.put(EXISTS, contactExists);
state.put(REMOTE_AUTHOR_ID, introducee2.getAuthor().getId());
state.put(REMOTE_AUTHOR_IS_US, false);
context.checking(new Expectations() {{
oneOf(clock).currentTimeMillis();
will(returnValue(time));
oneOf(cryptoComponent).getSecureRandom();
will(returnValue(secureRandom));
oneOf(secureRandom).nextBytes(salt.getBytes());
oneOf(introductionGroupFactory).createLocalGroup();
will(returnValue(localGroup1));
oneOf(clientHelper)
.createMessage(localGroup1.getId(), time, BdfList.of(salt));
will(returnValue(localStateMessage));
// who is making the introduction? who is the introducer?
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
groupId);
will(returnValue(groupMetadata));
oneOf(db).getContact(txn, introducer.getId());
will(returnValue(introducer));
// create remote author to check if contact exists
oneOf(authorFactory).createAuthor(introducee2.getAuthor().getName(),
introducee2.getAuthor().getPublicKey());
will(returnValue(introducee2.getAuthor()));
oneOf(contactManager)
.contactExists(txn, introducee2.getAuthor().getId(),
introducer.getLocalAuthorId());
will(returnValue(contactExists));
// checks if remote author is one of our identities
oneOf(db).containsLocalAuthor(txn, introducee2.getAuthor().getId());
will(returnValue(false));
// store session state
oneOf(clientHelper)
.addLocalMessage(txn, localStateMessage, state, false);
}});
BdfDictionary result = introduceeManager.initialize(txn, groupId, msg);
context.assertIsSatisfied();
assertFalse(txn.isCommitted());
return result;
}
}

View File

@@ -1,179 +0,0 @@
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.test.BriarTestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test;
import java.security.SecureRandom;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.briar.api.introduction.IntroducerProtocolState.AWAIT_RESPONSES;
import static org.briarproject.briar.api.introduction.IntroducerProtocolState.PREPARE_REQUESTS;
import static org.briarproject.briar.api.introduction.IntroductionConstants.AUTHOR_ID_1;
import static org.briarproject.briar.api.introduction.IntroductionConstants.AUTHOR_ID_2;
import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT_1;
import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT_2;
import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT_ID_1;
import static org.briarproject.briar.api.introduction.IntroductionConstants.CONTACT_ID_2;
import static org.briarproject.briar.api.introduction.IntroductionConstants.GROUP_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.GROUP_ID_1;
import static org.briarproject.briar.api.introduction.IntroductionConstants.GROUP_ID_2;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MESSAGE_TIME;
import static org.briarproject.briar.api.introduction.IntroductionConstants.NAME;
import static org.briarproject.briar.api.introduction.IntroductionConstants.PUBLIC_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.STATE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.STORAGE_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
import static org.junit.Assert.assertFalse;
public class IntroducerManagerTest extends BriarTestCase {
private final Mockery context;
private final IntroducerManager introducerManager;
private final CryptoComponent cryptoComponent;
private final ClientHelper clientHelper;
private final IntroductionGroupFactory introductionGroupFactory;
private final MessageSender messageSender;
private final Clock clock;
private final Contact introducee1;
private final Contact introducee2;
private final Group localGroup0;
private final Group introductionGroup1;
private final Group introductionGroup2;
public IntroducerManagerTest() {
context = new Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
messageSender = context.mock(MessageSender.class);
cryptoComponent = context.mock(CryptoComponent.class);
clientHelper = context.mock(ClientHelper.class);
clock = context.mock(Clock.class);
introductionGroupFactory =
context.mock(IntroductionGroupFactory.class);
introducerManager =
new IntroducerManager(messageSender, clientHelper, clock,
cryptoComponent, introductionGroupFactory);
Author author1 = getAuthor();
AuthorId localAuthorId1 = new AuthorId(getRandomId());
ContactId contactId1 = new ContactId(234);
introducee1 =
new Contact(contactId1, author1, localAuthorId1, true, true);
Author author2 = getAuthor();
AuthorId localAuthorId2 = new AuthorId(getRandomId());
ContactId contactId2 = new ContactId(235);
introducee2 =
new Contact(contactId2, author2, localAuthorId2, true, true);
localGroup0 = getGroup(CLIENT_ID);
introductionGroup1 = getGroup(CLIENT_ID);
introductionGroup2 = getGroup(CLIENT_ID);
context.assertIsSatisfied();
}
@Test
public void testMakeIntroduction() throws DbException, FormatException {
Transaction txn = new Transaction(null, false);
long time = 42L;
context.setImposteriser(ClassImposteriser.INSTANCE);
SecureRandom secureRandom = context.mock(SecureRandom.class);
Bytes salt = new Bytes(new byte[64]);
Message msg = new Message(new MessageId(getRandomId()),
localGroup0.getId(), time, getRandomBytes(64));
BdfDictionary state = new BdfDictionary();
state.put(SESSION_ID, msg.getId());
state.put(STORAGE_ID, msg.getId());
state.put(STATE, PREPARE_REQUESTS.getValue());
state.put(ROLE, ROLE_INTRODUCER);
state.put(GROUP_ID_1, introductionGroup1.getId());
state.put(GROUP_ID_2, introductionGroup2.getId());
state.put(CONTACT_1, introducee1.getAuthor().getName());
state.put(CONTACT_2, introducee2.getAuthor().getName());
state.put(CONTACT_ID_1, introducee1.getId().getInt());
state.put(CONTACT_ID_2, introducee2.getId().getInt());
state.put(AUTHOR_ID_1, introducee1.getAuthor().getId());
state.put(AUTHOR_ID_2, introducee2.getAuthor().getId());
BdfDictionary state2 = (BdfDictionary) state.clone();
state2.put(STATE, AWAIT_RESPONSES.getValue());
BdfDictionary msg1 = new BdfDictionary();
msg1.put(TYPE, TYPE_REQUEST);
msg1.put(SESSION_ID, state.getRaw(SESSION_ID));
msg1.put(GROUP_ID, state.getRaw(GROUP_ID_1));
msg1.put(NAME, state.getString(CONTACT_2));
msg1.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
BdfDictionary msg1send = (BdfDictionary) msg1.clone();
msg1send.put(MESSAGE_TIME, time);
BdfDictionary msg2 = new BdfDictionary();
msg2.put(TYPE, TYPE_REQUEST);
msg2.put(SESSION_ID, state.getRaw(SESSION_ID));
msg2.put(GROUP_ID, state.getRaw(GROUP_ID_2));
msg2.put(NAME, state.getString(CONTACT_1));
msg2.put(PUBLIC_KEY, introducee1.getAuthor().getPublicKey());
BdfDictionary msg2send = (BdfDictionary) msg2.clone();
msg2send.put(MESSAGE_TIME, time);
context.checking(new Expectations() {{
// initialize and store session state
oneOf(clock).currentTimeMillis();
will(returnValue(time));
oneOf(cryptoComponent).getSecureRandom();
will(returnValue(secureRandom));
oneOf(secureRandom).nextBytes(salt.getBytes());
oneOf(introductionGroupFactory).createLocalGroup();
will(returnValue(localGroup0));
oneOf(clientHelper).createMessage(localGroup0.getId(), time,
BdfList.of(salt));
will(returnValue(msg));
oneOf(introductionGroupFactory)
.createIntroductionGroup(introducee1);
will(returnValue(introductionGroup1));
oneOf(introductionGroupFactory)
.createIntroductionGroup(introducee2);
will(returnValue(introductionGroup2));
oneOf(clientHelper).addLocalMessage(txn, msg, state, false);
// send message
oneOf(clientHelper).mergeMessageMetadata(txn, msg.getId(), state2);
oneOf(messageSender).sendMessage(txn, msg1send);
oneOf(messageSender).sendMessage(txn, msg2send);
}});
introducerManager
.makeIntroduction(txn, introducee1, introducee2, null, time);
context.assertIsSatisfied();
assertFalse(txn.isCommitted());
}
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.introduction2;
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.crypto.CryptoComponent;

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.introduction2;
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.client.ClientHelper;
@@ -11,7 +11,7 @@ import org.junit.Test;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.briar.api.introduction2.IntroductionConstants.LABEL_SESSION_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_SESSION_ID;
import static org.junit.Assert.assertEquals;
public class IntroductionCryptoTest extends BrambleMockTestCase {

View File

@@ -6,23 +6,21 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.test.TestDatabaseModule;
import org.briarproject.briar.api.client.SessionId;
@@ -38,56 +36,35 @@ import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import javax.inject.Inject;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.test.TestPluginConfigModule.TRANSPORT_ID;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.client.MessageQueueManager.QUEUE_STATE_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ALICE_MAC_KEY_LABEL;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ALICE_NONCE_LABEL;
import static org.briarproject.briar.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.GROUP_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_LABEL;
import static org.briarproject.briar.api.introduction.IntroductionConstants.NAME;
import static org.briarproject.briar.api.introduction.IntroductionConstants.NONCE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.PUBLIC_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SHARED_SECRET_LABEL;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNATURE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNING_LABEL;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TIME;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TRANSPORT;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_RESPONSE;
import static org.briarproject.bramble.test.TestUtils.getTransportProperties;
import static org.briarproject.bramble.test.TestUtils.getTransportPropertiesMap;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_VERSION;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_AUTHOR;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_1;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCEE_2;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_SESSION_ID;
import static org.briarproject.briar.introduction.MessageType.ACCEPT;
import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class IntroductionIntegrationTest
extends BriarIntegrationTest<IntroductionIntegrationTestComponent> {
@Inject
IntroductionGroupFactory introductionGroupFactory;
// objects accessed from background threads need to be volatile
private volatile IntroductionManager introductionManager0;
private volatile IntroductionManager introductionManager1;
@@ -102,7 +79,7 @@ public class IntroductionIntegrationTest
Logger.getLogger(IntroductionIntegrationTest.class.getName());
interface StateVisitor {
boolean visit(BdfDictionary response);
AcceptMessage visit(AcceptMessage response);
}
@Before
@@ -151,50 +128,50 @@ public class IntroductionIntegrationTest
.makeIntroduction(introducee1, introducee2, "Hi!", time);
// check that messages are tracked properly
Group g1 = introductionGroupFactory
.createIntroductionGroup(introducee1);
Group g2 = introductionGroupFactory
.createIntroductionGroup(introducee2);
assertGroupCount(messageTracker0, g1.getId(), 1, 0, time);
assertGroupCount(messageTracker0, g2.getId(), 1, 0, time);
Group g1 = introductionManager0.getContactGroup(introducee1);
Group g2 = introductionManager0.getContactGroup(introducee2);
assertGroupCount(messageTracker0, g1.getId(), 1, 0);
assertGroupCount(messageTracker0, g2.getId(), 1, 0);
// sync first request message
// sync first REQUEST message
sync0To1(1, true);
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
assertGroupCount(messageTracker1, g1.getId(), 2, 1);
// sync second request message
// sync second REQUEST message
sync0To2(1, true);
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener2.requestReceived);
assertGroupCount(messageTracker2, g2.getId(), 2, 1);
// sync first response
// sync first ACCEPT message
sync1To0(1, true);
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.response1Received);
assertGroupCount(messageTracker0, g1.getId(), 2, 1);
// sync second response
// sync second ACCEPT message
sync2To0(1, true);
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.response2Received);
assertGroupCount(messageTracker0, g2.getId(), 2, 1);
// sync forwarded responses to introducees
// sync forwarded ACCEPT messages to introducees
sync0To1(1, true);
sync0To2(1, true);
assertGroupCount(messageTracker1, g1.getId(), 2, 1);
assertGroupCount(messageTracker2, g2.getId(), 2, 1);
// sync first ACK and its forward
// sync first AUTH and its forward
sync1To0(1, true);
sync0To2(1, true);
// sync second ACK and its forward
sync2To0(1, true);
sync0To1(1, true);
// sync second AUTH and its forward as well as the following ACTIVATE
sync2To0(2, true);
sync0To1(2, true);
// sync first ACTIVATE and its forward
sync1To0(1, true);
sync0To2(1, true);
// wait for introduction to succeed
eventWaiter.await(TIMEOUT, 2);
@@ -269,10 +246,8 @@ public class IntroductionIntegrationTest
assertFalse(contactManager2
.contactExists(author1.getId(), author2.getId()));
Group g1 = introductionGroupFactory
.createIntroductionGroup(introducee1);
Group g2 = introductionGroupFactory
.createIntroductionGroup(introducee2);
Group g1 = introductionManager0.getContactGroup(introducee1);
Group g2 = introductionManager0.getContactGroup(introducee2);
assertEquals(2,
introductionManager0.getIntroductionMessages(contactId1From0)
.size());
@@ -290,6 +265,10 @@ public class IntroductionIntegrationTest
introductionManager2.getIntroductionMessages(contactId0From2)
.size());
assertGroupCount(messageTracker2, g2.getId(), 3, 2);
assertFalse(listener0.aborted);
assertFalse(listener1.aborted);
assertFalse(listener2.aborted);
}
@Test
@@ -342,6 +321,9 @@ public class IntroductionIntegrationTest
assertEquals(2,
introductionManager2.getIntroductionMessages(contactId0From2)
.size());
assertFalse(listener0.aborted);
assertFalse(listener1.aborted);
assertFalse(listener2.aborted);
}
@Test
@@ -393,6 +375,9 @@ public class IntroductionIntegrationTest
// since introducee2 was already in FINISHED state when
// introducee1's response arrived, she ignores and deletes it
assertDefaultUiMessages();
assertFalse(listener0.aborted);
assertFalse(listener1.aborted);
assertFalse(listener2.aborted);
}
@Test
@@ -432,6 +417,8 @@ public class IntroductionIntegrationTest
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.response2Received);
assertFalse(listener0.aborted);
assertFalse(listener1.aborted);
assertFalse(listener2.aborted);
}
@Test
@@ -452,61 +439,11 @@ public class IntroductionIntegrationTest
// make really sure we don't have that request
assertTrue(introductionManager1.getIntroductionMessages(contactId0From1)
.isEmpty());
}
@Test
public void testSessionIdReuse() throws Exception {
addListeners(true, true);
// make introduction
long time = clock.currentTimeMillis();
introductionManager0
.makeIntroduction(contact1From0, contact2From0, "Hi!", time);
// sync first request message
sync0To1(1, true);
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// get SessionId
List<IntroductionMessage> list = new ArrayList<>(
introductionManager1.getIntroductionMessages(contactId0From1));
assertEquals(2, list.size());
assertTrue(list.get(0) instanceof IntroductionRequest);
IntroductionRequest msg = (IntroductionRequest) list.get(0);
SessionId sessionId = msg.getSessionId();
// get contact group
Group group =
introductionGroupFactory.createIntroductionGroup(contact1From0);
// create new message with same SessionId
BdfDictionary d = BdfDictionary.of(
new BdfEntry(TYPE, TYPE_REQUEST),
new BdfEntry(SESSION_ID, sessionId),
new BdfEntry(GROUP_ID, group.getId()),
new BdfEntry(NAME, getRandomString(42)),
new BdfEntry(PUBLIC_KEY, getRandomBytes(MAX_PUBLIC_KEY_LENGTH))
);
// reset request received state
listener1.requestReceived = false;
// add the message to the queue
MessageSender sender0 = c0.getMessageSender();
Transaction txn = db0.startTransaction(false);
try {
sender0.sendMessage(txn, d);
db0.commitTransaction(txn);
} finally {
db0.endTransaction(txn);
}
// actually send message
sync0To1(1, false);
// make sure it does not arrive
assertFalse(listener1.requestReceived);
// The message was invalid, so no abort message was sent
assertFalse(listener0.aborted);
assertFalse(listener1.aborted);
assertFalse(listener2.aborted);
}
@Test
@@ -523,34 +460,20 @@ public class IntroductionIntegrationTest
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// get database and local group for introducee
Group group1 = introductionGroupFactory.createLocalGroup();
// get local group for introducee1
Group group1 =
contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION);
// get local session state messages
Map<MessageId, Metadata> map;
Transaction txn = db1.startTransaction(false);
try {
map = db1.getMessageMetadata(txn, group1.getId());
db1.commitTransaction(txn);
} finally {
db1.endTransaction(txn);
}
// check that we have one session state
assertEquals(1, map.size());
assertEquals(1, c1.getClientHelper()
.getMessageMetadataAsDictionary(group1.getId()).size());
// introducee1 removes introducer
contactManager1.removeContact(contactId0From1);
// get local session state messages again
txn = db1.startTransaction(false);
try {
map = db1.getMessageMetadata(txn, group1.getId());
db1.commitTransaction(txn);
} finally {
db1.endTransaction(txn);
}
// make sure local state got deleted
assertEquals(0, map.size());
assertEquals(0, c1.getClientHelper()
.getMessageMetadataAsDictionary(group1.getId()).size());
}
@Test
@@ -567,48 +490,36 @@ public class IntroductionIntegrationTest
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// get database and local group for introducee
Group group1 = introductionGroupFactory.createLocalGroup();
// get local group for introducer
Group group0 =
contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION);
// get local session state messages
Map<MessageId, Metadata> map;
Transaction txn = db0.startTransaction(false);
try {
map = db0.getMessageMetadata(txn, group1.getId());
db0.commitTransaction(txn);
} finally {
db0.endTransaction(txn);
}
// check that we have one session state
assertEquals(1, map.size());
assertEquals(1, c0.getClientHelper()
.getMessageMetadataAsDictionary(group0.getId()).size());
// introducer removes introducee1
contactManager0.removeContact(contactId1From0);
// get local session state messages again
txn = db0.startTransaction(false);
try {
map = db0.getMessageMetadata(txn, group1.getId());
db0.commitTransaction(txn);
} finally {
db0.endTransaction(txn);
}
// make sure local state is still there
assertEquals(1, map.size());
assertEquals(1, c0.getClientHelper()
.getMessageMetadataAsDictionary(group0.getId()).size());
// ensure introducer has aborted the session
assertTrue(listener0.aborted);
// sync REQUEST and ABORT message
sync0To2(2, true);
// ensure introducee2 has aborted the session as well
assertTrue(listener2.aborted);
// introducer removes other introducee
contactManager0.removeContact(contactId2From0);
// get local session state messages again
txn = db0.startTransaction(false);
try {
map = db0.getMessageMetadata(txn, group1.getId());
db0.commitTransaction(txn);
} finally {
db0.endTransaction(txn);
}
// make sure local state is gone now
assertEquals(0, map.size());
assertEquals(0, c0.getClientHelper()
.getMessageMetadataAsDictionary(group0.getId()).size());
}
private void testModifiedResponse(StateVisitor visitor)
@@ -630,26 +541,36 @@ public class IntroductionIntegrationTest
eventWaiter.await(TIMEOUT, 1);
// get response to be forwarded
ClientHelper ch = c0.getClientHelper(); // need 0's ClientHelper here
Entry<MessageId, BdfDictionary> resp =
getMessageFor(ch, contact2From0, TYPE_RESPONSE);
MessageId responseId = resp.getKey();
BdfDictionary response = resp.getValue();
// adapt outgoing message queue to removed message
Group g2 = introductionGroupFactory
.createIntroductionGroup(contact2From0);
decreaseOutgoingMessageCounter(ch, g2.getId());
AcceptMessage message =
(AcceptMessage) getMessageFor(c0.getClientHelper(),
contact2From0, ACCEPT);
// allow visitor to modify response
boolean earlyAbort = visitor.visit(response);
AcceptMessage m = visitor.visit(message);
// replace original response with modified one
MessageSender sender0 = c0.getMessageSender();
Transaction txn = db0.startTransaction(false);
try {
db0.deleteMessage(txn, responseId);
sender0.sendMessage(txn, response);
db0.removeMessage(txn, message.getMessageId());
Message msg = c0.getMessageEncoder()
.encodeAcceptMessage(m.getGroupId(), m.getTimestamp(),
m.getPreviousMessageId(), m.getSessionId(),
m.getEphemeralPublicKey(), m.getAcceptTimestamp(),
m.getTransportProperties());
c0.getClientHelper()
.addLocalMessage(txn, msg, new BdfDictionary(), true);
Group group0 = contactGroupFactory
.createLocalGroup(CLIENT_ID, CLIENT_VERSION);
BdfDictionary query = BdfDictionary.of(
new BdfEntry(SESSION_KEY_SESSION_ID, m.getSessionId())
);
Map.Entry<MessageId, BdfDictionary> session = c0.getClientHelper()
.getMessageMetadataAsDictionary(txn, group0.getId(), query)
.entrySet().iterator().next();
replacePreviousLocalMessageId(contact2From0.getAuthor(),
session.getValue(), msg.getId());
c0.getClientHelper().mergeMessageMetadata(txn, session.getKey(),
session.getValue());
db0.commitTransaction(txn);
} finally {
db0.endTransaction(txn);
@@ -663,21 +584,14 @@ public class IntroductionIntegrationTest
sync0To1(1, true);
sync0To2(1, true);
// sync first ACK and forward it
// sync first AUTH and forward it
sync1To0(1, true);
sync0To2(1, true);
// introducee2 should have detected the fake now
// and deleted introducee1 again
Collection<Contact> contacts2;
txn = db2.startTransaction(true);
try {
contacts2 = db2.getContacts(txn);
db2.commitTransaction(txn);
} finally {
db2.endTransaction(txn);
}
assertEquals(1, contacts2.size());
assertFalse(listener0.aborted);
assertFalse(listener1.aborted);
assertTrue(listener2.aborted);
// sync introducee2's ack and following abort
sync2To0(2, true);
@@ -687,144 +601,44 @@ public class IntroductionIntegrationTest
// sync abort messages to introducees
sync0To1(2, true);
sync0To2(1, true);
if (earlyAbort) {
assertTrue(listener1.aborted);
assertTrue(listener2.aborted);
} else {
assertTrue(listener2.aborted);
// when aborted late, introducee1 keeps the contact,
// so introducer can not make contacts disappear by aborting
Collection<Contact> contacts1;
txn = db1.startTransaction(true);
try {
contacts1 = db1.getContacts(txn);
db1.commitTransaction(txn);
} finally {
db1.endTransaction(txn);
}
assertEquals(2, contacts1.size());
}
// ensure everybody got the abort now
assertTrue(listener0.aborted);
assertTrue(listener1.aborted);
assertTrue(listener2.aborted);
}
@Test
public void testModifiedTransportProperties() throws Exception {
testModifiedResponse(response -> {
BdfDictionary tp = response.getDictionary(TRANSPORT, null);
tp.put("fakeId", BdfDictionary.of(new BdfEntry("fake", "fake")));
response.put(TRANSPORT, tp);
return false;
});
testModifiedResponse(
m -> new AcceptMessage(m.getMessageId(), m.getGroupId(),
m.getTimestamp(), m.getPreviousMessageId(),
m.getSessionId(), m.getEphemeralPublicKey(),
m.getAcceptTimestamp(),
getTransportPropertiesMap(2))
);
}
@Test
public void testModifiedTimestamp() throws Exception {
testModifiedResponse(response -> {
long timestamp = response.getLong(TIME, 0L);
response.put(TIME, timestamp + 1);
return false;
});
testModifiedResponse(
m -> new AcceptMessage(m.getMessageId(), m.getGroupId(),
m.getTimestamp(), m.getPreviousMessageId(),
m.getSessionId(), m.getEphemeralPublicKey(),
clock.currentTimeMillis(),
m.getTransportProperties())
);
}
@Test
public void testModifiedEphemeralPublicKey() throws Exception {
testModifiedResponse(response -> {
KeyPair keyPair = crypto.generateAgreementKeyPair();
response.put(E_PUBLIC_KEY, keyPair.getPublic().getEncoded());
return true;
});
}
@Test
public void testModifiedEphemeralPublicKeyWithFakeMac()
throws Exception {
// initialize a real introducee manager
MessageSender messageSender = c2.getMessageSender();
TransportPropertyManager tpManager = c2.getTransportPropertyManager();
IntroduceeManager manager2 =
new IntroduceeManager(messageSender, db2, clientHelper, clock,
crypto, tpManager, authorFactory, contactManager2,
identityManager2, introductionGroupFactory);
// create keys
KeyPair keyPair1 = crypto.generateSignatureKeyPair();
KeyPair eKeyPair1 = crypto.generateAgreementKeyPair();
KeyPair eKeyPair2 = crypto.generateAgreementKeyPair();
// Nonce 1
byte[][] inputs = {
new byte[] {CLIENT_VERSION},
eKeyPair1.getPublic().getEncoded(),
eKeyPair2.getPublic().getEncoded()
};
SecretKey sharedSecret = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
eKeyPair2.getPublic(), eKeyPair1, inputs);
byte[] nonce1 = crypto.mac(ALICE_NONCE_LABEL, sharedSecret);
// Signature 1
byte[] sig1 = crypto.sign(SIGNING_LABEL, nonce1,
keyPair1.getPrivate().getEncoded());
// MAC 1
SecretKey macKey1 = crypto.deriveKey(ALICE_MAC_KEY_LABEL, sharedSecret);
BdfDictionary tp1 = BdfDictionary.of(new BdfEntry("fake", "fake"));
long time1 = clock.currentTimeMillis();
BdfList toMacList = BdfList.of(keyPair1.getPublic().getEncoded(),
eKeyPair1.getPublic().getEncoded(), tp1, time1);
byte[] toMac = clientHelper.toByteArray(toMacList);
byte[] mac1 = crypto.mac(MAC_LABEL, macKey1, toMac);
// create only relevant part of state for introducee2
BdfDictionary state = new BdfDictionary();
state.put(PUBLIC_KEY, keyPair1.getPublic().getEncoded());
state.put(TRANSPORT, tp1);
state.put(TIME, time1);
state.put(E_PUBLIC_KEY, eKeyPair1.getPublic().getEncoded());
state.put(MAC, mac1);
state.put(MAC_KEY, macKey1.getBytes());
state.put(NONCE, nonce1);
state.put(SIGNATURE, sig1);
// MAC and signature verification should pass
manager2.verifyMac(state);
manager2.verifySignature(state);
// replace ephemeral key pair and recalculate matching keys and nonce
KeyPair eKeyPair1f = crypto.generateAgreementKeyPair();
byte[][] fakeInputs = {
new byte[] {CLIENT_VERSION},
eKeyPair1f.getPublic().getEncoded(),
eKeyPair2.getPublic().getEncoded()
};
sharedSecret = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
eKeyPair2.getPublic(), eKeyPair1f, fakeInputs);
nonce1 = crypto.mac(ALICE_NONCE_LABEL, sharedSecret);
// recalculate MAC
macKey1 = crypto.deriveKey(ALICE_MAC_KEY_LABEL, sharedSecret);
toMacList = BdfList.of(keyPair1.getPublic().getEncoded(),
eKeyPair1f.getPublic().getEncoded(), tp1, time1);
toMac = clientHelper.toByteArray(toMacList);
mac1 = crypto.mac(MAC_LABEL, macKey1, toMac);
// update state with faked information
state.put(E_PUBLIC_KEY, eKeyPair1f.getPublic().getEncoded());
state.put(MAC, mac1);
state.put(MAC_KEY, macKey1.getBytes());
state.put(NONCE, nonce1);
// MAC verification should still pass
manager2.verifyMac(state);
// Signature can not be verified, because we don't have private
// long-term key to fake it
try {
manager2.verifySignature(state);
fail();
} catch (GeneralSecurityException e) {
// expected
}
testModifiedResponse(
m -> new AcceptMessage(m.getMessageId(), m.getGroupId(),
m.getTimestamp(), m.getPreviousMessageId(),
m.getSessionId(),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
m.getAcceptTimestamp(), m.getTransportProperties())
);
}
private void addTransportProperties()
@@ -832,17 +646,15 @@ public class IntroductionIntegrationTest
TransportPropertyManager tpm0 = c0.getTransportPropertyManager();
TransportPropertyManager tpm1 = c1.getTransportPropertyManager();
TransportPropertyManager tpm2 = c2.getTransportPropertyManager();
TransportProperties tp = new TransportProperties(
Collections.singletonMap("key", "value"));
tpm0.mergeLocalProperties(TRANSPORT_ID, tp);
tpm0.mergeLocalProperties(TRANSPORT_ID, getTransportProperties(2));
sync0To1(1, true);
sync0To2(1, true);
tpm1.mergeLocalProperties(TRANSPORT_ID, tp);
tpm1.mergeLocalProperties(TRANSPORT_ID, getTransportProperties(2));
sync1To0(1, true);
tpm2.mergeLocalProperties(TRANSPORT_ID, tp);
tpm2.mergeLocalProperties(TRANSPORT_ID, getTransportProperties(2));
sync2To0(1, true);
}
@@ -935,7 +747,7 @@ public class IntroductionIntegrationTest
time);
}
}
} catch (DbException | FormatException exception) {
} catch (DbException exception) {
eventWaiter.rethrow(exception);
} finally {
eventWaiter.resume();
@@ -945,7 +757,6 @@ public class IntroductionIntegrationTest
Contact contact = ((IntroductionSucceededEvent) e).getContact();
eventWaiter
.assertFalse(contact.getId().equals(contactId0From1));
eventWaiter.assertTrue(contact.isActive());
eventWaiter.resume();
} else if (e instanceof IntroductionAbortedEvent) {
aborted = true;
@@ -981,30 +792,41 @@ public class IntroductionIntegrationTest
}
private void decreaseOutgoingMessageCounter(ClientHelper ch, GroupId g)
throws FormatException, DbException {
BdfDictionary gD = ch.getGroupMetadataAsDictionary(g);
LOG.warning(gD.toString());
BdfDictionary queue = gD.getDictionary(QUEUE_STATE_KEY);
queue.put("nextOut", queue.getLong("nextOut") - 1);
gD.put(QUEUE_STATE_KEY, queue);
ch.mergeGroupMetadata(g, gD);
private void replacePreviousLocalMessageId(Author author,
BdfDictionary d, MessageId id) throws FormatException {
BdfDictionary i1 = d.getDictionary(SESSION_KEY_INTRODUCEE_1);
BdfDictionary i2 = d.getDictionary(SESSION_KEY_INTRODUCEE_2);
Author a1 = clientHelper
.parseAndValidateAuthor(i1.getList(SESSION_KEY_AUTHOR));
Author a2 = clientHelper
.parseAndValidateAuthor(i2.getList(SESSION_KEY_AUTHOR));
if (a1.equals(author)) {
i1.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, id);
d.put(SESSION_KEY_INTRODUCEE_1, i1);
} else if (a2.equals(author)) {
i2.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, id);
d.put(SESSION_KEY_INTRODUCEE_2, i2);
} else {
throw new AssertionError();
}
}
private Entry<MessageId, BdfDictionary> getMessageFor(ClientHelper ch,
Contact contact, long type) throws FormatException, DbException {
Entry<MessageId, BdfDictionary> response = null;
Group g = introductionGroupFactory
.createIntroductionGroup(contact);
private AbstractIntroductionMessage getMessageFor(ClientHelper ch,
Contact contact, MessageType type)
throws FormatException, DbException {
Group g = introductionManager0.getContactGroup(contact);
BdfDictionary query = BdfDictionary.of(
new BdfEntry(MSG_KEY_MESSAGE_TYPE, type.getValue())
);
Map<MessageId, BdfDictionary> map =
ch.getMessageMetadataAsDictionary(g.getId());
for (Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
if (entry.getValue().getLong(TYPE) == type) {
response = entry;
}
}
assertTrue(response != null);
return response;
ch.getMessageMetadataAsDictionary(g.getId(), query);
assertEquals(1, map.size());
MessageId id = map.entrySet().iterator().next().getKey();
Message m = ch.getMessage(id);
BdfList body = ch.getMessageAsList(id);
//noinspection ConstantConditions
return c0.getMessageParser().parseAcceptMessage(m, body);
}
}

View File

@@ -59,6 +59,7 @@ interface IntroductionIntegrationTestComponent
void inject(IntroductionIntegrationTest init);
MessageSender getMessageSender();
MessageEncoder getMessageEncoder();
MessageParser getMessageParser();
}

View File

@@ -1,291 +0,0 @@
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.test.BriarTestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.briar.api.introduction.IntroductionConstants.GROUP_ID_1;
import static org.briarproject.briar.api.introduction.IntroductionConstants.GROUP_ID_2;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_RESPONSE;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
import static org.junit.Assert.assertFalse;
public class IntroductionManagerImplTest extends BriarTestCase {
private final Mockery context;
private final IntroductionManagerImpl introductionManager;
private final IntroducerManager introducerManager;
private final IntroduceeManager introduceeManager;
private final DatabaseComponent db;
private final ClientHelper clientHelper;
private final MessageTracker messageTracker;
private final IntroductionGroupFactory introductionGroupFactory;
private final SessionId sessionId = new SessionId(getRandomId());
private final MessageId storageId = new MessageId(sessionId.getBytes());
private final long time = 42L;
private final Contact introducee1;
private final Contact introducee2;
private final Group introductionGroup1;
private final Group introductionGroup2;
private final Message message1;
private Transaction txn;
public IntroductionManagerImplTest() {
Author author1 = getAuthor();
AuthorId localAuthorId1 = new AuthorId(getRandomId());
ContactId contactId1 = new ContactId(234);
introducee1 =
new Contact(contactId1, author1, localAuthorId1, true, true);
Author author2 = getAuthor();
AuthorId localAuthorId2 = new AuthorId(getRandomId());
ContactId contactId2 = new ContactId(235);
introducee2 =
new Contact(contactId2, author2, localAuthorId2, true, true);
introductionGroup1 = getGroup(CLIENT_ID);
introductionGroup2 = getGroup(CLIENT_ID);
message1 = new Message(
new MessageId(getRandomId()),
introductionGroup1.getId(),
time,
getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
);
// mock ALL THE THINGS!!!
context = new Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
introducerManager = context.mock(IntroducerManager.class);
introduceeManager = context.mock(IntroduceeManager.class);
db = context.mock(DatabaseComponent.class);
clientHelper = context.mock(ClientHelper.class);
MetadataParser metadataParser = context.mock(MetadataParser.class);
messageTracker = context.mock(MessageTracker.class);
introductionGroupFactory = context.mock(IntroductionGroupFactory.class);
introductionManager = new IntroductionManagerImpl(db, clientHelper,
metadataParser, messageTracker, introducerManager,
introduceeManager, introductionGroupFactory);
}
@Test
public void testMakeIntroduction() throws DbException, FormatException {
txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
oneOf(introducerManager)
.makeIntroduction(txn, introducee1, introducee2, null,
time);
// get both introduction groups
oneOf(introductionGroupFactory)
.createIntroductionGroup(introducee1);
will(returnValue(introductionGroup1));
oneOf(introductionGroupFactory)
.createIntroductionGroup(introducee2);
will(returnValue(introductionGroup2));
// track message for group 1
oneOf(messageTracker).trackMessage(txn,
introductionGroup1.getId(), time, true);
// track message for group 2
oneOf(messageTracker).trackMessage(txn,
introductionGroup2.getId(), time, true);
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
introductionManager
.makeIntroduction(introducee1, introducee2, null, time);
context.assertIsSatisfied();
}
@Test
public void testAcceptIntroduction() throws DbException, FormatException {
BdfDictionary state = BdfDictionary.of(
new BdfEntry(GROUP_ID_1, introductionGroup1.getId()),
new BdfEntry(GROUP_ID_2, introductionGroup2.getId())
);
txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
oneOf(db).getContact(txn, introducee1.getId());
will(returnValue(introducee1));
oneOf(introductionGroupFactory).createIntroductionGroup(introducee1);
will(returnValue(introductionGroup1));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, storageId);
will(returnValue(state));
oneOf(introduceeManager).acceptIntroduction(txn, state, time);
// track message
oneOf(messageTracker).trackMessage(txn,
introductionGroup1.getId(), time, true);
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
introductionManager
.acceptIntroduction(introducee1.getId(), sessionId, time);
context.assertIsSatisfied();
}
@Test
public void testDeclineIntroduction() throws DbException, FormatException {
BdfDictionary state = BdfDictionary.of(
new BdfEntry(GROUP_ID_1, introductionGroup1.getId()),
new BdfEntry(GROUP_ID_2, introductionGroup2.getId())
);
txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
oneOf(db).getContact(txn, introducee1.getId());
will(returnValue(introducee1));
oneOf(introductionGroupFactory).createIntroductionGroup(introducee1);
will(returnValue(introductionGroup1));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, storageId);
will(returnValue(state));
oneOf(introduceeManager).declineIntroduction(txn, state, time);
// track message
oneOf(messageTracker).trackMessage(txn,
introductionGroup1.getId(), time, true);
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
introductionManager
.declineIntroduction(introducee1.getId(), sessionId, time);
context.assertIsSatisfied();
}
@Test
public void testGetIntroductionMessages()
throws DbException, FormatException {
Map<MessageId, BdfDictionary> metadata = Collections.emptyMap();
Collection<MessageStatus> statuses = Collections.emptyList();
txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getContact(txn, introducee1.getId());
will(returnValue(introducee1));
oneOf(introductionGroupFactory).createIntroductionGroup(introducee1);
will(returnValue(introductionGroup1));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
introductionGroup1.getId());
will(returnValue(metadata));
oneOf(db).getMessageStatus(txn, introducee1.getId(),
introductionGroup1.getId());
will(returnValue(statuses));
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
introductionManager.getIntroductionMessages(introducee1.getId());
context.assertIsSatisfied();
}
@Test
public void testIncomingRequestMessage()
throws DbException, FormatException {
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_REQUEST);
BdfDictionary state = new BdfDictionary();
txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(introduceeManager)
.initialize(txn, introductionGroup1.getId(), msg);
will(returnValue(state));
oneOf(introduceeManager)
.incomingMessage(txn, state, msg);
// track message
oneOf(messageTracker).trackIncomingMessage(txn, message1);
}});
introductionManager
.incomingMessage(txn, message1, new BdfList(), msg);
context.assertIsSatisfied();
assertFalse(txn.isCommitted());
}
@Test
public void testIncomingResponseMessage()
throws DbException, FormatException {
BdfDictionary msg = BdfDictionary.of(
new BdfEntry(TYPE, TYPE_RESPONSE),
new BdfEntry(SESSION_ID, sessionId)
);
BdfDictionary state = new BdfDictionary();
state.put(ROLE, ROLE_INTRODUCER);
state.put(GROUP_ID_1, introductionGroup1.getId());
state.put(GROUP_ID_2, introductionGroup2.getId());
txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, storageId);
will(returnValue(state));
oneOf(introducerManager).incomingMessage(txn, state, msg);
// track message
oneOf(messageTracker).trackIncomingMessage(txn, message1);
}});
introductionManager
.incomingMessage(txn, message1, new BdfList(), msg);
context.assertIsSatisfied();
assertFalse(txn.isCommitted());
}
}

View File

@@ -1,361 +1,424 @@
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.system.SystemClock;
import org.briarproject.bramble.test.ValidatorTestCase;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.test.BriarTestCase;
import org.jmock.Mockery;
import org.jmock.Expectations;
import org.junit.Test;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import javax.annotation.Nullable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAC_BYTES;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_BYTES;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ACCEPT;
import static org.briarproject.briar.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.GROUP_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC_LENGTH;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MSG;
import static org.briarproject.briar.api.introduction.IntroductionConstants.NAME;
import static org.briarproject.briar.api.introduction.IntroductionConstants.PUBLIC_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNATURE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TIME;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TRANSPORT;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ABORT;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ACK;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_RESPONSE;
import static org.junit.Assert.assertArrayEquals;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_REQUEST_MESSAGE_LENGTH;
import static org.briarproject.briar.introduction.MessageType.ABORT;
import static org.briarproject.briar.introduction.MessageType.ACCEPT;
import static org.briarproject.briar.introduction.MessageType.ACTIVATE;
import static org.briarproject.briar.introduction.MessageType.AUTH;
import static org.briarproject.briar.introduction.MessageType.DECLINE;
import static org.briarproject.briar.introduction.MessageType.REQUEST;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
public class IntroductionValidatorTest extends BriarTestCase {
public class IntroductionValidatorTest extends ValidatorTestCase {
private final Mockery context = new Mockery();
private final Group group;
private final Message message;
private final IntroductionValidator validator;
private final Clock clock = new SystemClock();
private final MessageEncoder messageEncoder =
context.mock(MessageEncoder.class);
private final IntroductionValidator validator =
new IntroductionValidator(messageEncoder, clientHelper,
metadataEncoder, clock);
public IntroductionValidatorTest() {
group = getGroup(getClientId());
MessageId messageId = new MessageId(getRandomId());
long timestamp = System.currentTimeMillis();
byte[] raw = getRandomBytes(123);
message = new Message(messageId, group.getId(), timestamp, raw);
ClientHelper clientHelper = context.mock(ClientHelper.class);
MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class);
validator = new IntroductionValidator(clientHelper, metadataEncoder,
clock);
context.assertIsSatisfied();
}
private final SessionId sessionId = new SessionId(getRandomId());
private final MessageId previousMsgId = new MessageId(getRandomId());
private final String text = getRandomString(MAX_REQUEST_MESSAGE_LENGTH);
private final BdfDictionary meta = new BdfDictionary();
private final long acceptTimestamp = 42;
private final BdfDictionary transportProperties = BdfDictionary.of(
new BdfEntry("transportId", new BdfDictionary())
);
private final byte[] mac = getRandomBytes(MAC_BYTES);
private final byte[] signature = getRandomBytes(MAX_SIGNATURE_BYTES);
//
// Introduction Requests
// Introduction REQUEST
//
@Test
public void testValidateProperIntroductionRequest() throws Exception {
byte[] sessionId = getRandomId();
String name = getRandomString(MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
String text = getRandomString(MAX_INTRODUCTION_MESSAGE_LENGTH);
public void testAcceptsRequest() throws Exception {
BdfList body = BdfList.of(REQUEST.getValue(), previousMsgId.getBytes(),
authorList, text);
BdfList body = BdfList.of(TYPE_REQUEST, sessionId,
name, publicKey, text);
expectParseAuthor(authorList, author);
expectEncodeRequestMetadata();
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
BdfDictionary result =
validator.validateMessage(message, group, body).getDictionary();
assertEquals(Long.valueOf(TYPE_REQUEST), result.getLong(TYPE));
assertEquals(sessionId, result.getRaw(SESSION_ID));
assertEquals(name, result.getString(NAME));
assertEquals(publicKey, result.getRaw(PUBLIC_KEY));
assertEquals(text, result.getString(MSG));
context.assertIsSatisfied();
}
@Test(expected = FormatException.class)
public void testValidateIntroductionRequestWithNoName() throws Exception {
BdfDictionary msg = getValidIntroductionRequest();
// no NAME is message
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getRaw(PUBLIC_KEY));
if (msg.containsKey(MSG)) body.add(msg.getString(MSG));
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateIntroductionRequestWithLongName() throws Exception {
// too long NAME in message
BdfDictionary msg = getValidIntroductionRequest();
msg.put(NAME, msg.get(NAME) + "x");
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getString(NAME), msg.getRaw(PUBLIC_KEY));
if (msg.containsKey(MSG)) body.add(msg.getString(MSG));
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateIntroductionRequestWithWrongType()
throws Exception {
// wrong message type
BdfDictionary msg = getValidIntroductionRequest();
msg.put(TYPE, 324234);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getString(NAME), msg.getRaw(PUBLIC_KEY));
if (msg.containsKey(MSG)) body.add(msg.getString(MSG));
validator.validateMessage(message, group, body);
}
private BdfDictionary getValidIntroductionRequest() throws Exception {
byte[] sessionId = getRandomId();
Author author = getAuthor();
String text = getRandomString(MAX_MESSAGE_BODY_LENGTH);
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_REQUEST);
msg.put(SESSION_ID, sessionId);
msg.put(NAME, author.getName());
msg.put(PUBLIC_KEY, author.getPublicKey());
msg.put(MSG, text);
return msg;
}
//
// Introduction Responses
//
@Test
public void testValidateIntroductionAcceptResponse() throws Exception {
byte[] groupId = getRandomId();
byte[] sessionId = getRandomId();
long time = clock.currentTimeMillis();
byte[] publicKey = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
String transportId =
getRandomString(TransportId.MAX_TRANSPORT_ID_LENGTH);
BdfDictionary tProps = BdfDictionary.of(
new BdfEntry(getRandomString(MAX_PROPERTY_LENGTH),
getRandomString(MAX_PROPERTY_LENGTH))
);
BdfDictionary tp = BdfDictionary.of(
new BdfEntry(transportId, tProps)
);
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_RESPONSE);
msg.put(GROUP_ID, groupId);
msg.put(SESSION_ID, sessionId);
msg.put(ACCEPT, true);
msg.put(TIME, time);
msg.put(E_PUBLIC_KEY, publicKey);
msg.put(TRANSPORT, tp);
BdfList body = BdfList.of(TYPE_RESPONSE, msg.getRaw(SESSION_ID),
msg.getBoolean(ACCEPT), msg.getLong(TIME),
msg.getRaw(E_PUBLIC_KEY), msg.getDictionary(TRANSPORT));
BdfDictionary result =
validator.validateMessage(message, group, body).getDictionary();
assertEquals(Long.valueOf(TYPE_RESPONSE), result.getLong(TYPE));
assertEquals(sessionId, result.getRaw(SESSION_ID));
assertEquals(true, result.getBoolean(ACCEPT));
assertEquals(publicKey, result.getRaw(E_PUBLIC_KEY));
assertEquals(tp, result.getDictionary(TRANSPORT));
context.assertIsSatisfied();
assertExpectedContext(messageContext, previousMsgId);
}
@Test
public void testValidateIntroductionDeclineResponse() throws Exception {
BdfDictionary msg = getValidIntroductionResponse(false);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getBoolean(ACCEPT));
public void testAcceptsRequestWithPreviousMsgIdNull() throws Exception {
BdfList body = BdfList.of(REQUEST.getValue(), null, authorList, text);
BdfDictionary result = validator.validateMessage(message, group, body)
.getDictionary();
expectParseAuthor(authorList, author);
expectEncodeRequestMetadata();
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertFalse(result.getBoolean(ACCEPT));
context.assertIsSatisfied();
assertExpectedContext(messageContext, null);
}
@Test
public void testAcceptsRequestWithMessageNull() throws Exception {
BdfList body = BdfList.of(REQUEST.getValue(), null, authorList, null);
expectParseAuthor(authorList, author);
expectEncodeRequestMetadata();
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, null);
}
@Test(expected = FormatException.class)
public void testValidateIntroductionResponseWithoutAccept()
throws Exception {
BdfDictionary msg = getValidIntroductionResponse(false);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID));
public void testRejectsTooShortBodyForRequest() throws Exception {
BdfList body = BdfList.of(REQUEST.getValue(), null, authorList);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateIntroductionResponseWithBrokenTp()
throws Exception {
BdfDictionary msg = getValidIntroductionResponse(true);
BdfDictionary tp = msg.getDictionary(TRANSPORT);
tp.put(
getRandomString(TransportId.MAX_TRANSPORT_ID_LENGTH), "X");
msg.put(TRANSPORT, tp);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getBoolean(ACCEPT), msg.getLong(TIME),
msg.getRaw(E_PUBLIC_KEY), msg.getDictionary(TRANSPORT));
public void testRejectsTooLongBodyForRequest() throws Exception {
BdfList body =
BdfList.of(REQUEST.getValue(), null, authorList, text, null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateIntroductionResponseWithoutPublicKey()
throws Exception {
BdfDictionary msg = getValidIntroductionResponse(true);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getBoolean(ACCEPT), msg.getLong(TIME),
msg.getDictionary(TRANSPORT));
public void testRejectsRawMessageForRequest() throws Exception {
BdfList body =
BdfList.of(REQUEST.getValue(), null, authorList, getRandomId());
expectParseAuthor(authorList, author);
validator.validateMessage(message, group, body);
}
private BdfDictionary getValidIntroductionResponse(boolean accept)
@Test(expected = FormatException.class)
public void testRejectsStringMessageIdForRequest() throws Exception {
BdfList body =
BdfList.of(REQUEST.getValue(), "NoMessageId", authorList, null);
validator.validateMessage(message, group, body);
}
//
// Introduction ACCEPT
//
@Test
public void testAcceptsAccept() throws Exception {
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
acceptTimestamp, transportProperties);
context.checking(new Expectations() {{
oneOf(clientHelper).parseAndValidateTransportProperties(
transportProperties.getDictionary("transportId"));
}});
expectEncodeMetadata(ACCEPT);
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForAccept() throws Exception {
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH), acceptTimestamp);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForAccept() throws Exception {
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
acceptTimestamp, transportProperties, null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidSessionIdForAccept() throws Exception {
BdfList body =
BdfList.of(ACCEPT.getValue(), null, previousMsgId.getBytes(),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH), acceptTimestamp,
transportProperties);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidPreviousMsgIdForAccept() throws Exception {
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(), 1,
getRandomBytes(MAX_PUBLIC_KEY_LENGTH), acceptTimestamp,
transportProperties);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongPublicKeyForAccept() throws Exception {
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), acceptTimestamp,
transportProperties);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsEmptyTransportPropertiesForAccept()
throws Exception {
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), acceptTimestamp,
new BdfDictionary());
validator.validateMessage(message, group, body);
}
byte[] groupId = getRandomId();
byte[] sessionId = getRandomId();
long time = clock.currentTimeMillis();
byte[] publicKey = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
String transportId =
getRandomString(TransportId.MAX_TRANSPORT_ID_LENGTH);
BdfDictionary tProps = BdfDictionary.of(
new BdfEntry(getRandomString(MAX_PROPERTY_LENGTH),
getRandomString(MAX_PROPERTY_LENGTH))
);
BdfDictionary tp = BdfDictionary.of(
new BdfEntry(transportId, tProps)
);
//
// Introduction DECLINE
//
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_RESPONSE);
msg.put(GROUP_ID, groupId);
msg.put(SESSION_ID, sessionId);
msg.put(ACCEPT, accept);
if (accept) {
msg.put(TIME, time);
msg.put(E_PUBLIC_KEY, publicKey);
msg.put(TRANSPORT, tp);
@Test
public void testAcceptsDecline() throws Exception {
BdfList body = BdfList.of(DECLINE.getValue(), sessionId.getBytes(),
previousMsgId.getBytes());
expectEncodeMetadata(DECLINE);
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForDecline() throws Exception {
BdfList body = BdfList.of(DECLINE.getValue(), sessionId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForDecline() throws Exception {
BdfList body = BdfList.of(DECLINE.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidSessionIdForDecline() throws Exception {
BdfList body =
BdfList.of(DECLINE.getValue(), null, previousMsgId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidPreviousMsgIdForDecline() throws Exception {
BdfList body = BdfList.of(DECLINE.getValue(), sessionId.getBytes(), 1);
validator.validateMessage(message, group, body);
}
//
// Introduction AUTH
//
@Test
public void testAcceptsAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), mac, signature);
expectEncodeMetadata(AUTH);
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), mac);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), mac, signature, null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortMacForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), getRandomBytes(MAC_BYTES - 1),
signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongMacForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(),
getRandomBytes(MAC_BYTES + 1), signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidMacForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), null, signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortSignatureForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), mac, getRandomBytes(0));
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongSignatureForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), mac,
getRandomBytes(MAX_SIGNATURE_BYTES + 1));
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidSignatureForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), mac, null);
validator.validateMessage(message, group, body);
}
//
// Introduction ACTIVATE
//
@Test
public void testAcceptsActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),
previousMsgId.getBytes());
expectEncodeMetadata(ACTIVATE);
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidSessionIdForActivate() throws Exception {
BdfList body =
BdfList.of(ACTIVATE.getValue(), null, previousMsgId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidPreviousMsgIdForActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(), 1);
validator.validateMessage(message, group, body);
}
//
// Introduction ABORT
//
@Test
public void testAcceptsAbort() throws Exception {
BdfList body = BdfList.of(ABORT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes());
expectEncodeMetadata(ABORT);
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForAbort() throws Exception {
BdfList body = BdfList.of(ABORT.getValue(), sessionId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForAbort() throws Exception {
BdfList body = BdfList.of(ABORT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidSessionIdForAbort() throws Exception {
BdfList body =
BdfList.of(ABORT.getValue(), null, previousMsgId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidPreviousMsgIdForAbort() throws Exception {
BdfList body = BdfList.of(ABORT.getValue(), sessionId.getBytes(), 1);
validator.validateMessage(message, group, body);
}
//
// Introduction Helper Methods
//
private void expectEncodeRequestMetadata() {
context.checking(new Expectations() {{
oneOf(messageEncoder)
.encodeRequestMetadata(message.getTimestamp(), false, false,
false, false);
will(returnValue(meta));
}});
}
private void expectEncodeMetadata(MessageType type) {
context.checking(new Expectations() {{
oneOf(messageEncoder)
.encodeMetadata(type, sessionId, message.getTimestamp(),
false, false, false);
will(returnValue(meta));
}});
}
private void assertExpectedContext(BdfMessageContext c,
@Nullable MessageId dependency) {
assertEquals(meta, c.getDictionary());
if (dependency == null) {
assertEquals(0, c.getDependencies().size());
} else {
assertEquals(dependency, c.getDependencies().iterator().next());
}
return msg;
}
//
// Introduction ACK
//
@Test
public void testValidateProperIntroductionAck() throws Exception {
byte[] sessionId = getRandomId();
byte[] mac = getRandomBytes(MAC_LENGTH);
byte[] sig = getRandomBytes(MAX_SIGNATURE_LENGTH);
BdfList body = BdfList.of(TYPE_ACK, sessionId, mac, sig);
BdfDictionary result =
validator.validateMessage(message, group, body).getDictionary();
assertEquals(Long.valueOf(TYPE_ACK), result.getLong(TYPE));
assertArrayEquals(sessionId, result.getRaw(SESSION_ID));
assertArrayEquals(mac, result.getRaw(MAC));
assertArrayEquals(sig, result.getRaw(SIGNATURE));
context.assertIsSatisfied();
}
@Test(expected = FormatException.class)
public void testValidateTooLongIntroductionAck() throws Exception {
BdfDictionary msg = BdfDictionary.of(
new BdfEntry(TYPE, TYPE_ACK),
new BdfEntry(SESSION_ID, getRandomId()),
new BdfEntry("garbage", getRandomString(255))
);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getString("garbage"));
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateIntroductionAckWithLongSessionId()
throws Exception {
BdfDictionary msg = BdfDictionary.of(
new BdfEntry(TYPE, TYPE_ACK),
new BdfEntry(SESSION_ID, new byte[SessionId.LENGTH + 1])
);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID));
validator.validateMessage(message, group, body);
}
//
// Introduction Abort
//
@Test
public void testValidateProperIntroductionAbort() throws Exception {
byte[] sessionId = getRandomId();
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, TYPE_ABORT);
msg.put(SESSION_ID, sessionId);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID));
BdfDictionary result =
validator.validateMessage(message, group, body).getDictionary();
assertEquals(Long.valueOf(TYPE_ABORT), result.getLong(TYPE));
assertEquals(sessionId, result.getRaw(SESSION_ID));
context.assertIsSatisfied();
}
@Test(expected = FormatException.class)
public void testValidateTooLongIntroductionAbort() throws Exception {
BdfDictionary msg = BdfDictionary.of(
new BdfEntry(TYPE, TYPE_ABORT),
new BdfEntry(SESSION_ID, getRandomId()),
new BdfEntry("garbage", getRandomString(255))
);
BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID),
msg.getString("garbage"));
validator.validateMessage(message, group, body);
}
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.introduction2;
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
@@ -28,9 +28,9 @@ import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getTransportPropertiesMap;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH;
import static org.briarproject.briar.introduction2.MessageType.ABORT;
import static org.briarproject.briar.introduction2.MessageType.REQUEST;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_REQUEST_MESSAGE_LENGTH;
import static org.briarproject.briar.introduction.MessageType.ABORT;
import static org.briarproject.briar.introduction.MessageType.REQUEST;
import static org.briarproject.briar.test.BriarTestUtils.getRealAuthor;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -55,8 +55,7 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
private final SessionId sessionId = new SessionId(getRandomId());
private final MessageId previousMsgId = new MessageId(getRandomId());
private final Author author;
private final String text =
getRandomString(MAX_INTRODUCTION_MESSAGE_LENGTH);
private final String text = getRandomString(MAX_REQUEST_MESSAGE_LENGTH);
private final byte[] ephemeralPublicKey =
getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
private final byte[] mac = getRandomBytes(MAC_BYTES);

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.introduction2;
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
@@ -16,8 +16,8 @@ import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH;
import static org.briarproject.briar.introduction2.MessageType.REQUEST;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_REQUEST_MESSAGE_LENGTH;
import static org.briarproject.briar.introduction.MessageType.REQUEST;
public class MessageEncoderTest extends BrambleMockTestCase {
@@ -35,8 +35,7 @@ public class MessageEncoderTest extends BrambleMockTestCase {
private final byte[] body = getRandomBytes(42);
private final Author author = getAuthor();
private final BdfList authorList = new BdfList();
private final String text =
getRandomString(MAX_INTRODUCTION_MESSAGE_LENGTH);
private final String text = getRandomString(MAX_REQUEST_MESSAGE_LENGTH);
@Test
public void testEncodeRequestMessage() throws FormatException {

View File

@@ -1,98 +0,0 @@
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.MessageQueueManager;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.test.BriarTestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.briar.api.introduction.IntroductionConstants.GROUP_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAC;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.briar.api.introduction.IntroductionConstants.SIGNATURE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ACK;
import static org.junit.Assert.assertFalse;
public class MessageSenderTest extends BriarTestCase {
private final Mockery context;
private final MessageSender messageSender;
private final DatabaseComponent db;
private final ClientHelper clientHelper;
private final MetadataEncoder metadataEncoder;
private final MessageQueueManager messageQueueManager;
private final Clock clock;
public MessageSenderTest() {
context = new Mockery();
db = context.mock(DatabaseComponent.class);
clientHelper = context.mock(ClientHelper.class);
metadataEncoder =
context.mock(MetadataEncoder.class);
messageQueueManager =
context.mock(MessageQueueManager.class);
clock = context.mock(Clock.class);
messageSender = new MessageSender(db, clientHelper, clock,
metadataEncoder, messageQueueManager);
}
@Test
public void testSendMessage() throws DbException, FormatException {
Transaction txn = new Transaction(null, false);
Group privateGroup = getGroup(getClientId());
SessionId sessionId = new SessionId(getRandomId());
byte[] mac = getRandomBytes(42);
byte[] sig = getRandomBytes(MAX_SIGNATURE_LENGTH);
long time = 42L;
BdfDictionary msg = BdfDictionary.of(
new BdfEntry(TYPE, TYPE_ACK),
new BdfEntry(GROUP_ID, privateGroup.getId()),
new BdfEntry(SESSION_ID, sessionId),
new BdfEntry(MAC, mac),
new BdfEntry(SIGNATURE, sig)
);
BdfList bodyList =
BdfList.of(TYPE_ACK, sessionId.getBytes(), mac, sig);
byte[] body = getRandomBytes(8);
Metadata metadata = new Metadata();
context.checking(new Expectations() {{
oneOf(clientHelper).toByteArray(bodyList);
will(returnValue(body));
oneOf(db).getGroup(txn, privateGroup.getId());
will(returnValue(privateGroup));
oneOf(metadataEncoder).encode(msg);
will(returnValue(metadata));
oneOf(clock).currentTimeMillis();
will(returnValue(time));
oneOf(messageQueueManager)
.sendMessage(txn, privateGroup, time, body, metadata);
}});
messageSender.sendMessage(txn, msg);
context.assertIsSatisfied();
assertFalse(txn.isCommitted());
}
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.introduction2;
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
@@ -13,7 +13,7 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.introduction2.IntroducerSession.Introducee;
import org.briarproject.briar.introduction.IntroducerSession.Introducee;
import org.briarproject.briar.test.BriarIntegrationTestComponent;
import org.briarproject.briar.test.DaggerBriarIntegrationTestComponent;
import org.junit.Test;
@@ -29,11 +29,11 @@ import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.briarproject.bramble.test.TestUtils.getTransportPropertiesMap;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.introduction2.IntroduceeState.LOCAL_ACCEPTED;
import static org.briarproject.briar.introduction2.IntroducerState.AWAIT_AUTHS;
import static org.briarproject.briar.introduction2.IntroductionConstants.SESSION_KEY_ROLE;
import static org.briarproject.briar.api.introduction2.Role.INTRODUCEE;
import static org.briarproject.briar.api.introduction2.Role.INTRODUCER;
import static org.briarproject.briar.introduction.IntroduceeState.LOCAL_ACCEPTED;
import static org.briarproject.briar.introduction.IntroducerState.AWAIT_AUTHS;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_ROLE;
import static org.briarproject.briar.api.introduction.Role.INTRODUCEE;
import static org.briarproject.briar.api.introduction.Role.INTRODUCER;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

View File

@@ -1,428 +0,0 @@
package org.briarproject.briar.introduction2;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.test.ValidatorTestCase;
import org.briarproject.briar.api.client.SessionId;
import org.jmock.Expectations;
import org.junit.Test;
import javax.annotation.Nullable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAC_BYTES;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_BYTES;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH;
import static org.briarproject.briar.introduction2.MessageType.ABORT;
import static org.briarproject.briar.introduction2.MessageType.ACCEPT;
import static org.briarproject.briar.introduction2.MessageType.ACTIVATE;
import static org.briarproject.briar.introduction2.MessageType.AUTH;
import static org.briarproject.briar.introduction2.MessageType.DECLINE;
import static org.briarproject.briar.introduction2.MessageType.REQUEST;
import static org.junit.Assert.assertEquals;
public class IntroductionValidatorTest extends ValidatorTestCase {
private final MessageEncoder messageEncoder =
context.mock(MessageEncoder.class);
private final IntroductionValidator validator =
new IntroductionValidator(messageEncoder, clientHelper,
metadataEncoder, clock);
private final SessionId sessionId = new SessionId(getRandomId());
private final MessageId previousMsgId = new MessageId(getRandomId());
private final String text =
getRandomString(MAX_INTRODUCTION_MESSAGE_LENGTH);
private final BdfDictionary meta = new BdfDictionary();
private final long acceptTimestamp = 42;
private final BdfDictionary transportProperties = BdfDictionary.of(
new BdfEntry("transportId", new BdfDictionary())
);
private final byte[] mac = getRandomBytes(MAC_BYTES);
private final byte[] signature = getRandomBytes(MAX_SIGNATURE_BYTES);
//
// Introduction REQUEST
//
@Test
public void testAcceptsRequest() throws Exception {
BdfList body = BdfList.of(REQUEST.getValue(), previousMsgId.getBytes(),
authorList, text);
expectParseAuthor(authorList, author);
expectEncodeRequestMetadata();
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, previousMsgId);
}
@Test
public void testAcceptsRequestWithPreviousMsgIdNull() throws Exception {
BdfList body = BdfList.of(REQUEST.getValue(), null, authorList, text);
expectParseAuthor(authorList, author);
expectEncodeRequestMetadata();
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, null);
}
@Test
public void testAcceptsRequestWithMessageNull() throws Exception {
BdfList body = BdfList.of(REQUEST.getValue(), null, authorList, null);
expectParseAuthor(authorList, author);
expectEncodeRequestMetadata();
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, null);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForRequest() throws Exception {
BdfList body = BdfList.of(REQUEST.getValue(), null, authorList);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForRequest() throws Exception {
BdfList body =
BdfList.of(REQUEST.getValue(), null, authorList, text, null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsRawMessageForRequest() throws Exception {
BdfList body =
BdfList.of(REQUEST.getValue(), null, authorList, getRandomId());
expectParseAuthor(authorList, author);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsStringMessageIdForRequest() throws Exception {
BdfList body =
BdfList.of(REQUEST.getValue(), "NoMessageId", authorList, null);
validator.validateMessage(message, group, body);
}
//
// Introduction ACCEPT
//
@Test
public void testAcceptsAccept() throws Exception {
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
acceptTimestamp, transportProperties);
context.checking(new Expectations() {{
oneOf(clientHelper).parseAndValidateTransportProperties(
transportProperties.getDictionary("transportId"));
}});
expectEncodeMetadata(ACCEPT);
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForAccept() throws Exception {
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH), acceptTimestamp);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForAccept() throws Exception {
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
acceptTimestamp, transportProperties, null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidSessionIdForAccept() throws Exception {
BdfList body =
BdfList.of(ACCEPT.getValue(), null, previousMsgId.getBytes(),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH), acceptTimestamp,
transportProperties);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidPreviousMsgIdForAccept() throws Exception {
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
null, getRandomBytes(MAX_PUBLIC_KEY_LENGTH), acceptTimestamp,
transportProperties);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongPublicKeyForAccept() throws Exception {
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), acceptTimestamp,
transportProperties);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsEmptyTransportPropertiesForAccept()
throws Exception {
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), acceptTimestamp,
new BdfDictionary());
validator.validateMessage(message, group, body);
}
//
// Introduction DECLINE
//
@Test
public void testAcceptsDecline() throws Exception {
BdfList body = BdfList.of(DECLINE.getValue(), sessionId.getBytes(),
previousMsgId.getBytes());
expectEncodeMetadata(DECLINE);
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForDecline() throws Exception {
BdfList body = BdfList.of(DECLINE.getValue(), sessionId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForDecline() throws Exception {
BdfList body = BdfList.of(DECLINE.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidSessionIdForDecline() throws Exception {
BdfList body =
BdfList.of(DECLINE.getValue(), null, previousMsgId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidPreviousMsgIdForDecline() throws Exception {
BdfList body = BdfList.of(DECLINE.getValue(), sessionId.getBytes(),
null);
validator.validateMessage(message, group, body);
}
//
// Introduction AUTH
//
@Test
public void testAcceptsAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), mac, signature);
expectEncodeMetadata(AUTH);
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), mac);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), mac, signature, null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortMacForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), getRandomBytes(MAC_BYTES - 1),
signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongMacForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(),
getRandomBytes(MAC_BYTES + 1), signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidMacForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), null, signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortSignatureForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), mac, getRandomBytes(0));
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongSignatureForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), mac,
getRandomBytes(MAX_SIGNATURE_BYTES + 1));
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidSignatureForAuth() throws Exception {
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), mac, null);
validator.validateMessage(message, group, body);
}
//
// Introduction ACTIVATE
//
@Test
public void testAcceptsActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),
previousMsgId.getBytes());
expectEncodeMetadata(ACTIVATE);
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidSessionIdForActivate() throws Exception {
BdfList body =
BdfList.of(ACTIVATE.getValue(), null, previousMsgId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidPreviousMsgIdForActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),
null);
validator.validateMessage(message, group, body);
}
//
// Introduction ABORT
//
@Test
public void testAcceptsAbort() throws Exception {
BdfList body = BdfList.of(ABORT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes());
expectEncodeMetadata(ABORT);
BdfMessageContext messageContext =
validator.validateMessage(message, group, body);
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForAbort() throws Exception {
BdfList body = BdfList.of(ABORT.getValue(), sessionId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForAbort() throws Exception {
BdfList body = BdfList.of(ABORT.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidSessionIdForAbort() throws Exception {
BdfList body =
BdfList.of(ABORT.getValue(), null, previousMsgId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidPreviousMsgIdForAbort() throws Exception {
BdfList body = BdfList.of(ABORT.getValue(), sessionId.getBytes(),
null);
validator.validateMessage(message, group, body);
}
//
// Introduction Helper Methods
//
private void expectEncodeRequestMetadata() {
context.checking(new Expectations() {{
oneOf(messageEncoder)
.encodeRequestMetadata(message.getTimestamp(), false, false,
false, false);
will(returnValue(meta));
}});
}
private void expectEncodeMetadata(MessageType type) {
context.checking(new Expectations() {{
oneOf(messageEncoder)
.encodeMetadata(type, sessionId, message.getTimestamp(),
false, false, false);
will(returnValue(meta));
}});
}
private void assertExpectedContext(BdfMessageContext c,
@Nullable MessageId dependency) {
assertEquals(meta, c.getDictionary());
if (dependency == null) {
assertEquals(0, c.getDependencies().size());
} else {
assertEquals(dependency, c.getDependencies().iterator().next());
}
}
}

View File

@@ -37,9 +37,9 @@ import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.client.BriarClientModule;
import org.briarproject.briar.forum.ForumModule;
import org.briarproject.briar.introduction.IntroductionModule;
import org.briarproject.briar.introduction2.IntroductionCryptoImplTest;
import org.briarproject.briar.introduction2.MessageEncoderParserIntegrationTest;
import org.briarproject.briar.introduction2.SessionEncoderParserIntegrationTest;
import org.briarproject.briar.introduction.IntroductionCryptoImplTest;
import org.briarproject.briar.introduction.MessageEncoderParserIntegrationTest;
import org.briarproject.briar.introduction.SessionEncoderParserIntegrationTest;
import org.briarproject.briar.messaging.MessagingModule;
import org.briarproject.briar.privategroup.PrivateGroupModule;
import org.briarproject.briar.privategroup.invitation.GroupInvitationModule;