mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
292 lines
12 KiB
Java
292 lines
12 KiB
Java
package org.briarproject.introduction;
|
|
|
|
import org.briarproject.BriarTestCase;
|
|
import org.briarproject.TestUtils;
|
|
import org.briarproject.api.Bytes;
|
|
import org.briarproject.api.FormatException;
|
|
import org.briarproject.api.clients.ClientHelper;
|
|
import org.briarproject.api.clients.SessionId;
|
|
import org.briarproject.api.contact.Contact;
|
|
import org.briarproject.api.contact.ContactId;
|
|
import org.briarproject.api.contact.ContactManager;
|
|
import org.briarproject.api.crypto.CryptoComponent;
|
|
import org.briarproject.api.data.BdfDictionary;
|
|
import org.briarproject.api.data.BdfEntry;
|
|
import org.briarproject.api.data.BdfList;
|
|
import org.briarproject.api.db.DatabaseComponent;
|
|
import org.briarproject.api.db.DbException;
|
|
import org.briarproject.api.db.Transaction;
|
|
import org.briarproject.api.identity.Author;
|
|
import org.briarproject.api.identity.AuthorFactory;
|
|
import org.briarproject.api.identity.AuthorId;
|
|
import org.briarproject.api.identity.IdentityManager;
|
|
import org.briarproject.api.introduction.IntroduceeProtocolState;
|
|
import org.briarproject.api.properties.TransportPropertyManager;
|
|
import org.briarproject.api.sync.ClientId;
|
|
import org.briarproject.api.sync.Group;
|
|
import org.briarproject.api.sync.GroupId;
|
|
import org.briarproject.api.sync.Message;
|
|
import org.briarproject.api.sync.MessageId;
|
|
import org.briarproject.api.system.Clock;
|
|
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.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
|
import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.EXISTS;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.INTRODUCER;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.LOCAL_AUTHOR_ID;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_ID;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_IS_US;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCEE;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.TIME;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
|
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
|
import static org.junit.Assert.assertFalse;
|
|
|
|
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 MessageSender messageSender;
|
|
private final TransportPropertyManager transportPropertyManager;
|
|
private final AuthorFactory authorFactory;
|
|
private final ContactManager contactManager;
|
|
private final IdentityManager identityManager;
|
|
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 ClientId clientId;
|
|
private final SessionId sessionId;
|
|
private final Message message1;
|
|
|
|
public IntroduceeManagerTest() {
|
|
context = new Mockery();
|
|
context.setImposteriser(ClassImposteriser.INSTANCE);
|
|
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 = context.mock(TransportPropertyManager.class);
|
|
authorFactory = context.mock(AuthorFactory.class);
|
|
contactManager = context.mock(ContactManager.class);
|
|
identityManager = context.mock(IdentityManager.class);
|
|
|
|
introduceeManager = new IntroduceeManager(messageSender, db,
|
|
clientHelper, clock, cryptoComponent, transportPropertyManager,
|
|
authorFactory, contactManager, identityManager,
|
|
introductionGroupFactory);
|
|
|
|
AuthorId authorId0 = new AuthorId(TestUtils.getRandomId());
|
|
Author author0 = new Author(authorId0, "Introducer",
|
|
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
|
AuthorId localAuthorId = new AuthorId(TestUtils.getRandomId());
|
|
ContactId contactId0 = new ContactId(234);
|
|
introducer =
|
|
new Contact(contactId0, author0, localAuthorId, true, true);
|
|
|
|
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
|
|
Author author1 = new Author(authorId1, "Introducee1",
|
|
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
|
AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId());
|
|
ContactId contactId1 = new ContactId(234);
|
|
introducee1 =
|
|
new Contact(contactId1, author1, localAuthorId1, true, true);
|
|
|
|
AuthorId authorId2 = new AuthorId(TestUtils.getRandomId());
|
|
Author author2 = new Author(authorId2, "Introducee2",
|
|
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
|
ContactId contactId2 = new ContactId(235);
|
|
introducee2 =
|
|
new Contact(contactId2, author2, localAuthorId, true, true);
|
|
|
|
clientId = IntroductionManagerImpl.CLIENT_ID;
|
|
localGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
|
|
clientId, new byte[0]);
|
|
introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
|
|
clientId, new byte[0]);
|
|
|
|
sessionId = new SessionId(TestUtils.getRandomId());
|
|
localStateMessage = new Message(
|
|
new MessageId(TestUtils.getRandomId()),
|
|
localGroup1.getId(),
|
|
time,
|
|
TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
|
|
);
|
|
message1 = new Message(
|
|
new MessageId(TestUtils.getRandomId()),
|
|
introductionGroup1.getId(),
|
|
time,
|
|
TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
|
|
);
|
|
|
|
txn = new Transaction(null, false);
|
|
}
|
|
|
|
@Test
|
|
public void testIncomingRequestMessage()
|
|
throws DbException, FormatException {
|
|
|
|
final 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());
|
|
|
|
final 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.isComplete());
|
|
}
|
|
|
|
@Test
|
|
public void testIncomingResponseMessage()
|
|
throws DbException, FormatException {
|
|
|
|
final 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());
|
|
|
|
final 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, TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
|
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.isComplete());
|
|
}
|
|
|
|
private BdfDictionary initializeSessionState(final Transaction txn,
|
|
final GroupId groupId, final BdfDictionary msg)
|
|
throws DbException, FormatException {
|
|
|
|
final SecureRandom secureRandom = context.mock(SecureRandom.class);
|
|
final Bytes salt = new Bytes(new byte[64]);
|
|
final BdfDictionary groupMetadata = BdfDictionary.of(
|
|
new BdfEntry(CONTACT, introducee1.getId().getInt())
|
|
);
|
|
final boolean contactExists = true;
|
|
final 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, true);
|
|
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();
|
|
return result;
|
|
}
|
|
|
|
}
|