mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 19:59:05 +01:00
Replace old introduction client with new one
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ interface IntroductionIntegrationTestComponent
|
||||
|
||||
void inject(IntroductionIntegrationTest init);
|
||||
|
||||
MessageSender getMessageSender();
|
||||
MessageEncoder getMessageEncoder();
|
||||
MessageParser getMessageParser();
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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 {
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user