mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
2 Commits
removable-
...
377_replac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cef4c31e0b | ||
|
|
62527a62c1 |
@@ -300,7 +300,7 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener0.response2Received);
|
||||
|
||||
// sync first forwarded response
|
||||
deliverMessage(sync0, contactId0, sync2, contactId2);
|
||||
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||
|
||||
// note how the introducer does not forward the second response,
|
||||
// because after the first decline the protocol finished
|
||||
@@ -1142,6 +1142,7 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
||||
}
|
||||
}
|
||||
} catch (DbException | IOException exception) {
|
||||
msgWaiter.rethrow(exception);
|
||||
eventWaiter.rethrow(exception);
|
||||
} finally {
|
||||
eventWaiter.resume();
|
||||
|
||||
@@ -71,6 +71,7 @@ public interface IntroductionConstants {
|
||||
String OUR_SIGNATURE = "ourSignature";
|
||||
|
||||
String TASK = "task";
|
||||
int NO_TASK = -1;
|
||||
int TASK_ADD_CONTACT = 0;
|
||||
int TASK_ACTIVATE_CONTACT = 1;
|
||||
int TASK_ABORT = 2;
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.introduction;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ProtocolEngine;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.event.Event;
|
||||
@@ -11,7 +12,6 @@ import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.introduction.IntroduceeAction;
|
||||
import org.briarproject.api.introduction.IntroduceeProtocolState;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -34,30 +34,17 @@ import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_RE
|
||||
import static org.briarproject.api.introduction.IntroduceeProtocolState.ERROR;
|
||||
import static org.briarproject.api.introduction.IntroduceeProtocolState.FINISHED;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED;
|
||||
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.MAC;
|
||||
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.MSG;
|
||||
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.OUR_MAC;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_PUBLIC_KEY;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_SIGNATURE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_TIME;
|
||||
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_INTRODUCEE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.SIGNATURE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TASK;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TASK_ABORT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TASK_ACTIVATE_CONTACT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TASK_ADD_CONTACT;
|
||||
@@ -69,23 +56,24 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
||||
|
||||
public class IntroduceeEngine
|
||||
implements ProtocolEngine<BdfDictionary, BdfDictionary, BdfDictionary> {
|
||||
class IntroduceeEngine
|
||||
implements
|
||||
ProtocolEngine<BdfDictionary, IntroduceeSessionState, BdfDictionary> {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(IntroduceeEngine.class.getName());
|
||||
|
||||
@Override
|
||||
public StateUpdate<BdfDictionary, BdfDictionary> onLocalAction(
|
||||
BdfDictionary localState, BdfDictionary localAction) {
|
||||
public StateUpdate<IntroduceeSessionState, BdfDictionary> onLocalAction(
|
||||
IntroduceeSessionState localState, BdfDictionary localAction) {
|
||||
|
||||
try {
|
||||
IntroduceeProtocolState currentState =
|
||||
getState(localState.getLong(STATE));
|
||||
IntroduceeProtocolState currentState = localState.getState();
|
||||
int type = localAction.getLong(TYPE).intValue();
|
||||
IntroduceeAction action;
|
||||
if (localState.containsKey(ACCEPT)) action = IntroduceeAction
|
||||
.getLocal(type, localState.getBoolean(ACCEPT));
|
||||
// FIXME: discuss? used to be: if has key ACCEPT:
|
||||
if (localState.wasLocallyAcceptedOrDeclined()) action = IntroduceeAction
|
||||
.getLocal(type, localState.wasLocallyAccepted());
|
||||
else action = IntroduceeAction.getLocal(type);
|
||||
IntroduceeProtocolState nextState = currentState.next(action);
|
||||
|
||||
@@ -104,17 +92,17 @@ public class IntroduceeEngine
|
||||
|
||||
List<BdfDictionary> messages = new ArrayList<BdfDictionary>(1);
|
||||
if (action == LOCAL_ACCEPT || action == LOCAL_DECLINE) {
|
||||
localState.put(STATE, nextState.getValue());
|
||||
localState.put(ANSWERED, true);
|
||||
localState.setState(nextState);
|
||||
localState.setAnswered(true);
|
||||
// create the introduction response message
|
||||
BdfDictionary msg = new BdfDictionary();
|
||||
msg.put(TYPE, TYPE_RESPONSE);
|
||||
msg.put(GROUP_ID, localState.getRaw(GROUP_ID));
|
||||
msg.put(SESSION_ID, localState.getRaw(SESSION_ID));
|
||||
msg.put(ACCEPT, localState.getBoolean(ACCEPT));
|
||||
if (localState.getBoolean(ACCEPT)) {
|
||||
msg.put(TIME, localState.getLong(OUR_TIME));
|
||||
msg.put(E_PUBLIC_KEY, localState.getRaw(OUR_PUBLIC_KEY));
|
||||
msg.put(GROUP_ID, localState.getIntroductionGroupId());
|
||||
msg.put(SESSION_ID, localState.getSessionId());
|
||||
msg.put(ACCEPT, localState.wasLocallyAccepted());
|
||||
if (localState.wasLocallyAccepted()) {
|
||||
msg.put(TIME, localState.getOurTime());
|
||||
msg.put(E_PUBLIC_KEY, localState.getOurPublicKey());
|
||||
msg.put(TRANSPORT, localAction.getDictionary(TRANSPORT));
|
||||
}
|
||||
msg.put(MESSAGE_TIME, localAction.getLong(MESSAGE_TIME));
|
||||
@@ -122,7 +110,7 @@ public class IntroduceeEngine
|
||||
logAction(currentState, localState, msg);
|
||||
|
||||
if (nextState == AWAIT_ACK) {
|
||||
localState.put(TASK, TASK_ADD_CONTACT);
|
||||
localState.setTask(TASK_ADD_CONTACT);
|
||||
}
|
||||
} else if (action == ACK) {
|
||||
// just send ACK, don't update local state again
|
||||
@@ -132,7 +120,7 @@ public class IntroduceeEngine
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
List<Event> events = Collections.emptyList();
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(false,
|
||||
return new StateUpdate<IntroduceeSessionState, BdfDictionary>(false,
|
||||
false,
|
||||
localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
@@ -141,12 +129,11 @@ public class IntroduceeEngine
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<BdfDictionary, BdfDictionary> onMessageReceived(
|
||||
BdfDictionary localState, BdfDictionary msg) {
|
||||
public StateUpdate<IntroduceeSessionState, BdfDictionary> onMessageReceived(
|
||||
IntroduceeSessionState localState, BdfDictionary msg) {
|
||||
|
||||
try {
|
||||
IntroduceeProtocolState currentState =
|
||||
getState(localState.getLong(STATE));
|
||||
IntroduceeProtocolState currentState = localState.getState();
|
||||
int type = msg.getLong(TYPE).intValue();
|
||||
IntroduceeAction action = IntroduceeAction.getRemote(type);
|
||||
IntroduceeProtocolState nextState = currentState.next(action);
|
||||
@@ -162,13 +149,12 @@ public class IntroduceeEngine
|
||||
}
|
||||
|
||||
// update local session state with next protocol state
|
||||
localState.put(STATE, nextState.getValue());
|
||||
localState.setState(nextState);
|
||||
List<BdfDictionary> messages;
|
||||
List<Event> events;
|
||||
// we received the introduction request
|
||||
if (currentState == AWAIT_REQUEST) {
|
||||
// remember the session ID used by the introducer
|
||||
localState.put(SESSION_ID, msg.getRaw(SESSION_ID));
|
||||
localState.setSessionId(new SessionId(msg.getRaw(SESSION_ID)));
|
||||
|
||||
addRequestData(localState, msg);
|
||||
messages = Collections.emptyList();
|
||||
@@ -178,29 +164,32 @@ public class IntroduceeEngine
|
||||
// we had sent our response already and now received the other one
|
||||
else if (currentState == AWAIT_RESPONSES ||
|
||||
currentState == AWAIT_REMOTE_RESPONSE) {
|
||||
|
||||
// update next state based on message content
|
||||
action = IntroduceeAction
|
||||
.getRemote(type, msg.getBoolean(ACCEPT));
|
||||
nextState = currentState.next(action);
|
||||
localState.put(STATE, nextState.getValue());
|
||||
localState.setState(nextState);
|
||||
|
||||
addResponseData(localState, msg);
|
||||
if (nextState == AWAIT_ACK) {
|
||||
localState.put(TASK, TASK_ADD_CONTACT);
|
||||
localState.setTask(TASK_ADD_CONTACT);
|
||||
}
|
||||
messages = Collections.emptyList();
|
||||
events = Collections.emptyList();
|
||||
|
||||
}
|
||||
// we already sent our ACK and now received the other one
|
||||
else if (currentState == AWAIT_ACK) {
|
||||
localState.put(TASK, TASK_ACTIVATE_CONTACT);
|
||||
localState.setTask(TASK_ACTIVATE_CONTACT);
|
||||
addAckData(localState, msg);
|
||||
messages = Collections.emptyList();
|
||||
events = Collections.emptyList();
|
||||
}
|
||||
// we are done (probably declined response), ignore & delete message
|
||||
else if (currentState == FINISHED) {
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(true,
|
||||
return new StateUpdate<IntroduceeSessionState, BdfDictionary>(
|
||||
true,
|
||||
false, localState,
|
||||
Collections.<BdfDictionary>emptyList(),
|
||||
Collections.<Event>emptyList());
|
||||
@@ -209,62 +198,59 @@ public class IntroduceeEngine
|
||||
else {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
|
||||
localState, messages, events);
|
||||
return new StateUpdate<IntroduceeSessionState, BdfDictionary>(false,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRequestData(BdfDictionary localState, BdfDictionary msg)
|
||||
throws FormatException {
|
||||
private void addRequestData(IntroduceeSessionState localState,
|
||||
BdfDictionary msg) throws FormatException {
|
||||
|
||||
localState.put(NAME, msg.getString(NAME));
|
||||
localState.put(PUBLIC_KEY, msg.getRaw(PUBLIC_KEY));
|
||||
localState.setIntroducedName(msg.getString(NAME));
|
||||
localState.setIntroducedPublicKey(msg.getRaw(PUBLIC_KEY));
|
||||
if (msg.containsKey(MSG)) {
|
||||
localState.put(MSG, msg.getString(MSG));
|
||||
localState.setMessage(msg.getString(MSG));
|
||||
}
|
||||
}
|
||||
|
||||
private void addResponseData(BdfDictionary localState, BdfDictionary msg)
|
||||
throws FormatException {
|
||||
private void addResponseData(IntroduceeSessionState localState,
|
||||
BdfDictionary msg) throws FormatException {
|
||||
|
||||
if (localState.containsKey(ACCEPT)) {
|
||||
localState.put(ACCEPT,
|
||||
localState.getBoolean(ACCEPT) && msg.getBoolean(ACCEPT));
|
||||
} else {
|
||||
localState.put(ACCEPT, msg.getBoolean(ACCEPT));
|
||||
}
|
||||
localState.put(NOT_OUR_RESPONSE, msg.getRaw(MESSAGE_ID));
|
||||
localState.setAccept(msg.getBoolean(ACCEPT));
|
||||
localState.setTheirResponseId(msg.getRaw(MESSAGE_ID));
|
||||
|
||||
if (msg.getBoolean(ACCEPT)) {
|
||||
localState.put(TIME, msg.getLong(TIME));
|
||||
localState.put(E_PUBLIC_KEY, msg.getRaw(E_PUBLIC_KEY));
|
||||
localState.put(TRANSPORT, msg.getDictionary(TRANSPORT));
|
||||
localState.setTheirTime(msg.getLong(TIME));
|
||||
localState.setTheirEphemeralPublicKey(msg.getRaw(E_PUBLIC_KEY));
|
||||
localState.setOurTransportProperties(msg.getDictionary(TRANSPORT));
|
||||
}
|
||||
}
|
||||
|
||||
private void addAckData(BdfDictionary localState, BdfDictionary msg)
|
||||
private void addAckData(IntroduceeSessionState localState,
|
||||
BdfDictionary msg)
|
||||
throws FormatException {
|
||||
|
||||
localState.put(MAC, msg.getRaw(MAC));
|
||||
localState.put(SIGNATURE, msg.getRaw(SIGNATURE));
|
||||
localState.setMac(msg.getRaw(MAC));
|
||||
localState.setSignature(msg.getRaw(SIGNATURE));
|
||||
}
|
||||
|
||||
private BdfDictionary getAckMessage(BdfDictionary localState)
|
||||
private BdfDictionary getAckMessage(IntroduceeSessionState localState)
|
||||
throws FormatException {
|
||||
|
||||
BdfDictionary m = new BdfDictionary();
|
||||
m.put(TYPE, TYPE_ACK);
|
||||
m.put(GROUP_ID, localState.getRaw(GROUP_ID));
|
||||
m.put(SESSION_ID, localState.getRaw(SESSION_ID));
|
||||
m.put(MAC, localState.getRaw(OUR_MAC));
|
||||
m.put(SIGNATURE, localState.getRaw(OUR_SIGNATURE));
|
||||
m.put(MAC, localState.getOurMac());
|
||||
m.put(SIGNATURE, localState.getOurSignature());
|
||||
m.put(GROUP_ID, localState.getIntroductionGroupId());
|
||||
m.put(SESSION_ID, localState.getSessionId());
|
||||
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Sending ACK " + " to " +
|
||||
localState.getString(INTRODUCER) + " for " +
|
||||
localState.getString(NAME) + " with session ID " +
|
||||
localState.getIntroducerName() + " for " +
|
||||
localState.getIntroducedName() +
|
||||
" with session ID " +
|
||||
Arrays.hashCode(m.getRaw(SESSION_ID)) + " in group " +
|
||||
Arrays.hashCode(m.getRaw(GROUP_ID)));
|
||||
}
|
||||
@@ -272,20 +258,21 @@ public class IntroduceeEngine
|
||||
}
|
||||
|
||||
private void logAction(IntroduceeProtocolState state,
|
||||
BdfDictionary localState, BdfDictionary msg) {
|
||||
IntroduceeSessionState localState, BdfDictionary msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
try {
|
||||
LOG.info("Sending " +
|
||||
(localState.getBoolean(ACCEPT) ? "accept " : "decline ") +
|
||||
(localState.wasLocallyAccepted() ? "accept " : "decline ") +
|
||||
"response in state " + state.name() +
|
||||
" to " + localState.getString(INTRODUCER) +
|
||||
" for " + localState.getString(NAME) + " with session ID " +
|
||||
" to " + localState.getIntroducerName() +
|
||||
" for " + localState.getIntroducedName() +
|
||||
" with session ID " +
|
||||
Arrays.hashCode(msg.getRaw(SESSION_ID)) + " in group " +
|
||||
Arrays.hashCode(msg.getRaw(GROUP_ID)) + ". " +
|
||||
"Moving on to state " +
|
||||
getState(localState.getLong(STATE)).name()
|
||||
localState.getState().name()
|
||||
);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
@@ -293,8 +280,8 @@ public class IntroduceeEngine
|
||||
}
|
||||
|
||||
private void logMessageReceived(IntroduceeProtocolState currentState,
|
||||
IntroduceeProtocolState nextState, BdfDictionary localState,
|
||||
int type, BdfDictionary msg) {
|
||||
IntroduceeProtocolState nextState,
|
||||
IntroduceeSessionState localState, int type, BdfDictionary msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
@@ -306,9 +293,10 @@ public class IntroduceeEngine
|
||||
else if (type == TYPE_ABORT) t = "Abort";
|
||||
|
||||
LOG.info("Received " + t + " in state " + currentState.name() +
|
||||
" from " + localState.getString(INTRODUCER) +
|
||||
(localState.containsKey(NAME) ?
|
||||
" related to " + localState.getString(NAME) : "") +
|
||||
" from " + localState.getIntroducerName() +
|
||||
(localState.getIntroducedName() != null ?
|
||||
" related to " + localState.getIntroducedName() :
|
||||
"") +
|
||||
" with session ID " +
|
||||
Arrays.hashCode(msg.getRaw(SESSION_ID)) + " in group " +
|
||||
Arrays.hashCode(msg.getRaw(GROUP_ID)) + ". " +
|
||||
@@ -320,8 +308,8 @@ public class IntroduceeEngine
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<BdfDictionary, BdfDictionary> onMessageDelivered(
|
||||
BdfDictionary localState, BdfDictionary delivered) {
|
||||
public StateUpdate<IntroduceeSessionState, BdfDictionary> onMessageDelivered(
|
||||
IntroduceeSessionState localState, BdfDictionary delivered) {
|
||||
try {
|
||||
return noUpdate(localState);
|
||||
} catch (FormatException e) {
|
||||
@@ -330,25 +318,18 @@ public class IntroduceeEngine
|
||||
}
|
||||
}
|
||||
|
||||
private IntroduceeProtocolState getState(Long state) {
|
||||
return IntroduceeProtocolState.fromValue(state.intValue());
|
||||
}
|
||||
|
||||
private Event getEvent(BdfDictionary localState, BdfDictionary msg)
|
||||
private Event getEvent(IntroduceeSessionState localState, BdfDictionary msg)
|
||||
throws FormatException {
|
||||
|
||||
ContactId contactId =
|
||||
new ContactId(localState.getLong(CONTACT_ID_1).intValue());
|
||||
AuthorId authorId = new AuthorId(localState.getRaw(REMOTE_AUTHOR_ID));
|
||||
|
||||
SessionId sessionId = new SessionId(localState.getRaw(SESSION_ID));
|
||||
ContactId contactId = localState.getIntroducerId();
|
||||
AuthorId authorId = localState.getRemoteAuthorId();
|
||||
SessionId sessionId = localState.getSessionId();
|
||||
MessageId messageId = new MessageId(msg.getRaw(MESSAGE_ID));
|
||||
long time = msg.getLong(MESSAGE_TIME);
|
||||
String name = msg.getString(NAME);
|
||||
String message = msg.getOptionalString(MSG);
|
||||
boolean exists = localState.getBoolean(EXISTS);
|
||||
boolean introducesOtherIdentity =
|
||||
localState.getBoolean(REMOTE_AUTHOR_IS_US);
|
||||
boolean exists = localState.getContactExists();
|
||||
boolean introducesOtherIdentity = localState.getRemoteAuthorIsUs();
|
||||
|
||||
IntroductionRequest ir = new IntroductionRequest(sessionId, messageId,
|
||||
ROLE_INTRODUCEE, time, false, false, false, false, authorId,
|
||||
@@ -356,39 +337,40 @@ public class IntroduceeEngine
|
||||
return new IntroductionRequestReceivedEvent(contactId, ir);
|
||||
}
|
||||
|
||||
private StateUpdate<BdfDictionary, BdfDictionary> abortSession(
|
||||
IntroduceeProtocolState currentState, BdfDictionary localState)
|
||||
throws FormatException {
|
||||
private StateUpdate<IntroduceeSessionState, BdfDictionary> abortSession(
|
||||
IntroduceeProtocolState currentState,
|
||||
IntroduceeSessionState localState) throws FormatException {
|
||||
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Aborting protocol session " +
|
||||
Arrays.hashCode(localState.getRaw(SESSION_ID)) +
|
||||
" in state " + currentState.name());
|
||||
localState.getSessionId().hashCode() +
|
||||
" in state " + currentState.name()
|
||||
);
|
||||
}
|
||||
|
||||
localState.put(STATE, ERROR.getValue());
|
||||
localState.put(TASK, TASK_ABORT);
|
||||
localState.setState(ERROR);
|
||||
localState.setTask(TASK_ABORT);
|
||||
BdfDictionary msg = new BdfDictionary();
|
||||
msg.put(TYPE, TYPE_ABORT);
|
||||
msg.put(GROUP_ID, localState.getRaw(GROUP_ID));
|
||||
msg.put(SESSION_ID, localState.getRaw(SESSION_ID));
|
||||
msg.put(GROUP_ID, localState.getIntroductionGroupId());
|
||||
msg.put(SESSION_ID, localState.getSessionId());
|
||||
List<BdfDictionary> messages = Collections.singletonList(msg);
|
||||
|
||||
// send abort event
|
||||
ContactId contactId =
|
||||
new ContactId(localState.getLong(CONTACT_ID_1).intValue());
|
||||
SessionId sessionId = new SessionId(localState.getRaw(SESSION_ID));
|
||||
ContactId contactId = localState.getIntroducerId();
|
||||
SessionId sessionId = localState.getSessionId();
|
||||
Event event = new IntroductionAbortedEvent(contactId, sessionId);
|
||||
List<Event> events = Collections.singletonList(event);
|
||||
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
|
||||
localState, messages, events);
|
||||
return new StateUpdate<IntroduceeSessionState, BdfDictionary>(false,
|
||||
false, localState, messages, events);
|
||||
}
|
||||
|
||||
private StateUpdate<BdfDictionary, BdfDictionary> noUpdate(
|
||||
BdfDictionary localState) throws FormatException {
|
||||
private StateUpdate<IntroduceeSessionState, BdfDictionary> noUpdate(
|
||||
IntroduceeSessionState localState) throws FormatException {
|
||||
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
|
||||
return new StateUpdate<IntroduceeSessionState, BdfDictionary>(false,
|
||||
false,
|
||||
localState, Collections.<BdfDictionary>emptyList(),
|
||||
Collections.<Event>emptyList());
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.briarproject.api.Bytes;
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.TransportId;
|
||||
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;
|
||||
@@ -44,44 +45,16 @@ import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ADDED_CONTACT_ID;
|
||||
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.MAC;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.MAC_KEY;
|
||||
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.NONCE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_MAC;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_PRIVATE_KEY;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_PUBLIC_KEY;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_SIGNATURE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_TIME;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_TRANSPORT;
|
||||
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.SIGNATURE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TASK;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.NO_TASK;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TASK_ABORT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TASK_ACTIVATE_CONTACT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TASK_ADD_CONTACT;
|
||||
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_ABORT;
|
||||
@@ -125,8 +98,9 @@ class IntroduceeManager {
|
||||
this.introductionGroupFactory = introductionGroupFactory;
|
||||
}
|
||||
|
||||
public BdfDictionary initialize(Transaction txn, GroupId groupId,
|
||||
BdfDictionary message) throws DbException, FormatException {
|
||||
public IntroduceeSessionState initialize(Transaction txn,
|
||||
SessionId sessionId, GroupId groupId, BdfDictionary message)
|
||||
throws DbException, FormatException {
|
||||
|
||||
// create local message to keep engine state
|
||||
long now = clock.currentTimeMillis();
|
||||
@@ -145,16 +119,12 @@ class IntroduceeManager {
|
||||
new ContactId(gd.getLong(CONTACT).intValue());
|
||||
Contact introducer = db.getContact(txn, introducerId);
|
||||
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put(STORAGE_ID, storageId);
|
||||
d.put(STATE, AWAIT_REQUEST.getValue());
|
||||
d.put(ROLE, ROLE_INTRODUCEE);
|
||||
d.put(GROUP_ID, groupId);
|
||||
d.put(INTRODUCER, introducer.getAuthor().getName());
|
||||
d.put(CONTACT_ID_1, introducer.getId().getInt());
|
||||
d.put(LOCAL_AUTHOR_ID, introducer.getLocalAuthorId().getBytes());
|
||||
d.put(NOT_OUR_RESPONSE, storageId);
|
||||
d.put(ANSWERED, false);
|
||||
IntroduceeSessionState localState =
|
||||
new IntroduceeSessionState(storageId,
|
||||
sessionId, groupId, introducer.getId(),
|
||||
introducer.getAuthor().getId(),
|
||||
introducer.getAuthor().getName(),
|
||||
introducer.getLocalAuthorId(), AWAIT_REQUEST);
|
||||
|
||||
// check if the contact we are introduced to does already exist
|
||||
AuthorId remoteAuthorId = authorFactory
|
||||
@@ -162,11 +132,12 @@ class IntroduceeManager {
|
||||
message.getRaw(PUBLIC_KEY)).getId();
|
||||
boolean exists = contactManager.contactExists(txn, remoteAuthorId,
|
||||
introducer.getLocalAuthorId());
|
||||
d.put(EXISTS, exists);
|
||||
d.put(REMOTE_AUTHOR_ID, remoteAuthorId);
|
||||
localState.setContactExists(exists);
|
||||
localState.setRemoteAuthorId(remoteAuthorId);
|
||||
localState.setIntroducedPublicKey(message.getRaw(PUBLIC_KEY));
|
||||
|
||||
// check if someone is trying to introduce us to ourselves
|
||||
if(remoteAuthorId.equals(introducer.getLocalAuthorId())) {
|
||||
if (remoteAuthorId.equals(introducer.getLocalAuthorId())) {
|
||||
LOG.warning("Received Introduction Request to Ourselves");
|
||||
throw new FormatException();
|
||||
}
|
||||
@@ -174,40 +145,40 @@ class IntroduceeManager {
|
||||
// check if remote author is actually one of our other identities
|
||||
boolean introducesOtherIdentity =
|
||||
db.containsLocalAuthor(txn, remoteAuthorId);
|
||||
d.put(REMOTE_AUTHOR_IS_US, introducesOtherIdentity);
|
||||
localState.setRemoteAuthorIsUs(introducesOtherIdentity);
|
||||
|
||||
// save local state to database
|
||||
clientHelper.addLocalMessage(txn, localMsg, d, false);
|
||||
clientHelper.addLocalMessage(txn, localMsg,
|
||||
localState.toBdfDictionary(), false);
|
||||
|
||||
return d;
|
||||
return localState;
|
||||
}
|
||||
|
||||
public void incomingMessage(Transaction txn, BdfDictionary state,
|
||||
public void incomingMessage(Transaction txn, IntroduceeSessionState state,
|
||||
BdfDictionary message) throws DbException, FormatException {
|
||||
|
||||
IntroduceeEngine engine = new IntroduceeEngine();
|
||||
processStateUpdate(txn, message, engine.onMessageReceived(state, message));
|
||||
processStateUpdate(txn, message,
|
||||
engine.onMessageReceived(state, message));
|
||||
}
|
||||
|
||||
public void acceptIntroduction(Transaction txn, BdfDictionary state,
|
||||
final long timestamp)
|
||||
void acceptIntroduction(Transaction txn,
|
||||
IntroduceeSessionState state, final long timestamp)
|
||||
throws DbException, FormatException {
|
||||
|
||||
// get data to connect and derive a shared secret later
|
||||
long now = clock.currentTimeMillis();
|
||||
KeyPair keyPair = cryptoComponent.generateAgreementKeyPair();
|
||||
byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||
byte[] privateKey = keyPair.getPrivate().getEncoded();
|
||||
Map<TransportId, TransportProperties> transportProperties =
|
||||
transportPropertyManager.getLocalProperties(txn);
|
||||
BdfDictionary tp = encodeTransportProperties(transportProperties);
|
||||
|
||||
// update session state for later
|
||||
state.put(ACCEPT, true);
|
||||
state.put(OUR_TIME, now);
|
||||
state.put(OUR_PUBLIC_KEY, publicKey);
|
||||
state.put(OUR_PRIVATE_KEY, privateKey);
|
||||
state.put(OUR_TRANSPORT, tp);
|
||||
state.setAccept(true);
|
||||
state.setOurTime(now);
|
||||
state.setOurPrivateKey(keyPair.getPrivate().getEncoded());
|
||||
state.setOurPublicKey(keyPair.getPublic().getEncoded());
|
||||
state.setOurTransport(tp);
|
||||
|
||||
// define action
|
||||
BdfDictionary localAction = new BdfDictionary();
|
||||
@@ -220,12 +191,12 @@ class IntroduceeManager {
|
||||
processStateUpdate(txn, null, engine.onLocalAction(state, localAction));
|
||||
}
|
||||
|
||||
public void declineIntroduction(Transaction txn, BdfDictionary state,
|
||||
final long timestamp)
|
||||
void declineIntroduction(Transaction txn,
|
||||
IntroduceeSessionState state, final long timestamp)
|
||||
throws DbException, FormatException {
|
||||
|
||||
// update session state
|
||||
state.put(ACCEPT, false);
|
||||
state.setAccept(false);
|
||||
|
||||
// define action
|
||||
BdfDictionary localAction = new BdfDictionary();
|
||||
@@ -239,16 +210,16 @@ class IntroduceeManager {
|
||||
}
|
||||
|
||||
private void processStateUpdate(Transaction txn, BdfDictionary msg,
|
||||
IntroduceeEngine.StateUpdate<BdfDictionary, BdfDictionary>
|
||||
IntroduceeEngine.StateUpdate<IntroduceeSessionState, BdfDictionary>
|
||||
result) throws DbException, FormatException {
|
||||
|
||||
// perform actions based on new local state
|
||||
performTasks(txn, result.localState);
|
||||
|
||||
// save new local state
|
||||
MessageId storageId =
|
||||
new MessageId(result.localState.getRaw(STORAGE_ID));
|
||||
clientHelper.mergeMessageMetadata(txn, storageId, result.localState);
|
||||
MessageId storageId = result.localState.getStorageId();
|
||||
clientHelper.mergeMessageMetadata(txn, storageId,
|
||||
result.localState.toBdfDictionary());
|
||||
|
||||
// send messages
|
||||
for (BdfDictionary d : result.toSend) {
|
||||
@@ -271,18 +242,18 @@ class IntroduceeManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void performTasks(Transaction txn, BdfDictionary localState)
|
||||
private void performTasks(Transaction txn,
|
||||
IntroduceeSessionState localState)
|
||||
throws FormatException, DbException {
|
||||
|
||||
if (!localState.containsKey(TASK) || localState.get(TASK) == NULL_VALUE)
|
||||
return;
|
||||
long task = localState.getTask();
|
||||
if (task == NO_TASK) return;
|
||||
|
||||
// remember task and remove it from localState
|
||||
long task = localState.getLong(TASK);
|
||||
localState.put(TASK, NULL_VALUE);
|
||||
localState.setTask(NO_TASK);
|
||||
|
||||
if (task == TASK_ADD_CONTACT) {
|
||||
if (localState.getBoolean(EXISTS)) {
|
||||
if (localState.getContactExists()) {
|
||||
// we have this contact already, so do not perform actions
|
||||
LOG.info("We have this contact already, do not add");
|
||||
return;
|
||||
@@ -296,11 +267,10 @@ class IntroduceeManager {
|
||||
PublicKey publicKey;
|
||||
PrivateKey privateKey;
|
||||
try {
|
||||
publicKeyBytes = localState.getRaw(OUR_PUBLIC_KEY);
|
||||
publicKey = keyParser
|
||||
.parsePublicKey(publicKeyBytes);
|
||||
publicKeyBytes = localState.getOurPublicKey();
|
||||
publicKey = keyParser.parsePublicKey(publicKeyBytes);
|
||||
privateKey = keyParser.parsePrivateKey(
|
||||
localState.getRaw(OUR_PRIVATE_KEY));
|
||||
localState.getOurPrivateKey());
|
||||
} catch (GeneralSecurityException e) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
@@ -308,11 +278,13 @@ class IntroduceeManager {
|
||||
// we can not continue without the keys
|
||||
throw new RuntimeException("Our own ephemeral key is invalid");
|
||||
}
|
||||
KeyPair keyPair = new KeyPair(publicKey, privateKey);
|
||||
byte[] theirEphemeralKey = localState.getRaw(E_PUBLIC_KEY);
|
||||
|
||||
KeyPair ourEphemeralKeyPair;
|
||||
ourEphemeralKeyPair = new KeyPair(publicKey, privateKey);
|
||||
byte[] theirEphemeralKey = localState.getTheirEphemeralPublicKey();
|
||||
|
||||
// figure out who takes which role by comparing public keys
|
||||
int comp = Bytes.COMPARATOR.compare(new Bytes(publicKeyBytes),
|
||||
int comp = new Bytes(publicKeyBytes).compareTo(
|
||||
new Bytes(theirEphemeralKey));
|
||||
boolean alice = comp < 0;
|
||||
|
||||
@@ -321,7 +293,8 @@ class IntroduceeManager {
|
||||
SecretKey secretKey;
|
||||
try {
|
||||
secretKey = cryptoComponent
|
||||
.deriveMasterSecret(theirEphemeralKey, keyPair, alice);
|
||||
.deriveMasterSecret(theirEphemeralKey,
|
||||
ourEphemeralKeyPair, alice);
|
||||
} catch (GeneralSecurityException e) {
|
||||
// we can not continue without the shared secret
|
||||
throw new DbException(e);
|
||||
@@ -337,12 +310,11 @@ class IntroduceeManager {
|
||||
cryptoComponent.deriveMacKey(secretKey, !alice);
|
||||
|
||||
// Save the other nonce and MAC key for the verification
|
||||
localState.put(NONCE, theirNonce);
|
||||
localState.put(MAC_KEY, theirMacKey.getBytes());
|
||||
localState.setTheirNonce(theirNonce);
|
||||
localState.setTheirMacKey(theirMacKey.getBytes());
|
||||
|
||||
// Sign our nonce with our long-term identity public key
|
||||
AuthorId localAuthorId =
|
||||
new AuthorId(localState.getRaw(LOCAL_AUTHOR_ID));
|
||||
AuthorId localAuthorId = localState.getLocalAuthorId();
|
||||
LocalAuthor author =
|
||||
identityManager.getLocalAuthor(txn, localAuthorId);
|
||||
Signature signature = cryptoComponent.getSignature();
|
||||
@@ -358,44 +330,45 @@ class IntroduceeManager {
|
||||
signature.update(ourNonce);
|
||||
byte[] sig = signature.sign();
|
||||
|
||||
|
||||
// The agreed timestamp is the minimum of the peers' timestamps
|
||||
long ourTime = localState.getLong(OUR_TIME);
|
||||
long theirTime = localState.getLong(TIME);
|
||||
long ourTime = localState.getOurTime();
|
||||
long theirTime = localState.getTheirTime();
|
||||
long timestamp = Math.min(ourTime, theirTime);
|
||||
|
||||
// Calculate a MAC over identity public key, ephemeral public key,
|
||||
// transport properties and timestamp.
|
||||
BdfDictionary tp = localState.getDictionary(OUR_TRANSPORT);
|
||||
BdfDictionary tp = localState.getOurTransport();
|
||||
BdfList toSignList = BdfList.of(author.getPublicKey(),
|
||||
publicKeyBytes, tp, ourTime);
|
||||
byte[] toSign = clientHelper.toByteArray(toSignList);
|
||||
byte[] mac = cryptoComponent.mac(macKey, toSign);
|
||||
|
||||
// Add MAC and signature to localState, so it can be included in ACK
|
||||
localState.put(OUR_MAC, mac);
|
||||
localState.put(OUR_SIGNATURE, sig);
|
||||
localState.setOurMac(mac);
|
||||
localState.setOurSignature(sig);
|
||||
|
||||
// Add the contact to the database as inactive
|
||||
Author remoteAuthor = authorFactory
|
||||
.createAuthor(localState.getString(NAME),
|
||||
localState.getRaw(PUBLIC_KEY));
|
||||
.createAuthor(localState.getIntroducedName(),
|
||||
localState.getIntroducedPublicKey());
|
||||
ContactId contactId = contactManager
|
||||
.addContact(txn, remoteAuthor, localAuthorId, secretKey,
|
||||
timestamp, alice, false, false);
|
||||
|
||||
// Update local state with ContactId, so we know what to activate
|
||||
localState.put(ADDED_CONTACT_ID, contactId.getInt());
|
||||
localState.setIntroducedId(contactId);
|
||||
|
||||
// let the transport manager know how to connect to the contact
|
||||
Map<TransportId, TransportProperties> transportProperties =
|
||||
parseTransportProperties(localState);
|
||||
parseTransportProperties(localState.toBdfDictionary());
|
||||
transportPropertyManager.addRemoteProperties(txn, contactId,
|
||||
transportProperties);
|
||||
|
||||
// delete the ephemeral private key by overwriting with NULL value
|
||||
// this ensures future ephemeral keys can not be recovered when
|
||||
// this device should gets compromised
|
||||
localState.put(OUR_PRIVATE_KEY, NULL_VALUE);
|
||||
localState.clearOurKeyPair();
|
||||
|
||||
// define next action: Send ACK
|
||||
BdfDictionary localAction = new BdfDictionary();
|
||||
@@ -409,18 +382,19 @@ class IntroduceeManager {
|
||||
|
||||
// we sent and received an ACK, so activate contact
|
||||
if (task == TASK_ACTIVATE_CONTACT) {
|
||||
if (!localState.getBoolean(EXISTS) &&
|
||||
localState.containsKey(ADDED_CONTACT_ID)) {
|
||||
if (!localState.getContactExists() &&
|
||||
localState.getIntroducedId() != null) {
|
||||
|
||||
LOG.info("Verifying Signature...");
|
||||
|
||||
byte[] nonce = localState.getRaw(NONCE);
|
||||
byte[] sig = localState.getRaw(SIGNATURE);
|
||||
byte[] keyBytes = localState.getRaw(PUBLIC_KEY);
|
||||
byte[] nonce = localState.getTheirNonce();
|
||||
byte[] sig = localState.getSignature();
|
||||
byte[] introducedPubKey = localState.getIntroducedPublicKey();
|
||||
try {
|
||||
// Parse the public key
|
||||
KeyParser keyParser = cryptoComponent.getSignatureKeyParser();
|
||||
PublicKey key = keyParser.parsePublicKey(keyBytes);
|
||||
KeyParser keyParser =
|
||||
cryptoComponent.getSignatureKeyParser();
|
||||
PublicKey key = keyParser.parsePublicKey(introducedPubKey);
|
||||
// Verify the signature
|
||||
Signature signature = cryptoComponent.getSignature();
|
||||
signature.initVerify(key);
|
||||
@@ -439,16 +413,16 @@ class IntroduceeManager {
|
||||
LOG.info("Verifying MAC...");
|
||||
|
||||
// get MAC and MAC key from session state
|
||||
byte[] mac = localState.getRaw(MAC);
|
||||
byte[] macKeyBytes = localState.getRaw(MAC_KEY);
|
||||
byte[] mac = localState.getMac();
|
||||
byte[] macKeyBytes = localState.getTheirMacKey();
|
||||
SecretKey macKey = new SecretKey(macKeyBytes);
|
||||
|
||||
// get MAC data and calculate a new MAC with stored key
|
||||
byte[] pubKey = localState.getRaw(PUBLIC_KEY);
|
||||
byte[] ePubKey = localState.getRaw(E_PUBLIC_KEY);
|
||||
BdfDictionary tp = localState.getDictionary(TRANSPORT);
|
||||
long timestamp = localState.getLong(TIME);
|
||||
BdfList toSignList = BdfList.of(pubKey, ePubKey, tp, timestamp);
|
||||
byte[] ePubKey = localState.getTheirEphemeralPublicKey();
|
||||
BdfDictionary tp = localState.getOurTransportProperties();
|
||||
long timestamp = localState.getTheirTime();
|
||||
BdfList toSignList = BdfList.of(introducedPubKey, ePubKey, tp,
|
||||
timestamp);
|
||||
byte[] toSign = clientHelper.toByteArray(toSignList);
|
||||
byte[] calculatedMac = cryptoComponent.mac(macKey, toSign);
|
||||
if (!Arrays.equals(mac, calculatedMac)) {
|
||||
@@ -458,8 +432,7 @@ class IntroduceeManager {
|
||||
|
||||
LOG.info("Activating Contact...");
|
||||
|
||||
ContactId contactId = new ContactId(
|
||||
localState.getLong(ADDED_CONTACT_ID).intValue());
|
||||
ContactId contactId = localState.getIntroducedId();
|
||||
|
||||
// activate and show contact in contact list
|
||||
contactManager.setContactActive(txn, contactId, true);
|
||||
@@ -476,17 +449,16 @@ class IntroduceeManager {
|
||||
|
||||
// we need to abort the protocol, clean up what has been done
|
||||
if (task == TASK_ABORT) {
|
||||
if (localState.containsKey(ADDED_CONTACT_ID)) {
|
||||
if (localState.getIntroducedId() != null) {
|
||||
LOG.info("Deleting added contact due to abort...");
|
||||
ContactId contactId = new ContactId(
|
||||
localState.getLong(ADDED_CONTACT_ID).intValue());
|
||||
ContactId contactId = localState.getIntroducedId();
|
||||
contactManager.removeContact(txn, contactId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void abort(Transaction txn, BdfDictionary state) {
|
||||
public void abort(Transaction txn, IntroduceeSessionState state) {
|
||||
|
||||
IntroduceeEngine engine = new IntroduceeEngine();
|
||||
BdfDictionary localAction = new BdfDictionary();
|
||||
|
||||
@@ -0,0 +1,563 @@
|
||||
package org.briarproject.introduction;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.introduction.IntroduceeProtocolState;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ADDED_CONTACT_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_2;
|
||||
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.EXISTS;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.MSG;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.SIGNATURE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.MAC;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_IS_US;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TASK;
|
||||
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.NAME;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.NONCE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.NO_TASK;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_PRIVATE_KEY;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_PUBLIC_KEY;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_TIME;
|
||||
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.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.OUR_TRANSPORT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_SIGNATURE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_MAC;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.MAC_KEY;
|
||||
|
||||
// This class is not thread-safe
|
||||
class IntroduceeSessionState extends IntroductionState {
|
||||
|
||||
private IntroduceeProtocolState state;
|
||||
|
||||
private final ContactId introducerId;
|
||||
private final AuthorId introducerAuthorId;
|
||||
private final String introducerName;
|
||||
|
||||
private AuthorId localAuthorId;
|
||||
|
||||
private long ourTime;
|
||||
private long theirTime;
|
||||
|
||||
private byte[] ourPrivateKey;
|
||||
private byte[] ourPublicKey;
|
||||
private byte[] introducedPublicKey;
|
||||
private byte[] theirEphemeralPublicKey;
|
||||
|
||||
private byte[] mac;
|
||||
private byte[] signature;
|
||||
private byte[] ourMac;
|
||||
private byte[] ourSignature;
|
||||
private BdfDictionary ourTransport;
|
||||
private byte[] theirNonce;
|
||||
private byte[] theirMacKey;
|
||||
|
||||
private int task;
|
||||
|
||||
private String message;
|
||||
private BdfDictionary ourTransportProperties;
|
||||
// FIXME should not be a dictionary
|
||||
|
||||
private boolean answered;
|
||||
private boolean accept;
|
||||
private boolean accepted;
|
||||
|
||||
private boolean contactAlreadyExists;
|
||||
|
||||
private byte[] otherResponseId;
|
||||
|
||||
private AuthorId remoteAuthorId;
|
||||
private boolean remoteAuthorIsUs;
|
||||
|
||||
private String introducedName;
|
||||
private GroupId introductionGroupId;
|
||||
private ContactId introducedId;
|
||||
// private String introducedName;
|
||||
private AuthorId introducedAuthorId;
|
||||
|
||||
IntroduceeSessionState(MessageId storageId, SessionId sessionId,
|
||||
GroupId groupId,
|
||||
ContactId introducerId, AuthorId introducerAuthorId,
|
||||
String introducerName, AuthorId introducerLocalAuthorId,
|
||||
IntroduceeProtocolState state) {
|
||||
|
||||
super(sessionId, storageId);
|
||||
|
||||
this.introducerName = introducerName;
|
||||
this.introducerId = introducerId;
|
||||
this.introducerAuthorId = introducerAuthorId;
|
||||
this.otherResponseId = sessionId.getBytes();
|
||||
this.localAuthorId = introducerLocalAuthorId;
|
||||
this.state = state;
|
||||
this.answered = false;
|
||||
this.accept = false;
|
||||
this.accepted = false;
|
||||
this.contactAlreadyExists = false;
|
||||
this.otherResponseId = null;
|
||||
this.task = NO_TASK;
|
||||
this.ourTransportProperties = null;
|
||||
this.introductionGroupId = groupId;
|
||||
|
||||
}
|
||||
|
||||
BdfDictionary toBdfDictionary() {
|
||||
BdfDictionary d = super.toBdfDictionary();
|
||||
d.put(ROLE, ROLE_INTRODUCEE);
|
||||
d.put(STATE, getState().getValue());
|
||||
d.put(INTRODUCER, introducerName);
|
||||
d.put(ANSWERED, answered);
|
||||
d.put(REMOTE_AUTHOR_IS_US, remoteAuthorIsUs);
|
||||
|
||||
if (message != null)
|
||||
d.put(MSG, message);
|
||||
|
||||
if (accepted)
|
||||
d.put(ACCEPT, accept);
|
||||
|
||||
if (introducedId != null)
|
||||
d.put(ADDED_CONTACT_ID, introducedId.getInt());
|
||||
|
||||
d.put(GROUP_ID, introductionGroupId);
|
||||
|
||||
d.put(AUTHOR_ID_1, introducerAuthorId);
|
||||
d.put(CONTACT_1, introducerName);
|
||||
d.put(CONTACT_ID_1, introducerId.getInt());
|
||||
|
||||
if (introducedAuthorId != null) {
|
||||
d.put(AUTHOR_ID_2, introducedAuthorId);
|
||||
d.put(CONTACT_ID_2, introducedId);
|
||||
}
|
||||
// TODO check if we really need three names and what this introducedName refers to
|
||||
if (introducedName != null) d.put(NAME, introducedName);
|
||||
|
||||
if (remoteAuthorId != null)
|
||||
d.put(REMOTE_AUTHOR_ID, remoteAuthorId);
|
||||
|
||||
d.put(LOCAL_AUTHOR_ID, localAuthorId);
|
||||
|
||||
if (ourTransportProperties != null)
|
||||
d.put(TRANSPORT, ourTransportProperties);
|
||||
|
||||
if (ourPublicKey != null)
|
||||
d.put(OUR_PUBLIC_KEY, ourPublicKey);
|
||||
|
||||
if (ourPrivateKey != null)
|
||||
d.put(OUR_PRIVATE_KEY, ourPrivateKey);
|
||||
else
|
||||
d.put(OUR_PRIVATE_KEY, BdfDictionary.NULL_VALUE);
|
||||
|
||||
if (theirEphemeralPublicKey != null)
|
||||
d.put(E_PUBLIC_KEY, theirEphemeralPublicKey);
|
||||
else
|
||||
d.put(E_PUBLIC_KEY, BdfDictionary.NULL_VALUE);
|
||||
|
||||
if (introducedPublicKey != null)
|
||||
d.put(PUBLIC_KEY, introducedPublicKey);
|
||||
|
||||
if (otherResponseId != null)
|
||||
d.put(NOT_OUR_RESPONSE, getOtherResponseId());
|
||||
|
||||
if (mac != null)
|
||||
d.put(MAC, mac);
|
||||
|
||||
if (signature != null)
|
||||
d.put(SIGNATURE, signature);
|
||||
|
||||
if (ourMac != null)
|
||||
d.put(OUR_MAC, ourMac);
|
||||
|
||||
if (ourSignature != null)
|
||||
d.put(OUR_SIGNATURE, ourSignature);
|
||||
|
||||
if (ourTransport != null)
|
||||
d.put(OUR_TRANSPORT, ourTransport);
|
||||
|
||||
if (theirNonce != null)
|
||||
d.put(NONCE, theirNonce);
|
||||
|
||||
if (theirMacKey != null)
|
||||
d.put(MAC_KEY, theirMacKey);
|
||||
|
||||
d.put(TIME, theirTime);
|
||||
d.put(OUR_TIME, ourTime);
|
||||
d.put(EXISTS, contactAlreadyExists);
|
||||
d.put(TASK, task);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static IntroduceeSessionState fromBdfDictionary(BdfDictionary d)
|
||||
throws FormatException {
|
||||
|
||||
if (d.getLong(ROLE).intValue() != ROLE_INTRODUCEE)
|
||||
throw new FormatException();
|
||||
|
||||
MessageId storageId = new MessageId(d.getRaw(STORAGE_ID));
|
||||
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
|
||||
|
||||
GroupId groupId = new GroupId(d.getRaw(GROUP_ID));
|
||||
|
||||
AuthorId authorId1 = new AuthorId(d.getRaw(AUTHOR_ID_1));
|
||||
String introducerName = d.getString(INTRODUCER);
|
||||
ContactId introducerId =
|
||||
new ContactId(d.getLong(CONTACT_ID_1).intValue());
|
||||
AuthorId introducerLocalAuthorId =
|
||||
new AuthorId(d.getRaw(LOCAL_AUTHOR_ID));
|
||||
|
||||
int stateNumber = d.getLong(STATE).intValue();
|
||||
IntroduceeProtocolState state =
|
||||
IntroduceeProtocolState.fromValue(stateNumber);
|
||||
|
||||
IntroduceeSessionState sessionState =
|
||||
new IntroduceeSessionState(storageId,
|
||||
sessionId, groupId, introducerId, authorId1,
|
||||
introducerName, introducerLocalAuthorId, state);
|
||||
|
||||
if (d.containsKey(AUTHOR_ID_2)) {
|
||||
sessionState
|
||||
.setIntroducedAuthorId(new AuthorId(d.getRaw(AUTHOR_ID_2)));
|
||||
sessionState.setIntroducedId(
|
||||
new ContactId(d.getLong(CONTACT_ID_2).intValue()));
|
||||
}
|
||||
|
||||
if (d.containsKey(REMOTE_AUTHOR_ID))
|
||||
sessionState.setRemoteAuthorId(
|
||||
new AuthorId(d.getRaw(REMOTE_AUTHOR_ID)));
|
||||
|
||||
if (d.containsKey(TRANSPORT))
|
||||
sessionState.setOurTransportProperties(d.getDictionary(TRANSPORT));
|
||||
|
||||
if (d.containsKey(OUR_PUBLIC_KEY))
|
||||
sessionState.ourPublicKey = d.getRaw(OUR_PUBLIC_KEY);
|
||||
|
||||
if (d.containsKey(OUR_PRIVATE_KEY)&&
|
||||
d.get(OUR_PRIVATE_KEY) != BdfDictionary.NULL_VALUE)
|
||||
sessionState.ourPrivateKey = d.getRaw(OUR_PRIVATE_KEY);
|
||||
|
||||
if (d.containsKey(E_PUBLIC_KEY) &&
|
||||
d.get(E_PUBLIC_KEY) != BdfDictionary.NULL_VALUE)
|
||||
sessionState.theirEphemeralPublicKey = d.getRaw(E_PUBLIC_KEY);
|
||||
|
||||
if (d.containsKey(PUBLIC_KEY))
|
||||
sessionState.setIntroducedPublicKey(d.getRaw(PUBLIC_KEY));
|
||||
|
||||
if (d.containsKey(ACCEPT))
|
||||
sessionState.setAccept(d.getBoolean(ACCEPT));
|
||||
|
||||
if (d.containsKey(NOT_OUR_RESPONSE))
|
||||
sessionState.setTheirResponseId(d.getRaw(NOT_OUR_RESPONSE));
|
||||
|
||||
if (d.containsKey(MAC))
|
||||
sessionState.setMac(d.getRaw(MAC));
|
||||
|
||||
if (d.containsKey(SIGNATURE))
|
||||
sessionState.setSignature(d.getRaw(SIGNATURE));
|
||||
|
||||
if (d.containsKey(OUR_TRANSPORT))
|
||||
sessionState.setOurTransport(d.getDictionary(OUR_TRANSPORT));
|
||||
|
||||
sessionState.setTheirTime(d.getLong(TIME));
|
||||
sessionState.setOurTime(d.getLong(OUR_TIME));
|
||||
|
||||
if (d.containsKey(NAME))
|
||||
sessionState.setIntroducedName(d.getString(NAME));
|
||||
|
||||
sessionState.setContactExists(d.getBoolean(EXISTS));
|
||||
sessionState.setTask(d.getLong(TASK).intValue());
|
||||
sessionState.setRemoteAuthorIsUs(d.getBoolean(REMOTE_AUTHOR_IS_US));
|
||||
|
||||
if (d.containsKey(ADDED_CONTACT_ID)) {
|
||||
ContactId introducedId =
|
||||
new ContactId(d.getLong(ADDED_CONTACT_ID).intValue());
|
||||
sessionState.setIntroducedId(introducedId);
|
||||
}
|
||||
|
||||
if (d.containsKey(ANSWERED))
|
||||
sessionState.setAnswered(d.getBoolean(ANSWERED));
|
||||
|
||||
if (d.containsKey(MSG))
|
||||
sessionState.setMessage(d.getString(MSG));
|
||||
|
||||
if (d.containsKey(NONCE))
|
||||
sessionState.setTheirNonce(d.getRaw(NONCE));
|
||||
|
||||
if (d.containsKey(MAC_KEY))
|
||||
sessionState.setTheirMacKey(d.getRaw(MAC_KEY));
|
||||
|
||||
if (d.containsKey(OUR_MAC)) {
|
||||
sessionState.setOurMac(d.getRaw(OUR_MAC));
|
||||
}
|
||||
|
||||
if (d.containsKey(OUR_SIGNATURE))
|
||||
sessionState.setOurSignature(d.getRaw(OUR_SIGNATURE));
|
||||
|
||||
|
||||
return sessionState;
|
||||
}
|
||||
|
||||
|
||||
IntroduceeProtocolState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
void setState(IntroduceeProtocolState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
boolean wasLocallyAccepted() {
|
||||
return accept;
|
||||
}
|
||||
|
||||
void setAccept(boolean accept) {
|
||||
if (accepted) {
|
||||
this.accept &= accept;
|
||||
} else {
|
||||
this.accept = accept;
|
||||
}
|
||||
accepted = true;
|
||||
}
|
||||
|
||||
void setAnswered(boolean answered) {
|
||||
this.answered = answered;
|
||||
}
|
||||
|
||||
boolean getAnswered() {
|
||||
return answered;
|
||||
}
|
||||
|
||||
long getOurTime() {
|
||||
return ourTime;
|
||||
}
|
||||
|
||||
void setOurTime(long time) {
|
||||
ourTime = time;
|
||||
}
|
||||
|
||||
long getTheirTime() {
|
||||
return theirTime;
|
||||
}
|
||||
|
||||
public void setTheirTime(long time) {
|
||||
theirTime = time;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
byte[] getTheirEphemeralPublicKey() {
|
||||
return theirEphemeralPublicKey;
|
||||
}
|
||||
|
||||
void setTheirEphemeralPublicKey(byte[] theirEphemeralPublicKey) {
|
||||
this.theirEphemeralPublicKey = theirEphemeralPublicKey;
|
||||
}
|
||||
|
||||
void setOurTransportProperties(BdfDictionary ourTransportProperties) {
|
||||
this.ourTransportProperties = ourTransportProperties;
|
||||
}
|
||||
|
||||
boolean getContactExists() {
|
||||
return contactAlreadyExists;
|
||||
}
|
||||
|
||||
void setContactExists(boolean exists) {
|
||||
this.contactAlreadyExists = exists;
|
||||
}
|
||||
|
||||
void setRemoteAuthorId(AuthorId remoteAuthorId) {
|
||||
this.remoteAuthorId = remoteAuthorId;
|
||||
}
|
||||
|
||||
AuthorId getRemoteAuthorId() {
|
||||
return remoteAuthorId;
|
||||
}
|
||||
|
||||
void setRemoteAuthorIsUs(boolean remoteAuthorIsUs) {
|
||||
this.remoteAuthorIsUs = remoteAuthorIsUs;
|
||||
}
|
||||
|
||||
boolean getRemoteAuthorIsUs() {
|
||||
return remoteAuthorIsUs;
|
||||
}
|
||||
|
||||
void clearOurKeyPair() {
|
||||
this.ourPrivateKey = null;
|
||||
this.ourPublicKey = null;
|
||||
}
|
||||
|
||||
byte[] getOtherResponseId() {
|
||||
return this.otherResponseId;
|
||||
}
|
||||
|
||||
void setTheirResponseId(byte[] otherResponse) {
|
||||
this.otherResponseId = otherResponse;
|
||||
}
|
||||
|
||||
String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
void setTask(int task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
int getTask() {
|
||||
return task;
|
||||
}
|
||||
|
||||
boolean wasLocallyAcceptedOrDeclined() {
|
||||
return accepted;
|
||||
}
|
||||
|
||||
void setIntroducedName(String introducedName) {
|
||||
this.introducedName = introducedName;
|
||||
}
|
||||
|
||||
String getIntroducedName() {
|
||||
return introducedName;
|
||||
}
|
||||
|
||||
byte[] getOurPublicKey() {
|
||||
return ourPublicKey;
|
||||
}
|
||||
|
||||
void setOurPublicKey(byte[] ourPublicKey) {
|
||||
this.ourPublicKey = ourPublicKey;
|
||||
}
|
||||
|
||||
byte[] getOurPrivateKey() {
|
||||
return ourPrivateKey;
|
||||
}
|
||||
|
||||
void setOurPrivateKey(byte[] ourPrivateKey) {
|
||||
this.ourPrivateKey = ourPrivateKey;
|
||||
}
|
||||
|
||||
GroupId getIntroductionGroupId() {
|
||||
return introductionGroupId;
|
||||
}
|
||||
|
||||
byte[] getIntroducedPublicKey() {
|
||||
return introducedPublicKey;
|
||||
}
|
||||
|
||||
void setIntroducedPublicKey(byte[] introducedPublicKey) {
|
||||
this.introducedPublicKey = introducedPublicKey;
|
||||
}
|
||||
|
||||
|
||||
ContactId getIntroducedId() {
|
||||
return introducedId;
|
||||
}
|
||||
|
||||
void setIntroducedId(
|
||||
ContactId introducedId) {
|
||||
this.introducedId = introducedId;
|
||||
}
|
||||
|
||||
void setIntroducedAuthorId(
|
||||
AuthorId introducedAuthorId) {
|
||||
this.introducedAuthorId = introducedAuthorId;
|
||||
}
|
||||
|
||||
AuthorId getLocalAuthorId() {
|
||||
return localAuthorId;
|
||||
}
|
||||
|
||||
void setLocalAuthorId(
|
||||
AuthorId localAuthorId) {
|
||||
this.localAuthorId = localAuthorId;
|
||||
}
|
||||
|
||||
ContactId getIntroducerId() {
|
||||
return introducerId;
|
||||
}
|
||||
|
||||
void setMac(byte[] mac) {
|
||||
this.mac = mac;
|
||||
}
|
||||
|
||||
byte[] getMac() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
void setSignature(byte[] signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
void setOurMac(byte[] ourMac) {
|
||||
this.ourMac = ourMac;
|
||||
}
|
||||
|
||||
byte[] getOurMac() {
|
||||
return ourMac;
|
||||
}
|
||||
|
||||
void setOurSignature(byte[] ourSignature) {
|
||||
this.ourSignature = ourSignature;
|
||||
}
|
||||
|
||||
byte[] getOurSignature() {
|
||||
return ourSignature;
|
||||
}
|
||||
|
||||
void setOurTransport(BdfDictionary ourTransport) {
|
||||
this.ourTransport = ourTransport;
|
||||
}
|
||||
|
||||
BdfDictionary getOurTransport() {
|
||||
return ourTransport;
|
||||
}
|
||||
|
||||
void setTheirNonce(byte[] theirNonce) {
|
||||
this.theirNonce = theirNonce;
|
||||
}
|
||||
|
||||
byte[] getTheirNonce() {
|
||||
return theirNonce;
|
||||
}
|
||||
|
||||
void setTheirMacKey(byte[] theirMacKey) {
|
||||
this.theirMacKey = theirMacKey;
|
||||
}
|
||||
|
||||
byte[] getTheirMacKey() {
|
||||
return this.theirMacKey;
|
||||
}
|
||||
|
||||
BdfDictionary getOurTransportProperties() {
|
||||
return ourTransportProperties;
|
||||
}
|
||||
|
||||
String getIntroducerName() {
|
||||
return introducerName;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import org.briarproject.api.introduction.IntroducerProtocolState;
|
||||
import org.briarproject.api.introduction.IntroductionResponse;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.introduction.IntroducerSessionState;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -65,18 +66,19 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUE
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
||||
|
||||
public class IntroducerEngine
|
||||
implements ProtocolEngine<BdfDictionary, BdfDictionary, BdfDictionary> {
|
||||
implements
|
||||
ProtocolEngine<BdfDictionary, IntroducerSessionState, BdfDictionary> {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(IntroducerEngine.class.getName());
|
||||
|
||||
|
||||
@Override
|
||||
public StateUpdate<BdfDictionary, BdfDictionary> onLocalAction(
|
||||
BdfDictionary localState, BdfDictionary localAction) {
|
||||
public StateUpdate<IntroducerSessionState, BdfDictionary> onLocalAction(
|
||||
IntroducerSessionState localState, BdfDictionary localAction) {
|
||||
|
||||
try {
|
||||
IntroducerProtocolState currentState =
|
||||
getState(localState.getLong(STATE));
|
||||
IntroducerProtocolState currentState = localState.getState();
|
||||
int type = localAction.getLong(TYPE).intValue();
|
||||
IntroducerAction action = IntroducerAction.getLocal(type);
|
||||
IntroducerProtocolState nextState = currentState.next(action);
|
||||
@@ -93,15 +95,15 @@ public class IntroducerEngine
|
||||
return noUpdate(localState);
|
||||
}
|
||||
|
||||
localState.put(STATE, nextState.getValue());
|
||||
localState.setState(nextState);
|
||||
if (action == LOCAL_REQUEST) {
|
||||
// create the introduction requests for both contacts
|
||||
List<BdfDictionary> messages = new ArrayList<BdfDictionary>(2);
|
||||
BdfDictionary msg1 = new BdfDictionary();
|
||||
msg1.put(TYPE, TYPE_REQUEST);
|
||||
msg1.put(SESSION_ID, localState.getRaw(SESSION_ID));
|
||||
msg1.put(GROUP_ID, localState.getRaw(GROUP_ID_1));
|
||||
msg1.put(NAME, localState.getString(CONTACT_2));
|
||||
msg1.put(SESSION_ID, localState.getSessionId());
|
||||
msg1.put(GROUP_ID, localState.getGroup1Id());
|
||||
msg1.put(NAME, localState.getContact2Name());
|
||||
msg1.put(PUBLIC_KEY, localAction.getRaw(PUBLIC_KEY2));
|
||||
if (localAction.containsKey(MSG)) {
|
||||
msg1.put(MSG, localAction.getString(MSG));
|
||||
@@ -111,9 +113,9 @@ public class IntroducerEngine
|
||||
logLocalAction(currentState, localState, msg1);
|
||||
BdfDictionary msg2 = new BdfDictionary();
|
||||
msg2.put(TYPE, TYPE_REQUEST);
|
||||
msg2.put(SESSION_ID, localState.getRaw(SESSION_ID));
|
||||
msg2.put(GROUP_ID, localState.getRaw(GROUP_ID_2));
|
||||
msg2.put(NAME, localState.getString(CONTACT_1));
|
||||
msg2.put(SESSION_ID, localState.getSessionId());
|
||||
msg2.put(GROUP_ID, localState.getGroup2Id());
|
||||
msg2.put(NAME, localState.getContact1Name());
|
||||
msg2.put(PUBLIC_KEY, localAction.getRaw(PUBLIC_KEY1));
|
||||
if (localAction.containsKey(MSG)) {
|
||||
msg2.put(MSG, localAction.getString(MSG));
|
||||
@@ -123,8 +125,8 @@ public class IntroducerEngine
|
||||
logLocalAction(currentState, localState, msg2);
|
||||
|
||||
List<Event> events = Collections.emptyList();
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
|
||||
localState, messages, events);
|
||||
return new StateUpdate<IntroducerSessionState, BdfDictionary>(
|
||||
false, false, localState, messages, events);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown Local Action");
|
||||
}
|
||||
@@ -134,12 +136,11 @@ public class IntroducerEngine
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<BdfDictionary, BdfDictionary> onMessageReceived(
|
||||
BdfDictionary localState, BdfDictionary msg) {
|
||||
public StateUpdate<IntroducerSessionState, BdfDictionary> onMessageReceived(
|
||||
IntroducerSessionState localState, BdfDictionary msg) {
|
||||
|
||||
try {
|
||||
IntroducerProtocolState currentState =
|
||||
getState(localState.getLong(STATE));
|
||||
IntroducerProtocolState currentState = localState.getState();
|
||||
int type = msg.getLong(TYPE).intValue();
|
||||
boolean one = isContact1(localState, msg);
|
||||
IntroducerAction action = IntroducerAction.getRemote(type, one);
|
||||
@@ -166,9 +167,11 @@ public class IntroducerEngine
|
||||
action = IntroducerAction
|
||||
.getRemote(type, one, msg.getBoolean(ACCEPT));
|
||||
nextState = currentState.next(action);
|
||||
localState.put(STATE, nextState.getValue());
|
||||
if (one) localState.put(RESPONSE_1, msg.getRaw(MESSAGE_ID));
|
||||
else localState.put(RESPONSE_2, msg.getRaw(MESSAGE_ID));
|
||||
localState.setState(nextState);
|
||||
if (one) localState.setResponse1(
|
||||
new MessageId(msg.getRaw(MESSAGE_ID)));
|
||||
else localState.setResponse2(
|
||||
new MessageId(msg.getRaw(MESSAGE_ID)));
|
||||
|
||||
messages = forwardMessage(localState, msg);
|
||||
events = Collections.singletonList(getEvent(localState, msg));
|
||||
@@ -177,7 +180,7 @@ public class IntroducerEngine
|
||||
else if (currentState == AWAIT_ACKS ||
|
||||
currentState == AWAIT_ACK_1 ||
|
||||
currentState == AWAIT_ACK_2) {
|
||||
localState.put(STATE, nextState.getValue());
|
||||
localState.setState(nextState);
|
||||
messages = forwardMessage(localState, msg);
|
||||
events = Collections.emptyList();
|
||||
}
|
||||
@@ -185,27 +188,31 @@ public class IntroducerEngine
|
||||
else if (currentState == FINISHED) {
|
||||
// if it was a response store it to be found later
|
||||
if (action == REMOTE_ACCEPT_1 || action == REMOTE_DECLINE_1) {
|
||||
localState.put(RESPONSE_1, msg.getRaw(MESSAGE_ID));
|
||||
localState.setResponse1(
|
||||
new MessageId(msg.getRaw(MESSAGE_ID)));
|
||||
messages = Collections.emptyList();
|
||||
events = Collections.singletonList(getEvent(localState, msg));
|
||||
events = Collections
|
||||
.singletonList(getEvent(localState, msg));
|
||||
} else if (action == REMOTE_ACCEPT_2 ||
|
||||
action == REMOTE_DECLINE_2) {
|
||||
localState.put(RESPONSE_2, msg.getRaw(MESSAGE_ID));
|
||||
localState.setResponse2(
|
||||
new MessageId(msg.getRaw(MESSAGE_ID)));
|
||||
messages = Collections.emptyList();
|
||||
events = Collections.singletonList(getEvent(localState, msg));
|
||||
events = Collections
|
||||
.singletonList(getEvent(localState, msg));
|
||||
} else return noUpdate(localState);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad state");
|
||||
}
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
|
||||
localState, messages, events);
|
||||
return new StateUpdate<IntroducerSessionState, BdfDictionary>(false,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logLocalAction(IntroducerProtocolState state,
|
||||
BdfDictionary localState, BdfDictionary msg) {
|
||||
IntroducerSessionState localState, BdfDictionary msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
@@ -216,7 +223,7 @@ public class IntroducerEngine
|
||||
Arrays.hashCode(msg.getRaw(SESSION_ID)) + " in group " +
|
||||
Arrays.hashCode(msg.getRaw(GROUP_ID)) + ". " +
|
||||
"Moving on to state " +
|
||||
getState(localState.getLong(STATE)).name()
|
||||
localState.getState().name()
|
||||
);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
@@ -225,7 +232,7 @@ public class IntroducerEngine
|
||||
|
||||
private void logMessageReceived(IntroducerProtocolState currentState,
|
||||
IntroducerProtocolState nextState,
|
||||
BdfDictionary localState, int type, BdfDictionary msg) {
|
||||
IntroducerSessionState localState, int type, BdfDictionary msg) {
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
try {
|
||||
@@ -238,7 +245,8 @@ public class IntroducerEngine
|
||||
String from = getMessagePartner(localState, msg);
|
||||
String to = getOtherContact(localState, msg);
|
||||
|
||||
LOG.info("Received " + t + " in state " + currentState.name() + " from " +
|
||||
LOG.info("Received " + t + " in state " + currentState.name() +
|
||||
" from " +
|
||||
from + " to " + to + " with session ID " +
|
||||
Arrays.hashCode(msg.getRaw(SESSION_ID)) + " in group " +
|
||||
Arrays.hashCode(msg.getRaw(GROUP_ID)) + ". " +
|
||||
@@ -249,15 +257,16 @@ public class IntroducerEngine
|
||||
}
|
||||
}
|
||||
|
||||
private List<BdfDictionary> forwardMessage(BdfDictionary localState,
|
||||
private List<BdfDictionary> forwardMessage(
|
||||
IntroducerSessionState localState,
|
||||
BdfDictionary message) throws FormatException {
|
||||
|
||||
// clone the message here, because we still need the original
|
||||
BdfDictionary msg = (BdfDictionary) message.clone();
|
||||
if (isContact1(localState, msg)) {
|
||||
msg.put(GROUP_ID, localState.getRaw(GROUP_ID_2));
|
||||
msg.put(GROUP_ID, localState.getGroup2Id());
|
||||
} else {
|
||||
msg.put(GROUP_ID, localState.getRaw(GROUP_ID_1));
|
||||
msg.put(GROUP_ID, localState.getGroup1Id());
|
||||
}
|
||||
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
@@ -269,34 +278,28 @@ public class IntroducerEngine
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<BdfDictionary, BdfDictionary> onMessageDelivered(
|
||||
BdfDictionary localState, BdfDictionary delivered) {
|
||||
public StateUpdate<IntroducerSessionState, BdfDictionary> onMessageDelivered(
|
||||
IntroducerSessionState localState, BdfDictionary delivered) {
|
||||
try {
|
||||
return noUpdate(localState);
|
||||
}
|
||||
catch (FormatException e) {
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IntroducerProtocolState getState(Long state) {
|
||||
return IntroducerProtocolState.fromValue(state.intValue());
|
||||
}
|
||||
private Event getEvent(IntroducerSessionState localState,
|
||||
BdfDictionary msg) throws FormatException {
|
||||
|
||||
private Event getEvent(BdfDictionary localState, BdfDictionary msg)
|
||||
throws FormatException {
|
||||
|
||||
ContactId contactId =
|
||||
new ContactId(localState.getLong(CONTACT_ID_1).intValue());
|
||||
AuthorId authorId = new AuthorId(localState.getRaw(AUTHOR_ID_1));
|
||||
if (Arrays.equals(msg.getRaw(GROUP_ID), localState.getRaw(GROUP_ID_2))) {
|
||||
contactId =
|
||||
new ContactId(localState.getLong(CONTACT_ID_2).intValue());
|
||||
authorId = new AuthorId(localState.getRaw(AUTHOR_ID_2));
|
||||
ContactId contactId = localState.getContact1Id();
|
||||
AuthorId authorId = localState.getContact1AuthorId();
|
||||
if (Arrays.equals(msg.getRaw(GROUP_ID),
|
||||
localState.getGroup2Id().getBytes())) {
|
||||
contactId = localState.getContact2Id();
|
||||
authorId = localState.getContact2AuthorId();
|
||||
}
|
||||
|
||||
SessionId sessionId = new SessionId(localState.getRaw(SESSION_ID));
|
||||
SessionId sessionId = localState.getSessionId();
|
||||
MessageId messageId = new MessageId(msg.getRaw(MESSAGE_ID));
|
||||
long time = msg.getLong(MESSAGE_TIME);
|
||||
String name = getOtherContact(localState, msg);
|
||||
@@ -309,12 +312,12 @@ public class IntroducerEngine
|
||||
return new IntroductionResponseReceivedEvent(contactId, ir);
|
||||
}
|
||||
|
||||
private boolean isContact1(BdfDictionary localState, BdfDictionary msg)
|
||||
throws FormatException {
|
||||
private boolean isContact1(IntroducerSessionState localState,
|
||||
BdfDictionary msg) throws FormatException {
|
||||
|
||||
byte[] group = msg.getRaw(GROUP_ID);
|
||||
byte[] group1 = localState.getRaw(GROUP_ID_1);
|
||||
byte[] group2 = localState.getRaw(GROUP_ID_2);
|
||||
byte[] group1 = localState.getGroup1Id().getBytes();
|
||||
byte[] group2 = localState.getGroup2Id().getBytes();
|
||||
|
||||
if (Arrays.equals(group, group1)) {
|
||||
return true;
|
||||
@@ -325,69 +328,70 @@ public class IntroducerEngine
|
||||
}
|
||||
}
|
||||
|
||||
private String getMessagePartner(BdfDictionary localState,
|
||||
private String getMessagePartner(IntroducerSessionState localState,
|
||||
BdfDictionary msg) throws FormatException {
|
||||
|
||||
String from = localState.getString(CONTACT_1);
|
||||
if (Arrays.equals(msg.getRaw(GROUP_ID), localState.getRaw(GROUP_ID_2))) {
|
||||
from = localState.getString(CONTACT_2);
|
||||
String from = localState.getContact1Name();
|
||||
if (Arrays.equals(msg.getRaw(GROUP_ID),
|
||||
localState.getGroup2Id().getBytes())) {
|
||||
from = localState.getContact2Name();
|
||||
}
|
||||
return from;
|
||||
}
|
||||
|
||||
private String getOtherContact(BdfDictionary localState, BdfDictionary msg)
|
||||
throws FormatException {
|
||||
private String getOtherContact(IntroducerSessionState localState,
|
||||
BdfDictionary msg) throws FormatException {
|
||||
|
||||
String to = localState.getString(CONTACT_2);
|
||||
if (Arrays.equals(msg.getRaw(GROUP_ID), localState.getRaw(GROUP_ID_2))) {
|
||||
to = localState.getString(CONTACT_1);
|
||||
String to = localState.getContact2Name();
|
||||
if (Arrays.equals(msg.getRaw(GROUP_ID),
|
||||
localState.getGroup2Id().getBytes())) {
|
||||
to = localState.getContact1Name();
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
private StateUpdate<BdfDictionary, BdfDictionary> abortSession(
|
||||
IntroducerProtocolState currentState, BdfDictionary localState)
|
||||
throws FormatException {
|
||||
private StateUpdate<IntroducerSessionState, BdfDictionary> abortSession(
|
||||
IntroducerProtocolState currentState,
|
||||
IntroducerSessionState localState) throws FormatException {
|
||||
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Aborting protocol session " +
|
||||
Arrays.hashCode(localState.getRaw(SESSION_ID)) +
|
||||
localState.getSessionId().hashCode() +
|
||||
" in state " + currentState.name());
|
||||
}
|
||||
|
||||
localState.put(STATE, ERROR.getValue());
|
||||
localState.setState(ERROR);
|
||||
List<BdfDictionary> messages = new ArrayList<BdfDictionary>(2);
|
||||
BdfDictionary msg1 = new BdfDictionary();
|
||||
msg1.put(TYPE, TYPE_ABORT);
|
||||
msg1.put(SESSION_ID, localState.getRaw(SESSION_ID));
|
||||
msg1.put(GROUP_ID, localState.getRaw(GROUP_ID_1));
|
||||
msg1.put(SESSION_ID, localState.getSessionId());
|
||||
msg1.put(GROUP_ID, localState.getGroup1Id());
|
||||
messages.add(msg1);
|
||||
BdfDictionary msg2 = new BdfDictionary();
|
||||
msg2.put(TYPE, TYPE_ABORT);
|
||||
msg2.put(SESSION_ID, localState.getRaw(SESSION_ID));
|
||||
msg2.put(GROUP_ID, localState.getRaw(GROUP_ID_2));
|
||||
msg2.put(SESSION_ID, localState.getSessionId());
|
||||
msg2.put(GROUP_ID, localState.getGroup2Id());
|
||||
messages.add(msg2);
|
||||
|
||||
// send one abort event per contact
|
||||
List<Event> events = new ArrayList<Event>(2);
|
||||
SessionId sessionId = new SessionId(localState.getRaw(SESSION_ID));
|
||||
ContactId contactId1 =
|
||||
new ContactId(localState.getLong(CONTACT_ID_1).intValue());
|
||||
ContactId contactId2 =
|
||||
new ContactId(localState.getLong(CONTACT_ID_2).intValue());
|
||||
SessionId sessionId = localState.getSessionId();
|
||||
ContactId contactId1 = localState.getContact1Id();
|
||||
ContactId contactId2 = localState.getContact2Id();
|
||||
Event event1 = new IntroductionAbortedEvent(contactId1, sessionId);
|
||||
events.add(event1);
|
||||
Event event2 = new IntroductionAbortedEvent(contactId2, sessionId);
|
||||
events.add(event2);
|
||||
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
|
||||
localState, messages, events);
|
||||
return new StateUpdate<IntroducerSessionState, BdfDictionary>(false,
|
||||
false, localState, messages, events);
|
||||
}
|
||||
|
||||
private StateUpdate<BdfDictionary, BdfDictionary> noUpdate(
|
||||
BdfDictionary localState) throws FormatException {
|
||||
private StateUpdate<IntroducerSessionState, BdfDictionary> noUpdate(
|
||||
IntroducerSessionState localState) throws FormatException {
|
||||
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
|
||||
return new StateUpdate<IntroducerSessionState, BdfDictionary>(false,
|
||||
false,
|
||||
localState, Collections.<BdfDictionary>emptyList(),
|
||||
Collections.<Event>emptyList());
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.introduction;
|
||||
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.crypto.CryptoComponent;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
@@ -10,6 +11,7 @@ import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.introduction.IntroducerProtocolState;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
@@ -22,32 +24,19 @@ import java.util.logging.Logger;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.introduction.IntroducerProtocolState.PREPARE_REQUESTS;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.MSG;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
|
||||
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.TYPE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||
|
||||
class IntroducerManager {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(IntroducerManager.class.getName());
|
||||
Logger.getLogger(IntroducerManager.class.getName());
|
||||
|
||||
private final MessageSender messageSender;
|
||||
private final ClientHelper clientHelper;
|
||||
@@ -67,8 +56,8 @@ class IntroducerManager {
|
||||
this.introductionGroupFactory = introductionGroupFactory;
|
||||
}
|
||||
|
||||
public BdfDictionary initialize(Transaction txn, Contact c1, Contact c2)
|
||||
throws FormatException, DbException {
|
||||
public IntroducerSessionState initialize(Transaction txn, Contact c1,
|
||||
Contact c2) throws FormatException, DbException {
|
||||
|
||||
// create local message to keep engine state
|
||||
long now = clock.currentTimeMillis();
|
||||
@@ -78,39 +67,36 @@ class IntroducerManager {
|
||||
Message m = clientHelper.createMessage(
|
||||
introductionGroupFactory.createLocalGroup().getId(), now,
|
||||
BdfList.of(salt));
|
||||
MessageId sessionId = m.getId();
|
||||
SessionId sessionId = new SessionId(m.getId().getBytes());
|
||||
|
||||
Group g1 = introductionGroupFactory.createIntroductionGroup(c1);
|
||||
Group g2 = introductionGroupFactory.createIntroductionGroup(c2);
|
||||
IntroducerProtocolState stateValue =
|
||||
IntroducerProtocolState.PREPARE_REQUESTS;
|
||||
IntroducerSessionState state = new IntroducerSessionState(m.getId(),
|
||||
sessionId, g1.getId(), g2.getId(), c1.getId(),
|
||||
c1.getAuthor().getId(), c1.getAuthor().getName(),
|
||||
c2.getId(), c2.getAuthor().getId(), c2.getAuthor().getName(),
|
||||
stateValue);
|
||||
state.setPublicKey1(c1.getAuthor().getPublicKey());
|
||||
state.setPublicKey2(c2.getAuthor().getPublicKey());
|
||||
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put(SESSION_ID, sessionId);
|
||||
d.put(STORAGE_ID, sessionId);
|
||||
d.put(STATE, PREPARE_REQUESTS.getValue());
|
||||
d.put(ROLE, ROLE_INTRODUCER);
|
||||
d.put(GROUP_ID_1, g1.getId());
|
||||
d.put(GROUP_ID_2, g2.getId());
|
||||
d.put(CONTACT_1, c1.getAuthor().getName());
|
||||
d.put(CONTACT_2, c2.getAuthor().getName());
|
||||
d.put(CONTACT_ID_1, c1.getId().getInt());
|
||||
d.put(CONTACT_ID_2, c2.getId().getInt());
|
||||
d.put(AUTHOR_ID_1, c1.getAuthor().getId());
|
||||
d.put(AUTHOR_ID_2, c2.getAuthor().getId());
|
||||
BdfDictionary d = state.toBdfDictionary();
|
||||
|
||||
// save local state to database
|
||||
clientHelper.addLocalMessage(txn, m, d, false);
|
||||
|
||||
return d;
|
||||
return state;
|
||||
}
|
||||
|
||||
public void makeIntroduction(Transaction txn, Contact c1, Contact c2,
|
||||
void makeIntroduction(Transaction txn, Contact c1, Contact c2,
|
||||
String msg, long timestamp) throws DbException, FormatException {
|
||||
|
||||
// TODO check for existing session with those contacts?
|
||||
// deny new introduction under which conditions?
|
||||
|
||||
// initialize engine state
|
||||
BdfDictionary localState = initialize(txn, c1, c2);
|
||||
IntroducerSessionState localState = initialize(txn, c1, c2);
|
||||
|
||||
// define action
|
||||
BdfDictionary localAction = new BdfDictionary();
|
||||
@@ -124,11 +110,10 @@ class IntroducerManager {
|
||||
|
||||
// start engine and process its state update
|
||||
IntroducerEngine engine = new IntroducerEngine();
|
||||
processStateUpdate(txn,
|
||||
engine.onLocalAction(localState, localAction));
|
||||
processStateUpdate(txn, engine.onLocalAction(localState, localAction));
|
||||
}
|
||||
|
||||
public void incomingMessage(Transaction txn, BdfDictionary state,
|
||||
public void incomingMessage(Transaction txn, IntroducerSessionState state,
|
||||
BdfDictionary message) throws DbException, FormatException {
|
||||
|
||||
IntroducerEngine engine = new IntroducerEngine();
|
||||
@@ -137,12 +122,13 @@ class IntroducerManager {
|
||||
}
|
||||
|
||||
private void processStateUpdate(Transaction txn,
|
||||
IntroducerEngine.StateUpdate<BdfDictionary, BdfDictionary>
|
||||
IntroducerEngine.StateUpdate<IntroducerSessionState, BdfDictionary>
|
||||
result) throws DbException, FormatException {
|
||||
|
||||
// save new local state
|
||||
MessageId storageId = new MessageId(result.localState.getRaw(STORAGE_ID));
|
||||
clientHelper.mergeMessageMetadata(txn, storageId, result.localState);
|
||||
MessageId storageId = result.localState.getStorageId();
|
||||
clientHelper.mergeMessageMetadata(txn, storageId,
|
||||
result.localState.toBdfDictionary());
|
||||
|
||||
// send messages
|
||||
for (BdfDictionary d : result.toSend) {
|
||||
@@ -155,7 +141,7 @@ class IntroducerManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void abort(Transaction txn, BdfDictionary state) {
|
||||
public void abort(Transaction txn, IntroducerSessionState state) {
|
||||
|
||||
IntroducerEngine engine = new IntroducerEngine();
|
||||
BdfDictionary localAction = new BdfDictionary();
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
package org.briarproject.introduction;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.introduction.IntroducerProtocolState;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.RESPONSE_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.RESPONSE_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
|
||||
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;
|
||||
|
||||
// This class is not thread-safe
|
||||
class IntroducerSessionState extends IntroductionState {
|
||||
|
||||
private IntroducerProtocolState state;
|
||||
|
||||
private byte[] publicKey1;
|
||||
private MessageId response1;
|
||||
private final GroupId group1Id;
|
||||
private final ContactId contact1Id;
|
||||
private final AuthorId contact1AuthorId;
|
||||
private final String contact1Name;
|
||||
|
||||
private byte[] publicKey2;
|
||||
private MessageId response2;
|
||||
private final GroupId group2Id;
|
||||
private final ContactId contact2Id;
|
||||
private final AuthorId contact2AuthorId;
|
||||
private final String contact2Name;
|
||||
|
||||
IntroducerSessionState(MessageId storageId, SessionId sessionId,
|
||||
GroupId group1Id, GroupId group2Id, ContactId contact1Id,
|
||||
AuthorId contact1AuthorId, String contact1Name,
|
||||
ContactId contact2Id, AuthorId contact2AuthorId,
|
||||
String contact2Name,
|
||||
IntroducerProtocolState state) {
|
||||
|
||||
super(sessionId, storageId);
|
||||
|
||||
this.group2Id = group2Id;
|
||||
this.group1Id = group1Id;
|
||||
|
||||
this.contact2Id = contact2Id;
|
||||
this.contact1Id = contact1Id;
|
||||
|
||||
this.contact1AuthorId = contact1AuthorId;
|
||||
this.contact2AuthorId = contact2AuthorId;
|
||||
|
||||
this.contact1Name = contact1Name;
|
||||
this.contact2Name = contact2Name;
|
||||
|
||||
this.state = state;
|
||||
this.response1 = null;
|
||||
this.response2 = null;
|
||||
this.publicKey1 = null;
|
||||
this.publicKey2 = null;
|
||||
|
||||
}
|
||||
|
||||
BdfDictionary toBdfDictionary() {
|
||||
BdfDictionary d = super.toBdfDictionary();
|
||||
d.put(ROLE, ROLE_INTRODUCER);
|
||||
|
||||
d.put(GROUP_ID_1, getGroup1Id());
|
||||
d.put(GROUP_ID_2, getGroup2Id());
|
||||
|
||||
d.put(STATE, getState().getValue());
|
||||
|
||||
d.put(CONTACT_1, contact1Name);
|
||||
d.put(CONTACT_ID_1, contact1Id.getInt());
|
||||
d.put(AUTHOR_ID_1, contact1AuthorId);
|
||||
|
||||
d.put(CONTACT_2, contact2Name);
|
||||
d.put(CONTACT_ID_2, contact2Id.getInt());
|
||||
d.put(AUTHOR_ID_2, contact2AuthorId);
|
||||
|
||||
if (publicKey1 != null)
|
||||
d.put(PUBLIC_KEY1, publicKey1);
|
||||
|
||||
if (publicKey2 != null)
|
||||
d.put(PUBLIC_KEY2, publicKey2);
|
||||
|
||||
if (response1 != null)
|
||||
d.put(RESPONSE_1, response1);
|
||||
if (response2 != null)
|
||||
d.put(RESPONSE_2, response2);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static IntroducerSessionState fromBdfDictionary(BdfDictionary d)
|
||||
throws FormatException {
|
||||
|
||||
MessageId storageId = new MessageId(d.getRaw(STORAGE_ID));
|
||||
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
|
||||
|
||||
AuthorId authorId1 = new AuthorId(d.getRaw(AUTHOR_ID_1));
|
||||
AuthorId authorId2 = new AuthorId(d.getRaw(AUTHOR_ID_2));
|
||||
|
||||
String author1 = d.getString(CONTACT_1);
|
||||
String author2 = d.getString(CONTACT_2);
|
||||
ContactId contactId1 =
|
||||
new ContactId(d.getLong(CONTACT_ID_1).intValue());
|
||||
ContactId contactId2 =
|
||||
new ContactId(d.getLong(CONTACT_ID_2).intValue());
|
||||
|
||||
GroupId group1Id = new GroupId(d.getRaw(GROUP_ID_1));
|
||||
GroupId group2Id = new GroupId(d.getRaw(GROUP_ID_2));
|
||||
|
||||
|
||||
int stateNumber = d.getLong(STATE).intValue();
|
||||
IntroducerProtocolState state = IntroducerProtocolState.fromValue(
|
||||
stateNumber);
|
||||
IntroducerSessionState newState = new IntroducerSessionState(storageId,
|
||||
sessionId, group1Id, group2Id, contactId1, authorId1, author1,
|
||||
contactId2,
|
||||
authorId2,
|
||||
author2, state);
|
||||
|
||||
if (d.containsKey(PUBLIC_KEY1))
|
||||
newState.setPublicKey1(d.getRaw(PUBLIC_KEY1));
|
||||
|
||||
if (d.containsKey(PUBLIC_KEY2))
|
||||
newState.setPublicKey2(d.getRaw(PUBLIC_KEY2));
|
||||
|
||||
if (d.containsKey(RESPONSE_1))
|
||||
newState.setResponse1(new MessageId(d.getRaw(RESPONSE_1)));
|
||||
if (d.containsKey(RESPONSE_2))
|
||||
newState.setResponse2(new MessageId(d.getRaw(RESPONSE_2)));
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
GroupId getGroup2Id() {
|
||||
return group2Id;
|
||||
}
|
||||
|
||||
IntroducerProtocolState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
void setState(IntroducerProtocolState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
MessageId getResponse1() {
|
||||
return this.response1;
|
||||
}
|
||||
|
||||
void setResponse1(MessageId response1) {
|
||||
this.response1 = response1;
|
||||
}
|
||||
|
||||
MessageId getResponse2() {
|
||||
return this.response2;
|
||||
}
|
||||
|
||||
void setResponse2(MessageId response2) {
|
||||
this.response2 = response2;
|
||||
}
|
||||
|
||||
void setPublicKey1(byte[] publicKey1) {
|
||||
this.publicKey1 = publicKey1;
|
||||
}
|
||||
|
||||
void setPublicKey2(byte[] publicKey2) {
|
||||
this.publicKey2 = publicKey2;
|
||||
}
|
||||
|
||||
GroupId getGroup1Id() {
|
||||
return this.group1Id;
|
||||
}
|
||||
|
||||
ContactId getContact2Id() {
|
||||
return contact2Id;
|
||||
}
|
||||
|
||||
AuthorId getContact2AuthorId() {
|
||||
return contact2AuthorId;
|
||||
}
|
||||
|
||||
String getContact2Name() {
|
||||
return contact2Name;
|
||||
}
|
||||
|
||||
String getContact1Name() {
|
||||
return contact1Name;
|
||||
}
|
||||
|
||||
ContactId getContact1Id() {
|
||||
return contact1Id;
|
||||
}
|
||||
|
||||
AuthorId getContact1AuthorId() {
|
||||
return contact1AuthorId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.briarproject.api.db.NoSuchMessageException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.introduction.IntroducerProtocolState;
|
||||
import org.briarproject.introduction.IntroducerSessionState;
|
||||
import org.briarproject.api.introduction.IntroductionManager;
|
||||
import org.briarproject.api.introduction.IntroductionMessage;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
@@ -44,32 +45,18 @@ import javax.inject.Inject;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.introduction.IntroduceeProtocolState.FINISHED;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.EXISTS;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.MSG;
|
||||
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.READ;
|
||||
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.RESPONSE_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.RESPONSE_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCEE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
|
||||
@@ -166,16 +153,15 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
.getMessageMetadataAsDictionary(txn, gId, query);
|
||||
for (Map.Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
|
||||
BdfDictionary d = entry.getValue();
|
||||
ContactId c1 = new ContactId(d.getLong(CONTACT_ID_1).intValue());
|
||||
ContactId c2 = new ContactId(d.getLong(CONTACT_ID_2).intValue());
|
||||
IntroducerSessionState stateobj =
|
||||
IntroducerSessionState.fromBdfDictionary(d);
|
||||
ContactId c1 = stateobj.getContact1Id();
|
||||
ContactId c2 = stateobj.getContact2Id();
|
||||
|
||||
if (c1.equals(c.getId()) || c2.equals(c.getId())) {
|
||||
IntroducerProtocolState state = IntroducerProtocolState
|
||||
.fromValue(d.getLong(STATE).intValue());
|
||||
// abort protocol if still ongoing
|
||||
if (IntroducerProtocolState.isOngoing(state)) {
|
||||
introducerManager.abort(txn, d);
|
||||
}
|
||||
if (IntroducerProtocolState.isOngoing(stateobj.getState()))
|
||||
introducerManager.abort(txn, stateobj);
|
||||
// also delete state if both contacts have been deleted
|
||||
if (c1.equals(c.getId())) {
|
||||
try {
|
||||
@@ -208,7 +194,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
*/
|
||||
@Override
|
||||
protected void incomingMessage(Transaction txn, Message m, BdfList body,
|
||||
BdfDictionary message) throws DbException {
|
||||
BdfDictionary message) throws DbException, FormatException{
|
||||
|
||||
// Get message data and type
|
||||
GroupId groupId = m.getGroupId();
|
||||
@@ -217,15 +203,19 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
// we are an introducee, need to initialize new state
|
||||
if (type == TYPE_REQUEST) {
|
||||
boolean stateExists = true;
|
||||
SessionId sessionId;
|
||||
IntroduceeSessionState state;
|
||||
sessionId = new SessionId(message.getRaw(SESSION_ID));
|
||||
|
||||
try {
|
||||
getSessionState(txn, groupId, message.getRaw(SESSION_ID), false);
|
||||
getSessionState(txn, groupId, sessionId.getBytes(), false);
|
||||
} catch (FormatException e) {
|
||||
stateExists = false;
|
||||
}
|
||||
BdfDictionary state;
|
||||
try {
|
||||
if (stateExists) throw new FormatException();
|
||||
state = introduceeManager.initialize(txn, groupId, message);
|
||||
state = introduceeManager
|
||||
.initialize(txn, sessionId, groupId, message);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning(
|
||||
@@ -247,37 +237,45 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
// our role can be anything
|
||||
else if (type == TYPE_RESPONSE || type == TYPE_ACK || type == TYPE_ABORT) {
|
||||
BdfDictionary state;
|
||||
IntroductionState state;
|
||||
try {
|
||||
state = getSessionState(txn, groupId,
|
||||
message.getRaw(SESSION_ID));
|
||||
state = getSessionState(txn, groupId, message.getRaw(SESSION_ID));
|
||||
} catch (FormatException e) {
|
||||
LOG.warning("Could not find state for message, deleting...");
|
||||
deleteMessage(txn, m.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
long role = state.getLong(ROLE, -1L);
|
||||
try {
|
||||
if (role == ROLE_INTRODUCER) {
|
||||
introducerManager.incomingMessage(txn, state, message);
|
||||
} else if (role == ROLE_INTRODUCEE) {
|
||||
introduceeManager.incomingMessage(txn, state, message);
|
||||
if (state instanceof IntroducerSessionState) {
|
||||
introducerManager.incomingMessage(txn,
|
||||
(IntroducerSessionState)state, message);
|
||||
} else if (state instanceof IntroduceeSessionState) {
|
||||
introduceeManager.incomingMessage(txn,
|
||||
(IntroduceeSessionState) state, message);
|
||||
} else {
|
||||
if(LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Unknown role '" + role +
|
||||
"'. Deleting message...");
|
||||
LOG.warning("Unknown role '"
|
||||
+ state.getClass().getName()
|
||||
+ "'. Deleting message...");
|
||||
deleteMessage(txn, m.getId());
|
||||
}
|
||||
throw new RuntimeException("Unknown role" +
|
||||
state.getClass().getName());
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
if (role == ROLE_INTRODUCER) introducerManager.abort(txn, state);
|
||||
else introduceeManager.abort(txn, state);
|
||||
if (state instanceof IntroducerSessionState)
|
||||
introducerManager.abort(txn,
|
||||
(IntroducerSessionState)state);
|
||||
else introduceeManager.abort(txn,
|
||||
(IntroduceeSessionState) state);
|
||||
} catch (IOException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
if (role == ROLE_INTRODUCER) introducerManager.abort(txn, state);
|
||||
else introduceeManager.abort(txn, state);
|
||||
if (state instanceof IntroducerSessionState)
|
||||
introducerManager.abort(txn, (IntroducerSessionState)state);
|
||||
else introduceeManager.abort(txn,
|
||||
(IntroduceeSessionState) state);
|
||||
}
|
||||
} else {
|
||||
// the message has been validated, so this should not happen
|
||||
@@ -310,8 +308,9 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
try {
|
||||
Contact c = db.getContact(txn, contactId);
|
||||
Group g = introductionGroupFactory.createIntroductionGroup(c);
|
||||
BdfDictionary state =
|
||||
getSessionState(txn, g.getId(), sessionId.getBytes());
|
||||
IntroduceeSessionState state =
|
||||
(IntroduceeSessionState) getSessionState(txn, g.getId(),
|
||||
sessionId.getBytes());
|
||||
|
||||
introduceeManager.acceptIntroduction(txn, state, timestamp);
|
||||
txn.setComplete();
|
||||
@@ -329,10 +328,11 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
try {
|
||||
Contact c = db.getContact(txn, contactId);
|
||||
Group g = introductionGroupFactory.createIntroductionGroup(c);
|
||||
BdfDictionary state =
|
||||
IntroductionState state =
|
||||
getSessionState(txn, g.getId(), sessionId.getBytes());
|
||||
|
||||
introduceeManager.declineIntroduction(txn, state, timestamp);
|
||||
introduceeManager.declineIntroduction(txn,
|
||||
(IntroduceeSessionState) state, timestamp);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
@@ -369,29 +369,39 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
|
||||
// get session state
|
||||
SessionId sessionId = new SessionId(msg.getRaw(SESSION_ID));
|
||||
BdfDictionary state =
|
||||
IntroductionState state =
|
||||
getSessionState(txn, g, sessionId.getBytes());
|
||||
|
||||
int role = state.getLong(ROLE).intValue();
|
||||
boolean local;
|
||||
long time = msg.getLong(MESSAGE_TIME);
|
||||
boolean accepted = msg.getBoolean(ACCEPT, false);
|
||||
boolean read = msg.getBoolean(READ, false);
|
||||
AuthorId authorId;
|
||||
String name;
|
||||
|
||||
int role;
|
||||
if (state instanceof IntroducerSessionState) {
|
||||
role = ROLE_INTRODUCER;
|
||||
} else {
|
||||
role = ROLE_INTRODUCEE;
|
||||
}
|
||||
if (type == TYPE_RESPONSE) {
|
||||
if (role == ROLE_INTRODUCER) {
|
||||
if (!concernsThisContact(contactId, messageId, state)) {
|
||||
IntroducerSessionState iss =
|
||||
(IntroducerSessionState)state;
|
||||
if (!concernsThisContact(contactId, messageId,
|
||||
iss)) {
|
||||
// this response is not from contactId
|
||||
continue;
|
||||
}
|
||||
local = false;
|
||||
authorId =
|
||||
getAuthorIdForIntroducer(contactId, state);
|
||||
name = getNameForIntroducer(contactId, state);
|
||||
authorId = getAuthorIdForIntroducer(contactId, iss);
|
||||
name = getNameForIntroducer(contactId, iss);
|
||||
} else {
|
||||
if (Arrays.equals(state.getRaw(NOT_OUR_RESPONSE),
|
||||
messageId.getBytes())) {
|
||||
IntroduceeSessionState iss =
|
||||
(IntroduceeSessionState) state;
|
||||
if (Arrays.equals(iss.getOtherResponseId(),
|
||||
messageId.getBytes())) {
|
||||
// this response is not ours,
|
||||
// check if it was a decline
|
||||
if (!accepted) {
|
||||
@@ -403,9 +413,9 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
} else {
|
||||
local = true;
|
||||
}
|
||||
authorId = new AuthorId(
|
||||
state.getRaw(REMOTE_AUTHOR_ID));
|
||||
name = state.getString(NAME);
|
||||
|
||||
authorId = iss.getRemoteAuthorId();
|
||||
name = iss.getIntroducedName();
|
||||
}
|
||||
IntroductionResponse ir = new IntroductionResponse(
|
||||
sessionId, messageId, role, time, local,
|
||||
@@ -416,26 +426,26 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
String message;
|
||||
boolean answered, exists, introducesOtherIdentity;
|
||||
if (role == ROLE_INTRODUCER) {
|
||||
IntroducerSessionState iss =
|
||||
(IntroducerSessionState)state;
|
||||
local = true;
|
||||
authorId =
|
||||
getAuthorIdForIntroducer(contactId, state);
|
||||
name = getNameForIntroducer(contactId, state);
|
||||
authorId = getAuthorIdForIntroducer(contactId, iss);
|
||||
name = getNameForIntroducer(contactId, iss);
|
||||
message = msg.getOptionalString(MSG);
|
||||
answered = false;
|
||||
exists = false;
|
||||
introducesOtherIdentity = false;
|
||||
} else {
|
||||
IntroduceeSessionState iss =
|
||||
(IntroduceeSessionState) state;
|
||||
local = false;
|
||||
authorId = new AuthorId(
|
||||
state.getRaw(REMOTE_AUTHOR_ID));
|
||||
name = state.getString(NAME);
|
||||
message = state.getOptionalString(MSG);
|
||||
boolean finished = state.getLong(STATE) ==
|
||||
FINISHED.getValue();
|
||||
answered = finished || state.getBoolean(ANSWERED);
|
||||
exists = state.getBoolean(EXISTS);
|
||||
introducesOtherIdentity =
|
||||
state.getBoolean(REMOTE_AUTHOR_IS_US);
|
||||
authorId = iss.getRemoteAuthorId();
|
||||
name = iss.getIntroducedName();
|
||||
message = iss.getMessage();
|
||||
boolean finished = iss.getState() == FINISHED;
|
||||
answered = finished || iss.getAnswered();
|
||||
exists = iss.getContactExists();
|
||||
introducesOtherIdentity = iss.getRemoteAuthorIsUs();
|
||||
}
|
||||
IntroductionRequest ir = new IntroductionRequest(
|
||||
sessionId, messageId, role, time, local,
|
||||
@@ -458,38 +468,36 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
private String getNameForIntroducer(ContactId contactId,
|
||||
BdfDictionary state) throws FormatException {
|
||||
IntroducerSessionState state) throws FormatException {
|
||||
|
||||
if (contactId.getInt() == state.getLong(CONTACT_ID_1).intValue())
|
||||
return state.getString(CONTACT_2);
|
||||
if (contactId.getInt() == state.getLong(CONTACT_ID_2).intValue())
|
||||
return state.getString(CONTACT_1);
|
||||
if (contactId.equals(state.getContact2Id()))
|
||||
return state.getContact1Name();
|
||||
if (contactId.equals(state.getContact1Id()))
|
||||
return state.getContact2Name();
|
||||
throw new RuntimeException("Contact not part of this introduction session");
|
||||
}
|
||||
|
||||
private AuthorId getAuthorIdForIntroducer(ContactId contactId,
|
||||
BdfDictionary state) throws FormatException {
|
||||
IntroducerSessionState state) throws FormatException {
|
||||
|
||||
if (contactId.getInt() == state.getLong(CONTACT_ID_1).intValue())
|
||||
return new AuthorId(state.getRaw(AUTHOR_ID_2));
|
||||
if (contactId.getInt() == state.getLong(CONTACT_ID_2).intValue())
|
||||
return new AuthorId(state.getRaw(AUTHOR_ID_1));
|
||||
if (contactId.equals(state.getContact1Id()))
|
||||
return state.getContact2AuthorId();
|
||||
if (contactId.equals(state.getContact2Id()))
|
||||
return state.getContact1AuthorId();
|
||||
throw new RuntimeException("Contact not part of this introduction session");
|
||||
}
|
||||
|
||||
private boolean concernsThisContact(ContactId contactId, MessageId messageId,
|
||||
BdfDictionary state) throws FormatException {
|
||||
IntroducerSessionState state) throws FormatException {
|
||||
|
||||
if (contactId.getInt() == state.getLong(CONTACT_ID_1).intValue()) {
|
||||
return Arrays.equals(state.getRaw(RESPONSE_1, new byte[0]),
|
||||
messageId.getBytes());
|
||||
if (contactId.equals(state.getContact1Id())) {
|
||||
return state.getResponse1().equals(messageId);
|
||||
} else {
|
||||
return Arrays.equals(state.getRaw(RESPONSE_2, new byte[0]),
|
||||
messageId.getBytes());
|
||||
return state.getResponse2().equals(messageId);
|
||||
}
|
||||
}
|
||||
|
||||
private BdfDictionary getSessionState(Transaction txn, GroupId groupId,
|
||||
private IntroductionState getSessionState(Transaction txn, GroupId groupId,
|
||||
byte[] sessionId, boolean warn)
|
||||
throws DbException, FormatException {
|
||||
|
||||
@@ -503,7 +511,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
if (!g1.equals(groupId) && !g2.equals(groupId)) {
|
||||
throw new NoSuchMessageException();
|
||||
}
|
||||
return state;
|
||||
return IntroductionState.fromBdfDictionary(state);
|
||||
} catch (NoSuchMessageException e) {
|
||||
// State not found directly, so iterate over all states
|
||||
// to find state for introducee
|
||||
@@ -514,7 +522,8 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
if (Arrays.equals(m.getValue().getRaw(SESSION_ID), sessionId)) {
|
||||
BdfDictionary state = m.getValue();
|
||||
GroupId g = new GroupId(state.getRaw(GROUP_ID));
|
||||
if (g.equals(groupId)) return state;
|
||||
if (g.equals(groupId))
|
||||
return IntroductionState.fromBdfDictionary(state);
|
||||
}
|
||||
}
|
||||
if (warn && LOG.isLoggable(WARNING)) {
|
||||
@@ -526,7 +535,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
}
|
||||
|
||||
private BdfDictionary getSessionState(Transaction txn, GroupId groupId,
|
||||
private IntroductionState getSessionState(Transaction txn, GroupId groupId,
|
||||
byte[] sessionId) throws DbException, FormatException {
|
||||
|
||||
return getSessionState(txn, groupId, sessionId, true);
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.briarproject.introduction;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCEE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID;
|
||||
|
||||
// This class is not thread-safe
|
||||
abstract class IntroductionState {
|
||||
|
||||
private SessionId sessionId;
|
||||
private final MessageId storageId;
|
||||
|
||||
IntroductionState(SessionId sessionId, MessageId storageId) {
|
||||
this.sessionId = sessionId;
|
||||
this.storageId = storageId;
|
||||
}
|
||||
|
||||
BdfDictionary toBdfDictionary() {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put(SESSION_ID, sessionId);
|
||||
d.put(STORAGE_ID, getStorageId());
|
||||
return d;
|
||||
}
|
||||
|
||||
static IntroductionState fromBdfDictionary(BdfDictionary state)
|
||||
throws FormatException {
|
||||
|
||||
int role = state.getLong(ROLE).intValue();
|
||||
if (role == ROLE_INTRODUCER) {
|
||||
return IntroducerSessionState.fromBdfDictionary(state);
|
||||
} else if (role == ROLE_INTRODUCEE) {
|
||||
return IntroduceeSessionState.fromBdfDictionary(state);
|
||||
} else {
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public MessageId getStorageId() {
|
||||
return storageId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -36,34 +36,31 @@ import org.junit.Test;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
||||
import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_LOCAL_RESPONSE;
|
||||
import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST;
|
||||
import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_RESPONSES;
|
||||
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.MAC_LENGTH;
|
||||
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.briarproject.introduction.IntroduceeSessionState.fromBdfDictionary;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class IntroduceeManagerTest extends BriarTestCase {
|
||||
@@ -171,12 +168,18 @@ public class IntroduceeManagerTest extends BriarTestCase {
|
||||
msg.put(NAME, introducee2.getAuthor().getName());
|
||||
msg.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
|
||||
|
||||
final BdfDictionary state =
|
||||
initializeSessionState(txn, introductionGroup1.getId(), msg);
|
||||
final IntroduceeSessionState state =
|
||||
initializeSessionState(txn, sessionId,
|
||||
introductionGroup1.getId(), msg);
|
||||
state.setIntroducedName(msg.getString(NAME));
|
||||
state.setIntroducedPublicKey(msg.getRaw(PUBLIC_KEY));
|
||||
|
||||
final BdfDictionary statedict = state.toBdfDictionary();
|
||||
statedict.put(STATE, AWAIT_RESPONSES.getValue());
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clientHelper).mergeMessageMetadata(txn,
|
||||
localStateMessage.getId(), state);
|
||||
localStateMessage.getId(), statedict);
|
||||
}});
|
||||
|
||||
introduceeManager.incomingMessage(txn, state, msg);
|
||||
@@ -199,19 +202,31 @@ public class IntroduceeManagerTest extends BriarTestCase {
|
||||
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());
|
||||
|
||||
final IntroduceeSessionState state =
|
||||
initializeSessionState(txn, sessionId,
|
||||
introductionGroup1.getId(), msg);
|
||||
state.setTheirTime(time);
|
||||
state.setOurTransportProperties(new BdfDictionary());
|
||||
final BdfDictionary statedict = state.toBdfDictionary();
|
||||
state.setState(AWAIT_RESPONSES);
|
||||
statedict.put(STATE, AWAIT_LOCAL_RESPONSE.getValue());
|
||||
statedict.put(ACCEPT, true);
|
||||
statedict.put(E_PUBLIC_KEY,
|
||||
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||
statedict.put(NOT_OUR_RESPONSE, message1.getId().getBytes());
|
||||
|
||||
// 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(E_PUBLIC_KEY, statedict.getRaw(E_PUBLIC_KEY));
|
||||
msg.put(TRANSPORT, new BdfDictionary());
|
||||
|
||||
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clientHelper).mergeMessageMetadata(txn,
|
||||
localStateMessage.getId(), state);
|
||||
localStateMessage.getId(), statedict);
|
||||
}});
|
||||
|
||||
introduceeManager.incomingMessage(txn, state, msg);
|
||||
@@ -221,9 +236,67 @@ public class IntroduceeManagerTest extends BriarTestCase {
|
||||
assertFalse(txn.isComplete());
|
||||
}
|
||||
|
||||
private BdfDictionary initializeSessionState(final Transaction txn,
|
||||
final GroupId groupId, final BdfDictionary msg)
|
||||
@Test
|
||||
public void testInitialSerialization() throws DbException, FormatException {
|
||||
IntroduceeSessionState state = initializeDefaultSessionState();
|
||||
|
||||
BdfDictionary statedict = state.toBdfDictionary();
|
||||
assertEquals(statedict, fromBdfDictionary(statedict).toBdfDictionary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFullSerialization() throws DbException, FormatException {
|
||||
IntroduceeSessionState state = initializeDefaultSessionState();
|
||||
|
||||
state.setOurMac(TestUtils.getRandomBytes(42));
|
||||
// TODO use ALL setters here to make the test cover everything
|
||||
state.setState(IntroduceeProtocolState.AWAIT_ACK);
|
||||
state.setAccept(true);
|
||||
state.setAnswered(true);
|
||||
state.setOurTime(-1L);
|
||||
state.setTheirTime(-2L);
|
||||
state.setMessage("");
|
||||
state.setTheirEphemeralPublicKey(TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||
state.setOurTransportProperties(new BdfDictionary());
|
||||
state.setContactExists(false);
|
||||
state.setRemoteAuthorId(new AuthorId(TestUtils.getRandomId()));
|
||||
state.setRemoteAuthorIsUs(false);
|
||||
state.setTheirResponseId(TestUtils.getRandomId());
|
||||
state.setTask(-1);
|
||||
state.setIntroducedName(TestUtils.getRandomString(MAX_AUTHOR_NAME_LENGTH));
|
||||
state.setOurPublicKey(TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||
state.setOurPrivateKey(TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||
state.setIntroducedPublicKey(TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||
state.setMac(TestUtils.getRandomBytes(MAC_LENGTH));
|
||||
state.setSignature(TestUtils.getRandomBytes(MAX_SIGNATURE_LENGTH));
|
||||
state.setOurSignature(TestUtils.getRandomBytes(MAX_SIGNATURE_LENGTH));
|
||||
state.setOurTransport(new BdfDictionary());
|
||||
state.setTheirNonce(TestUtils.getRandomBytes(32));
|
||||
state.setTheirMacKey(TestUtils.getRandomBytes(32));
|
||||
|
||||
BdfDictionary statedict = state.toBdfDictionary();
|
||||
assertEquals(statedict, fromBdfDictionary(statedict).toBdfDictionary());
|
||||
}
|
||||
|
||||
private IntroduceeSessionState initializeDefaultSessionState()
|
||||
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());
|
||||
msg.put(TRANSPORT, new BdfDictionary());
|
||||
|
||||
return initializeSessionState(txn, sessionId,
|
||||
introductionGroup1.getId(), msg);
|
||||
}
|
||||
|
||||
private IntroduceeSessionState initializeSessionState(final Transaction txn,
|
||||
final SessionId sessionId, final GroupId groupId,
|
||||
final BdfDictionary msg) throws DbException, FormatException {
|
||||
|
||||
final SecureRandom secureRandom = context.mock(SecureRandom.class);
|
||||
final Bytes salt = new Bytes(new byte[64]);
|
||||
@@ -231,19 +304,17 @@ public class IntroduceeManagerTest extends BriarTestCase {
|
||||
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);
|
||||
final IntroduceeSessionState state = new IntroduceeSessionState(
|
||||
localStateMessage.getId(),
|
||||
sessionId, groupId, introducer.getId(),
|
||||
introducer.getAuthor().getId(), introducer.getAuthor().getName(),
|
||||
introducer.getLocalAuthorId(), AWAIT_REQUEST);
|
||||
|
||||
state.setContactExists(true);
|
||||
state.setRemoteAuthorIsUs(false);
|
||||
state.setRemoteAuthorId(introducee2.getAuthor().getId());
|
||||
state.setIntroducedPublicKey(introducee2.getAuthor().getPublicKey());
|
||||
final BdfDictionary statedict = state.toBdfDictionary();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
@@ -279,10 +350,12 @@ public class IntroduceeManagerTest extends BriarTestCase {
|
||||
|
||||
// store session state
|
||||
oneOf(clientHelper)
|
||||
.addLocalMessage(txn, localStateMessage, state, false);
|
||||
.addLocalMessage(txn, localStateMessage, statedict,
|
||||
false);
|
||||
}});
|
||||
|
||||
BdfDictionary result = introduceeManager.initialize(txn, groupId, msg);
|
||||
IntroduceeSessionState result = introduceeManager.initialize(txn,
|
||||
sessionId, groupId, msg);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
return result;
|
||||
|
||||
@@ -19,6 +19,7 @@ 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.clients.SessionId;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
@@ -42,6 +43,8 @@ import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_2
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||
@@ -49,27 +52,30 @@ import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class IntroducerManagerTest extends BriarTestCase {
|
||||
|
||||
final Mockery context;
|
||||
final IntroducerManager introducerManager;
|
||||
final CryptoComponent cryptoComponent;
|
||||
final ClientHelper clientHelper;
|
||||
final IntroductionGroupFactory introductionGroupFactory;
|
||||
final MessageSender messageSender;
|
||||
final Clock clock;
|
||||
final Contact introducee1;
|
||||
final Contact introducee2;
|
||||
final Group localGroup0;
|
||||
final Group introductionGroup1;
|
||||
final Group introductionGroup2;
|
||||
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 SecureRandom secureRandom;
|
||||
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);
|
||||
secureRandom = context.mock(SecureRandom.class);
|
||||
cryptoComponent = context.mock(CryptoComponent.class);
|
||||
clientHelper = context.mock(ClientHelper.class);
|
||||
clock = context.mock(Clock.class);
|
||||
@@ -107,48 +113,130 @@ public class IntroducerManagerTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMakeIntroduction() throws DbException, FormatException {
|
||||
public void testInitializeSessionState()
|
||||
throws DbException, FormatException {
|
||||
|
||||
final Transaction txn = new Transaction(null, false);
|
||||
final long time = 42L;
|
||||
context.setImposteriser(ClassImposteriser.INSTANCE);
|
||||
final SecureRandom secureRandom = context.mock(SecureRandom.class);
|
||||
final Bytes salt = new Bytes(new byte[64]);
|
||||
final Message msg = new Message(new MessageId(TestUtils.getRandomId()),
|
||||
localGroup0.getId(), time, TestUtils.getRandomBytes(64));
|
||||
final 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());
|
||||
final BdfDictionary state2 = (BdfDictionary) state.clone();
|
||||
state2.put(STATE, AWAIT_RESPONSES.getValue());
|
||||
|
||||
final IntroducerSessionState state =
|
||||
getState(msg, introducee1, introducee2);
|
||||
|
||||
state.setPublicKey1(introducee1.getAuthor().getPublicKey());
|
||||
state.setPublicKey2(introducee2.getAuthor().getPublicKey());
|
||||
|
||||
checkInitialisation(time, salt, msg, txn, state);
|
||||
|
||||
IntroducerSessionState result = introducerManager.initialize(txn,
|
||||
introducee1, introducee2);
|
||||
assertEquals(state.toBdfDictionary(), result.toBdfDictionary());
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMakeIntroduction() throws DbException, FormatException {
|
||||
final Transaction txn = new Transaction(null, false);
|
||||
|
||||
final long time = 42L;
|
||||
final Bytes salt = new Bytes(new byte[64]);
|
||||
final Message msg = new Message(new MessageId(TestUtils.getRandomId()),
|
||||
localGroup0.getId(), time, TestUtils.getRandomBytes(64));
|
||||
|
||||
final IntroducerSessionState state =
|
||||
getState(msg, introducee1, introducee2);
|
||||
|
||||
state.setPublicKey1(introducee1.getAuthor().getPublicKey());
|
||||
state.setPublicKey2(introducee2.getAuthor().getPublicKey());
|
||||
|
||||
checkInitialisation(time, salt, msg, txn, state);
|
||||
|
||||
final IntroducerSessionState state2 = state;
|
||||
state2.setState(AWAIT_RESPONSES);
|
||||
|
||||
final 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(SESSION_ID, state.getSessionId());
|
||||
msg1.put(GROUP_ID, state.getGroup1Id());
|
||||
msg1.put(NAME, state.getContact2Name());
|
||||
msg1.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
|
||||
final BdfDictionary msg1send = (BdfDictionary) msg1.clone();
|
||||
msg1send.put(MESSAGE_TIME, time);
|
||||
|
||||
final 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(SESSION_ID, state.getSessionId());
|
||||
msg2.put(GROUP_ID, state.getGroup2Id());
|
||||
msg2.put(NAME, state.getContact1Name());
|
||||
msg2.put(PUBLIC_KEY, introducee1.getAuthor().getPublicKey());
|
||||
final BdfDictionary msg2send = (BdfDictionary) msg2.clone();
|
||||
msg2send.put(MESSAGE_TIME, time);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
// send message
|
||||
oneOf(clientHelper).mergeMessageMetadata(txn, msg.getId(),
|
||||
state2.toBdfDictionary());
|
||||
oneOf(messageSender).sendMessage(txn, msg1send);
|
||||
oneOf(messageSender).sendMessage(txn, msg2send);
|
||||
}});
|
||||
|
||||
introducerManager
|
||||
.makeIntroduction(txn, introducee1, introducee2, null, time);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
|
||||
assertFalse(txn.isComplete());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFullSerialization() throws FormatException {
|
||||
|
||||
final Message msg = new Message(new MessageId(TestUtils.getRandomId()),
|
||||
localGroup0.getId(), 0L, TestUtils.getRandomBytes(64));
|
||||
|
||||
IntroducerSessionState state = getState(msg, introducee1, introducee2);
|
||||
|
||||
final BdfDictionary d = new BdfDictionary();
|
||||
d.put(SESSION_ID, new SessionId(msg.getId().getBytes()));
|
||||
d.put(STORAGE_ID, msg.getId());
|
||||
d.put(STATE, PREPARE_REQUESTS.getValue());
|
||||
d.put(ROLE, ROLE_INTRODUCER);
|
||||
d.put(GROUP_ID_1, introductionGroup1.getId());
|
||||
d.put(GROUP_ID_2, introductionGroup2.getId());
|
||||
d.put(CONTACT_1, introducee1.getAuthor().getName());
|
||||
d.put(CONTACT_2, introducee2.getAuthor().getName());
|
||||
d.put(CONTACT_ID_1, introducee1.getId().getInt());
|
||||
d.put(CONTACT_ID_2, introducee2.getId().getInt());
|
||||
d.put(AUTHOR_ID_1, introducee1.getAuthor().getId());
|
||||
d.put(AUTHOR_ID_2, introducee2.getAuthor().getId());
|
||||
|
||||
assertEquals(d, state.toBdfDictionary());
|
||||
}
|
||||
|
||||
|
||||
private IntroducerSessionState getState(Message msg, Contact c1, Contact c2)
|
||||
throws FormatException {
|
||||
|
||||
IntroducerSessionState state = new IntroducerSessionState(msg.getId(),
|
||||
new SessionId(msg.getId().getBytes()),
|
||||
introductionGroup1.getId(),
|
||||
introductionGroup2.getId(), c1.getId(), c1.getAuthor().getId(),
|
||||
c1.getAuthor().getName(), c2.getId(), c2.getAuthor().getId(),
|
||||
c2.getAuthor().getName(), PREPARE_REQUESTS);
|
||||
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
private void checkInitialisation(final long time, final Bytes salt,
|
||||
final Message msg, final Transaction txn,
|
||||
final IntroducerSessionState state)
|
||||
throws FormatException, DbException {
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
// initialize and store session state
|
||||
oneOf(clock).currentTimeMillis();
|
||||
@@ -167,20 +255,10 @@ public class IntroducerManagerTest extends BriarTestCase {
|
||||
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);
|
||||
oneOf(clientHelper).addLocalMessage(txn, msg,
|
||||
state.toBdfDictionary(), false);
|
||||
}});
|
||||
|
||||
introducerManager
|
||||
.makeIntroduction(txn, introducee1, introducee2, null, time);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
|
||||
assertFalse(txn.isComplete());
|
||||
}
|
||||
|
||||
private ClientId getClientId() {
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.clients.MessageQueueManager;
|
||||
import org.briarproject.api.clients.PrivateGroupFactory;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
@@ -18,7 +19,7 @@ import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.introduction.IntroducerProtocolState;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
@@ -37,39 +38,58 @@ import java.util.Map;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
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.AUTHOR_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_1;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_2;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.LOCAL_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_INTRODUCER;
|
||||
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.STORAGE_ID;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
||||
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.introduction.IntroductionConstants.TIME;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.OUR_TIME;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.INTRODUCER;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.EXISTS;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.NO_TASK;
|
||||
import static org.briarproject.api.introduction.IntroductionConstants.TASK;
|
||||
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class IntroductionManagerImplTest extends BriarTestCase {
|
||||
|
||||
final Mockery context;
|
||||
final IntroductionManagerImpl introductionManager;
|
||||
final IntroducerManager introducerManager;
|
||||
final IntroduceeManager introduceeManager;
|
||||
final DatabaseComponent db;
|
||||
final PrivateGroupFactory privateGroupFactory;
|
||||
final ClientHelper clientHelper;
|
||||
final MetadataEncoder metadataEncoder;
|
||||
final MessageQueueManager messageQueueManager;
|
||||
final IntroductionGroupFactory introductionGroupFactory;
|
||||
final Clock clock;
|
||||
final SessionId sessionId = new SessionId(TestUtils.getRandomId());
|
||||
final long time = 42L;
|
||||
final Contact introducee1;
|
||||
final Contact introducee2;
|
||||
final Group localGroup0;
|
||||
final Group introductionGroup1;
|
||||
final Group introductionGroup2;
|
||||
final Message message1;
|
||||
Transaction txn;
|
||||
private final Mockery context;
|
||||
private final IntroductionManagerImpl introductionManager;
|
||||
private final IntroducerManager introducerManager;
|
||||
private final IntroduceeManager introduceeManager;
|
||||
private final DatabaseComponent db;
|
||||
private final PrivateGroupFactory privateGroupFactory;
|
||||
private final ClientHelper clientHelper;
|
||||
private final MetadataEncoder metadataEncoder;
|
||||
private final MessageQueueManager messageQueueManager;
|
||||
private final IntroductionGroupFactory introductionGroupFactory;
|
||||
private final Clock clock;
|
||||
private final SessionId sessionId = new SessionId(TestUtils.getRandomId());
|
||||
private final long time = 42L;
|
||||
private final Contact introducee1;
|
||||
private final Contact introducee2;
|
||||
private final Group localGroup0;
|
||||
private final Group introductionGroup1;
|
||||
private final Group introductionGroup2;
|
||||
private final Message message1;
|
||||
private Transaction txn;
|
||||
|
||||
public IntroductionManagerImplTest() {
|
||||
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
|
||||
@@ -148,21 +168,45 @@ public class IntroductionManagerImplTest extends BriarTestCase {
|
||||
@Test
|
||||
public void testAcceptIntroduction() throws DbException, FormatException {
|
||||
final BdfDictionary state = BdfDictionary.of(
|
||||
new BdfEntry(ROLE, ROLE_INTRODUCEE),
|
||||
new BdfEntry(GROUP_ID, introductionGroup1.getId()),
|
||||
new BdfEntry(GROUP_ID_1, introductionGroup1.getId()),
|
||||
new BdfEntry(GROUP_ID_2, introductionGroup2.getId())
|
||||
// FIXME: GROUP_ID, GROUP_ID_2 and GROUP_ID_1 are needed to
|
||||
// FIXME: deserialize an *introducee*. Why is this?
|
||||
new BdfEntry(GROUP_ID_2, introductionGroup2.getId()),
|
||||
new BdfEntry(SESSION_ID, sessionId),
|
||||
new BdfEntry(STORAGE_ID, sessionId),
|
||||
new BdfEntry(AUTHOR_ID_1, introducee1.getAuthor().getId()),
|
||||
new BdfEntry(CONTACT_1, introducee1.getAuthor().getName()),
|
||||
new BdfEntry(CONTACT_ID_1, introducee1.getId().getInt()),
|
||||
new BdfEntry(AUTHOR_ID_2, introducee2.getAuthor().getId()),
|
||||
new BdfEntry(CONTACT_2, introducee2.getAuthor().getName()),
|
||||
new BdfEntry(CONTACT_ID_2, introducee2.getId().getInt()),
|
||||
new BdfEntry(STATE, AWAIT_REQUEST.getValue()),
|
||||
new BdfEntry(TIME, time),
|
||||
new BdfEntry(OUR_TIME, time),
|
||||
new BdfEntry(NAME, introducee1.getAuthor().getName()),
|
||||
new BdfEntry(LOCAL_AUTHOR_ID, introducee1.getLocalAuthorId()),
|
||||
new BdfEntry(INTRODUCER, introducee1.getAuthor().getName()),
|
||||
new BdfEntry(EXISTS, false),
|
||||
new BdfEntry(TASK, NO_TASK),
|
||||
new BdfEntry(REMOTE_AUTHOR_IS_US, false)
|
||||
);
|
||||
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);
|
||||
oneOf(introductionGroupFactory)
|
||||
.createIntroductionGroup(introducee1);
|
||||
will(returnValue(introductionGroup1));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);
|
||||
will(returnValue(state));
|
||||
oneOf(introduceeManager).acceptIntroduction(txn, state, time);
|
||||
oneOf(introduceeManager).acceptIntroduction(with(equal(txn)),
|
||||
with(any(IntroduceeSessionState.class)), with(equal(time)));
|
||||
oneOf(db).endTransaction(txn);
|
||||
}});
|
||||
|
||||
@@ -176,8 +220,27 @@ public class IntroductionManagerImplTest extends BriarTestCase {
|
||||
@Test
|
||||
public void testDeclineIntroduction() throws DbException, FormatException {
|
||||
final BdfDictionary state = BdfDictionary.of(
|
||||
new BdfEntry(ROLE, ROLE_INTRODUCEE),
|
||||
new BdfEntry(GROUP_ID_1, introductionGroup1.getId()),
|
||||
new BdfEntry(GROUP_ID_2, introductionGroup2.getId())
|
||||
new BdfEntry(GROUP_ID, introductionGroup1.getId()),
|
||||
new BdfEntry(GROUP_ID_2, introductionGroup2.getId()),
|
||||
new BdfEntry(SESSION_ID, sessionId),
|
||||
new BdfEntry(STORAGE_ID, sessionId),
|
||||
new BdfEntry(AUTHOR_ID_1, introducee1.getAuthor().getId()),
|
||||
new BdfEntry(CONTACT_1, introducee1.getAuthor().getName()),
|
||||
new BdfEntry(CONTACT_ID_1, introducee1.getId().getInt()),
|
||||
new BdfEntry(AUTHOR_ID_2, introducee2.getAuthor().getId()),
|
||||
new BdfEntry(CONTACT_2, introducee2.getAuthor().getName()),
|
||||
new BdfEntry(CONTACT_ID_2, introducee2.getId().getInt()),
|
||||
new BdfEntry(STATE, AWAIT_REQUEST.getValue()),
|
||||
new BdfEntry(TIME, time),
|
||||
new BdfEntry(OUR_TIME, time),
|
||||
new BdfEntry(NAME, introducee1.getAuthor().getName()),
|
||||
new BdfEntry(LOCAL_AUTHOR_ID, introducee1.getLocalAuthorId()),
|
||||
new BdfEntry(INTRODUCER, introducee1.getAuthor().getName()),
|
||||
new BdfEntry(EXISTS, false),
|
||||
new BdfEntry(TASK, NO_TASK),
|
||||
new BdfEntry(REMOTE_AUTHOR_IS_US, false)
|
||||
);
|
||||
txn = new Transaction(null, false);
|
||||
|
||||
@@ -186,11 +249,13 @@ public class IntroductionManagerImplTest extends BriarTestCase {
|
||||
will(returnValue(txn));
|
||||
oneOf(db).getContact(txn, introducee1.getId());
|
||||
will(returnValue(introducee1));
|
||||
oneOf(introductionGroupFactory).createIntroductionGroup(introducee1);
|
||||
oneOf(introductionGroupFactory)
|
||||
.createIntroductionGroup(introducee1);
|
||||
will(returnValue(introductionGroup1));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);
|
||||
will(returnValue(state));
|
||||
oneOf(introduceeManager).declineIntroduction(txn, state, time);
|
||||
oneOf(introduceeManager).declineIntroduction(with(equal(txn)),
|
||||
with(any(IntroduceeSessionState.class)), with(equal(time)));
|
||||
oneOf(db).endTransaction(txn);
|
||||
}});
|
||||
|
||||
@@ -214,7 +279,8 @@ public class IntroductionManagerImplTest extends BriarTestCase {
|
||||
will(returnValue(txn));
|
||||
oneOf(db).getContact(txn, introducee1.getId());
|
||||
will(returnValue(introducee1));
|
||||
oneOf(introductionGroupFactory).createIntroductionGroup(introducee1);
|
||||
oneOf(introductionGroupFactory)
|
||||
.createIntroductionGroup(introducee1);
|
||||
will(returnValue(introductionGroup1));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
introductionGroup1.getId());
|
||||
@@ -238,19 +304,23 @@ public class IntroductionManagerImplTest extends BriarTestCase {
|
||||
final BdfDictionary msg = new BdfDictionary();
|
||||
msg.put(TYPE, TYPE_REQUEST);
|
||||
|
||||
final BdfDictionary state = new BdfDictionary();
|
||||
final IntroduceeSessionState state = initializeIntroduceeSS();
|
||||
txn = new Transaction(null, false);
|
||||
final SessionId sessionId = new SessionId(TestUtils.getRandomId());
|
||||
msg.put(SESSION_ID, sessionId);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
new MessageId(sessionId.getBytes()));
|
||||
will(returnValue(state.toBdfDictionary()));
|
||||
oneOf(introduceeManager)
|
||||
.initialize(txn, introductionGroup1.getId(), msg);
|
||||
.initialize(txn, sessionId, introductionGroup1.getId(),
|
||||
msg);
|
||||
will(returnValue(state));
|
||||
oneOf(introduceeManager)
|
||||
.incomingMessage(txn, state, msg);
|
||||
oneOf(introduceeManager).incomingMessage(txn, state, msg);
|
||||
}});
|
||||
|
||||
introductionManager
|
||||
.incomingMessage(txn, message1, new BdfList(), msg);
|
||||
introductionManager.incomingMessage(txn, message1, new BdfList(), msg);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
assertFalse(txn.isComplete());
|
||||
@@ -265,17 +335,16 @@ public class IntroductionManagerImplTest extends BriarTestCase {
|
||||
new BdfEntry(SESSION_ID, sessionId)
|
||||
);
|
||||
|
||||
final BdfDictionary state = new BdfDictionary();
|
||||
state.put(ROLE, ROLE_INTRODUCER);
|
||||
state.put(GROUP_ID_1, introductionGroup1.getId());
|
||||
state.put(GROUP_ID_2, introductionGroup2.getId());
|
||||
final IntroducerSessionState sessionState = initializeIntroducerSS();
|
||||
final BdfDictionary state = sessionState.toBdfDictionary();
|
||||
|
||||
txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);
|
||||
will(returnValue(state));
|
||||
oneOf(introducerManager).incomingMessage(txn, state, msg);
|
||||
oneOf(introducerManager).incomingMessage(with(equal(txn)),
|
||||
with(any(IntroducerSessionState.class)), with(equal(msg)));
|
||||
}});
|
||||
|
||||
introductionManager
|
||||
@@ -285,5 +354,46 @@ public class IntroductionManagerImplTest extends BriarTestCase {
|
||||
assertFalse(txn.isComplete());
|
||||
}
|
||||
|
||||
private IntroduceeSessionState initializeIntroduceeSS() {
|
||||
|
||||
final ContactId cid = new ContactId(0);
|
||||
final AuthorId aid = new AuthorId(TestUtils.getRandomId());
|
||||
Author author = new Author(aid, "Introducer",
|
||||
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||
final Contact introducer = new Contact(cid, author, aid, true, false);
|
||||
final IntroduceeSessionState state = new IntroduceeSessionState(
|
||||
new MessageId(TestUtils.getRandomId()),
|
||||
new SessionId(TestUtils.getRandomId()),
|
||||
new GroupId(TestUtils.getRandomId()),
|
||||
introducer.getId(), introducer.getAuthor().getId(),
|
||||
introducer.getAuthor().getName(), introducer.getLocalAuthorId(),
|
||||
AWAIT_REQUEST);
|
||||
|
||||
state.setContactExists(true);
|
||||
state.setRemoteAuthorIsUs(false);
|
||||
state.setRemoteAuthorId(introducee2.getAuthor().getId());
|
||||
state.setIntroducedName(introducee2.getAuthor().getName());
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
private IntroducerSessionState initializeIntroducerSS() {
|
||||
final ContactId cid = new ContactId(0);
|
||||
final AuthorId aid = new AuthorId(TestUtils.getRandomId());
|
||||
Author author = new Author(aid, "Introducer",
|
||||
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||
final Contact introducer = new Contact(cid, author, aid, true, false);
|
||||
return new IntroducerSessionState(
|
||||
new MessageId(TestUtils.getRandomId()),
|
||||
new SessionId(TestUtils.getRandomId()),
|
||||
introductionGroup1.getId(),
|
||||
introductionGroup2.getId(),
|
||||
introducer.getId(), introducer.getAuthor().getId(),
|
||||
introducer.getAuthor().getName(),
|
||||
introducer.getId(), introducer.getAuthor().getId(),
|
||||
introducer.getAuthor().getName(),
|
||||
IntroducerProtocolState.AWAIT_RESPONSES);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user