Address first round of review comments for new IntroductionClient

This commit is contained in:
Torsten Grote
2018-04-25 10:12:49 -03:00
parent a9b678df32
commit 72e9a9d807
17 changed files with 208 additions and 239 deletions

View File

@@ -8,7 +8,6 @@ import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
// TODO still needed?
public class IntroductionSucceededEvent extends Event {
private final Contact contact;

View File

@@ -57,19 +57,14 @@ public abstract class BdfIncomingMessageHook implements IncomingMessageHook {
public boolean incomingMessage(Transaction txn, Message m, Metadata meta)
throws DbException, InvalidMessageException {
try {
return incomingMessage(txn, m, meta, MESSAGE_HEADER_LENGTH);
byte[] raw = m.getRaw();
BdfList body = clientHelper.toList(raw, MESSAGE_HEADER_LENGTH,
raw.length - MESSAGE_HEADER_LENGTH);
BdfDictionary metaDictionary = metadataParser.parse(meta);
return incomingMessage(txn, m, body, metaDictionary);
} catch (FormatException e) {
throw new InvalidMessageException(e);
}
}
private boolean incomingMessage(Transaction txn, Message m, Metadata meta,
int headerLength) throws DbException, FormatException {
byte[] raw = m.getRaw();
BdfList body = clientHelper.toList(raw, headerLength,
raw.length - headerLength);
BdfDictionary metaDictionary = metadataParser.parse(meta);
return incomingMessage(txn, m, body, metaDictionary);
}
}

View File

@@ -13,7 +13,6 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
@@ -25,8 +24,6 @@ import java.util.Map;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_VERSION;
import static org.briarproject.briar.introduction.MessageType.ABORT;
import static org.briarproject.briar.introduction.MessageType.ACCEPT;
import static org.briarproject.briar.introduction.MessageType.ACTIVATE;
@@ -155,28 +152,6 @@ abstract class AbstractProtocolEngine<S extends Session>
}
}
void markRequestUnavailableToAnswer(Transaction txn, MessageId m)
throws DbException {
BdfDictionary meta = new BdfDictionary();
messageEncoder.setAvailableToAnswer(meta, false);
try {
clientHelper.mergeMessageMetadata(txn, m, meta);
} catch (FormatException e) {
throw new AssertionError(e);
}
}
Map<MessageId, BdfDictionary> getSessions(Transaction txn,
BdfDictionary query) throws DbException, FormatException {
return clientHelper
.getMessageMetadataAsDictionary(txn, getLocalGroup().getId(),
query);
}
private Group getLocalGroup() {
return contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION);
}
boolean isInvalidDependency(@Nullable MessageId lastRemoteMessageId,
@Nullable MessageId dependency) {
if (dependency == null) return lastRemoteMessageId != null;

View File

@@ -124,8 +124,7 @@ class IntroduceeProtocolEngine
@Override
public IntroduceeSession onRequestMessage(Transaction txn,
IntroduceeSession session, RequestMessage m)
throws DbException, FormatException {
IntroduceeSession session, RequestMessage m) throws DbException {
switch (session.getState()) {
case START:
return onRemoteRequest(txn, session, m);
@@ -143,8 +142,7 @@ class IntroduceeProtocolEngine
@Override
public IntroduceeSession onAcceptMessage(Transaction txn,
IntroduceeSession session, AcceptMessage m)
throws DbException, FormatException {
IntroduceeSession session, AcceptMessage m) throws DbException {
switch (session.getState()) {
case START:
return onRemoteResponseInStart(txn, session, m);
@@ -163,8 +161,7 @@ class IntroduceeProtocolEngine
@Override
public IntroduceeSession onDeclineMessage(Transaction txn,
IntroduceeSession session, DeclineMessage m)
throws DbException, FormatException {
IntroduceeSession session, DeclineMessage m) throws DbException {
switch (session.getState()) {
case START:
return onRemoteResponseInStart(txn, session, m);
@@ -183,8 +180,7 @@ class IntroduceeProtocolEngine
@Override
public IntroduceeSession onAuthMessage(Transaction txn,
IntroduceeSession session, AuthMessage m)
throws DbException, FormatException {
IntroduceeSession session, AuthMessage m) throws DbException {
switch (session.getState()) {
case AWAIT_AUTH:
return onRemoteAuth(txn, session, m);
@@ -202,8 +198,7 @@ class IntroduceeProtocolEngine
@Override
public IntroduceeSession onActivateMessage(Transaction txn,
IntroduceeSession session, ActivateMessage m)
throws DbException, FormatException {
IntroduceeSession session, ActivateMessage m) throws DbException {
switch (session.getState()) {
case AWAIT_ACTIVATE:
return onRemoteActivate(txn, session, m);
@@ -221,8 +216,7 @@ class IntroduceeProtocolEngine
@Override
public IntroduceeSession onAbortMessage(Transaction txn,
IntroduceeSession session, AbortMessage m)
throws DbException, FormatException {
IntroduceeSession session, AbortMessage m) throws DbException {
return onRemoteAbort(txn, session, m);
}
@@ -232,8 +226,9 @@ class IntroduceeProtocolEngine
if (isInvalidDependency(s, m.getPreviousMessageId()))
return abort(txn, s);
// Mark the request visible in the UI
// Mark the request visible in the UI and available to answer
markMessageVisibleInUi(txn, m.getMessageId());
markRequestAvailableToAnswer(txn, m.getMessageId(), true);
// Add SessionId to message metadata
addSessionId(txn, m.getMessageId(), s.getSessionId());
@@ -243,9 +238,11 @@ class IntroduceeProtocolEngine
.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
// Broadcast IntroductionRequestReceivedEvent
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
Contact c = contactManager.getContact(txn, s.getIntroducer().getId(),
identityManager.getLocalAuthor(txn).getId());
boolean contactExists = false; // TODO
localAuthor.getId());
boolean contactExists = contactManager
.contactExists(txn, m.getAuthor().getId(), localAuthor.getId());
IntroductionRequest request =
new IntroductionRequest(s.getSessionId(), m.getMessageId(),
m.getGroupId(), INTRODUCEE, m.getTimestamp(), false,
@@ -260,11 +257,10 @@ class IntroduceeProtocolEngine
}
private IntroduceeSession onLocalAccept(Transaction txn,
IntroduceeSession s, long timestamp) throws DbException {
IntroduceeSession s, long timestamp)
throws DbException {
// Mark the request message unavailable to answer
MessageId requestId = s.getLastRemoteMessageId();
if (requestId == null) throw new IllegalStateException();
markRequestUnavailableToAnswer(txn, requestId);
markRequestsUnavailableToAnswer(txn, s);
// Create ephemeral key pair and get local transport properties
KeyPair keyPair = crypto.generateKeyPair();
@@ -275,7 +271,7 @@ class IntroduceeProtocolEngine
// Send a ACCEPT message
long localTimestamp =
Math.max(timestamp, getLocalTimestamp(s));
Math.max(timestamp + 1, getLocalTimestamp(s));
Message sent = sendAcceptMessage(txn, s, localTimestamp, publicKey,
localTimestamp, transportProperties, true);
// Track the message
@@ -297,14 +293,13 @@ class IntroduceeProtocolEngine
}
private IntroduceeSession onLocalDecline(Transaction txn,
IntroduceeSession s, long timestamp) throws DbException {
IntroduceeSession s, long timestamp)
throws DbException {
// Mark the request message unavailable to answer
MessageId requestId = s.getLastRemoteMessageId();
if (requestId == null) throw new IllegalStateException();
markRequestUnavailableToAnswer(txn, requestId);
markRequestsUnavailableToAnswer(txn, s);
// Send a DECLINE message
long localTimestamp = Math.max(timestamp, getLocalTimestamp(s));
long localTimestamp = Math.max(timestamp + 1, getLocalTimestamp(s));
Message sent = sendDeclineMessage(txn, s, localTimestamp, true);
// Track the message
messageTracker.trackOutgoingMessage(txn, sent);
@@ -316,7 +311,7 @@ class IntroduceeProtocolEngine
private IntroduceeSession onRemoteAccept(Transaction txn,
IntroduceeSession s, AcceptMessage m)
throws DbException, FormatException {
throws DbException {
// The timestamp must be higher than the last request message
if (m.getTimestamp() <= s.getRequestTimestamp())
return abort(txn, s);
@@ -346,7 +341,7 @@ class IntroduceeProtocolEngine
if (isInvalidDependency(s, m.getPreviousMessageId()))
return abort(txn, s);
// Mark the request visible in the UI
// Mark the response visible in the UI
markMessageVisibleInUi(txn, m.getMessageId());
// Track the incoming message
@@ -401,8 +396,6 @@ class IntroduceeProtocolEngine
} catch (GeneralSecurityException e) {
// TODO
return abort(txn, s);
} catch (FormatException e) {
throw new AssertionError(e);
}
if (s.getState() != AWAIT_AUTH) throw new AssertionError();
Message sent = sendAuthMessage(txn, s, getLocalTimestamp(s), mac,
@@ -411,8 +404,7 @@ class IntroduceeProtocolEngine
}
private IntroduceeSession onRemoteAuth(Transaction txn,
IntroduceeSession s, AuthMessage m)
throws DbException, FormatException {
IntroduceeSession s, AuthMessage m) throws DbException {
// The dependency, if any, must be the last remote message
if (isInvalidDependency(s, m.getPreviousMessageId()))
return abort(txn, s);
@@ -479,9 +471,7 @@ class IntroduceeProtocolEngine
IntroduceeSession s, AbortMessage m)
throws DbException {
// Mark the request message unavailable to answer
MessageId requestId = s.getLastRemoteMessageId();
if (requestId == null) throw new IllegalStateException();
markRequestUnavailableToAnswer(txn, requestId);
markRequestsUnavailableToAnswer(txn, s);
// Broadcast abort event for testing
txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
@@ -495,9 +485,7 @@ class IntroduceeProtocolEngine
private IntroduceeSession abort(Transaction txn, IntroduceeSession s)
throws DbException {
// Mark the request message unavailable to answer
MessageId requestId = s.getLastRemoteMessageId();
if (requestId == null) throw new IllegalStateException();
markRequestUnavailableToAnswer(txn, requestId);
markRequestsUnavailableToAnswer(txn, s);
// Send an ABORT message
Message sent = sendAbortMessage(txn, s, getLocalTimestamp(s));
@@ -537,4 +525,30 @@ class IntroduceeProtocolEngine
}
}
private void markRequestsUnavailableToAnswer(Transaction txn,
IntroduceeSession s) throws DbException {
BdfDictionary query = messageParser
.getRequestsAvailableToAnswerQuery(s.getSessionId());
try {
Map<MessageId, BdfDictionary> results =
clientHelper.getMessageMetadataAsDictionary(txn,
s.getContactGroupId(), query);
for (MessageId m : results.keySet())
markRequestAvailableToAnswer(txn, m, false);
} catch (FormatException e) {
throw new AssertionError(e);
}
}
private void markRequestAvailableToAnswer(Transaction txn, MessageId m,
boolean available) throws DbException {
BdfDictionary meta = new BdfDictionary();
messageEncoder.setAvailableToAnswer(meta, available);
try {
clientHelper.mergeMessageMetadata(txn, m, meta);
} catch (FormatException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -1,11 +1,9 @@
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
@@ -23,8 +21,6 @@ import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent;
import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent;
import org.briarproject.briar.introduction.IntroducerSession.Introducee;
import java.util.Map;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@@ -97,21 +93,19 @@ class IntroducerProtocolEngine
}
IntroducerSession onAbortAction(Transaction txn, IntroducerSession s)
throws DbException, FormatException {
throws DbException {
return abort(txn, s);
}
@Override
public IntroducerSession onRequestMessage(Transaction txn,
IntroducerSession s, RequestMessage m)
throws DbException, FormatException {
IntroducerSession s, RequestMessage m) throws DbException {
return abort(txn, s); // Invalid in this role
}
@Override
public IntroducerSession onAcceptMessage(Transaction txn,
IntroducerSession s, AcceptMessage m)
throws DbException, FormatException {
IntroducerSession s, AcceptMessage m) throws DbException {
switch (s.getState()) {
case AWAIT_RESPONSES:
case AWAIT_RESPONSE_A:
@@ -133,8 +127,7 @@ class IntroducerProtocolEngine
@Override
public IntroducerSession onDeclineMessage(Transaction txn,
IntroducerSession s, DeclineMessage m)
throws DbException, FormatException {
IntroducerSession s, DeclineMessage m) throws DbException {
switch (s.getState()) {
case AWAIT_RESPONSES:
case AWAIT_RESPONSE_A:
@@ -156,7 +149,7 @@ class IntroducerProtocolEngine
@Override
public IntroducerSession onAuthMessage(Transaction txn, IntroducerSession s,
AuthMessage m) throws DbException, FormatException {
AuthMessage m) throws DbException {
switch (s.getState()) {
case AWAIT_AUTHS:
case AWAIT_AUTH_A:
@@ -177,8 +170,7 @@ class IntroducerProtocolEngine
@Override
public IntroducerSession onActivateMessage(Transaction txn,
IntroducerSession s, ActivateMessage m)
throws DbException, FormatException {
IntroducerSession s, ActivateMessage m) throws DbException {
switch (s.getState()) {
case AWAIT_ACTIVATES:
case AWAIT_ACTIVATE_A:
@@ -199,8 +191,7 @@ class IntroducerProtocolEngine
@Override
public IntroducerSession onAbortMessage(Transaction txn,
IntroducerSession s, AbortMessage m)
throws DbException, FormatException {
IntroducerSession s, AbortMessage m) throws DbException {
return onRemoteAbort(txn, s, m);
}
@@ -227,8 +218,7 @@ class IntroducerProtocolEngine
}
private IntroducerSession onRemoteAccept(Transaction txn,
IntroducerSession s, AcceptMessage m)
throws DbException, FormatException {
IntroducerSession s, AcceptMessage m) throws DbException {
// The timestamp must be higher than the last request message
if (m.getTimestamp() <= s.getRequestTimestamp())
return abort(txn, s);
@@ -284,8 +274,7 @@ class IntroducerProtocolEngine
}
private IntroducerSession onRemoteDecline(Transaction txn,
IntroducerSession s, DeclineMessage m)
throws DbException, FormatException {
IntroducerSession s, DeclineMessage m) throws DbException {
// The timestamp must be higher than the last request message
if (m.getTimestamp() <= s.getRequestTimestamp())
return abort(txn, s);
@@ -337,7 +326,7 @@ class IntroducerProtocolEngine
private IntroducerSession onRemoteResponseInStart(Transaction txn,
IntroducerSession s, AbstractIntroductionMessage m)
throws DbException, FormatException {
throws DbException {
// The timestamp must be higher than the last request message
if (m.getTimestamp() <= s.getRequestTimestamp())
return abort(txn, s);
@@ -384,8 +373,7 @@ class IntroducerProtocolEngine
}
private IntroducerSession onRemoteAuth(Transaction txn,
IntroducerSession s, AuthMessage m)
throws DbException, FormatException {
IntroducerSession s, AuthMessage m) throws DbException {
// The dependency, if any, must be the last remote message
if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId()))
return abort(txn, s);
@@ -413,8 +401,7 @@ class IntroducerProtocolEngine
}
private IntroducerSession onRemoteActivate(Transaction txn,
IntroducerSession s, ActivateMessage m)
throws DbException, FormatException {
IntroducerSession s, ActivateMessage m) throws DbException {
// The dependency, if any, must be the last remote message
if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId()))
return abort(txn, s);
@@ -441,11 +428,7 @@ class IntroducerProtocolEngine
}
private IntroducerSession onRemoteAbort(Transaction txn,
IntroducerSession s, AbortMessage m)
throws DbException, FormatException {
// Mark any REQUEST messages in the session unavailable to answer
markRequestsUnavailableToAnswer(txn, s);
IntroducerSession s, AbortMessage m) throws DbException {
// Forward ABORT message
Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i);
@@ -468,10 +451,7 @@ class IntroducerProtocolEngine
}
private IntroducerSession abort(Transaction txn,
IntroducerSession s) throws DbException, FormatException {
// Mark any REQUEST messages in the session unavailable to answer
markRequestsUnavailableToAnswer(txn, s);
IntroducerSession s) throws DbException {
// Broadcast abort event for testing
txn.attach(new IntroductionAbortedEvent(s.getSessionId()));
@@ -487,15 +467,6 @@ class IntroducerProtocolEngine
s.getRequestTimestamp(), introducee1, introducee2);
}
private void markRequestsUnavailableToAnswer(Transaction txn, Session s)
throws DbException, FormatException {
BdfDictionary query = messageParser
.getInvitesAvailableToAnswerQuery(s.getSessionId());
Map<MessageId, BdfDictionary> results = getSessions(txn, query);
for (MessageId m : results.keySet())
markRequestUnavailableToAnswer(txn, m);
}
private Introducee getIntroducee(IntroducerSession s, GroupId g) {
if (s.getIntroducee1().groupId.equals(g)) return s.getIntroducee1();
else if (s.getIntroducee2().groupId.equals(g))

View File

@@ -12,7 +12,6 @@ interface IntroductionConstants {
String MSG_KEY_LOCAL = "local";
String MSG_KEY_VISIBLE_IN_UI = "visibleInUi";
String MSG_KEY_AVAILABLE_TO_ANSWER = "availableToAnswer";
String MSG_KEY_INVITATION_ACCEPTED = "invitationAccepted";
// Session Keys
String SESSION_KEY_SESSION_ID = "sessionId";

View File

@@ -1,6 +1,5 @@
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author;
@@ -15,15 +14,13 @@ interface IntroductionCrypto {
/**
* Returns the {@link SessionId} based on the introducer
* and the two introducees.
*
* Note: The roles of Alice and Bob can be switched.
*/
SessionId getSessionId(Author introducer, Author alice, Author bob);
SessionId getSessionId(Author introducer, Author local, Author remote);
/**
* Returns true if the first author is indeed alice
* Returns true if the local author is alice
*/
boolean isAlice(AuthorId alice, AuthorId bob);
boolean isAlice(AuthorId local, AuthorId remote);
/**
* Generates an agreement key pair.
@@ -49,11 +46,11 @@ interface IntroductionCrypto {
SecretKey deriveMacKey(SecretKey masterKey, boolean alice);
/**
* Generates a MAC that covers both introducee's ephemeral public keys and
* transport properties.
* Generates a MAC that covers both introducee's ephemeral public keys,
* transport properties, Author IDs and timestamps of the accept message.
*/
byte[] mac(SecretKey macKey, IntroduceeSession s, AuthorId localAuthorId,
boolean alice) throws FormatException;
boolean alice);
/**
* Verifies a received MAC
@@ -63,7 +60,7 @@ interface IntroductionCrypto {
* @throws GeneralSecurityException if the verification fails
*/
void verifyMac(byte[] mac, IntroduceeSession s, AuthorId localAuthorId)
throws GeneralSecurityException, FormatException;
throws GeneralSecurityException;
/**
* Signs a nonce derived from the macKey

View File

@@ -18,7 +18,6 @@ import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.briar.api.client.SessionId;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Map;
import javax.annotation.concurrent.Immutable;
@@ -49,14 +48,14 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
}
@Override
public SessionId getSessionId(Author introducer, Author alice,
Author bob) {
boolean isAlice = isAlice(alice.getId(), bob.getId());
public SessionId getSessionId(Author introducer, Author local,
Author remote) {
boolean isAlice = isAlice(local.getId(), remote.getId());
byte[] hash = crypto.hash(
LABEL_SESSION_ID,
introducer.getId().getBytes(),
isAlice ? alice.getId().getBytes() : bob.getId().getBytes(),
isAlice ? bob.getId().getBytes() : alice.getId().getBytes()
isAlice ? local.getId().getBytes() : remote.getId().getBytes(),
isAlice ? remote.getId().getBytes() : local.getId().getBytes()
);
return new SessionId(hash);
}
@@ -67,9 +66,9 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
}
@Override
public boolean isAlice(AuthorId alice, AuthorId bob) {
byte[] a = alice.getBytes();
byte[] b = bob.getBytes();
public boolean isAlice(AuthorId local, AuthorId remote) {
byte[] a = local.getBytes();
byte[] b = remote.getBytes();
return Bytes.COMPARATOR.compare(new Bytes(a), new Bytes(b)) < 0;
}
@@ -110,7 +109,7 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
@Override
@SuppressWarnings("ConstantConditions")
public byte[] mac(SecretKey macKey, IntroduceeSession s,
AuthorId localAuthorId, boolean alice) throws FormatException {
AuthorId localAuthorId, boolean alice) {
return mac(macKey, s.getIntroducer().getId(), localAuthorId,
s.getRemoteAuthor().getId(), s.getAcceptTimestamp(),
s.getRemoteAcceptTimestamp(), s.getEphemeralPublicKey(),
@@ -124,7 +123,59 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
byte[] ephemeralPublicKey, byte[] remoteEphemeralPublicKey,
Map<TransportId, TransportProperties> transportProperties,
Map<TransportId, TransportProperties> remoteTransportProperties,
boolean alice) throws FormatException {
boolean alice) {
byte[] inputs =
getMacInputs(introducerId, localAuthorId, remoteAuthorId,
acceptTimestamp, remoteAcceptTimestamp,
ephemeralPublicKey, remoteEphemeralPublicKey,
transportProperties, remoteTransportProperties, alice);
return crypto.mac(
LABEL_AUTH_MAC,
macKey,
inputs
);
}
@Override
@SuppressWarnings("ConstantConditions")
public void verifyMac(byte[] mac, IntroduceeSession s,
AuthorId localAuthorId)
throws GeneralSecurityException {
boolean alice = isAlice(localAuthorId, s.getRemoteAuthor().getId());
verifyMac(mac, new SecretKey(s.getMasterKey()),
s.getIntroducer().getId(), localAuthorId,
s.getRemoteAuthor().getId(), s.getAcceptTimestamp(),
s.getRemoteAcceptTimestamp(), s.getEphemeralPublicKey(),
s.getRemotePublicKey(), s.getTransportProperties(),
s.getRemoteTransportProperties(), !alice);
}
void verifyMac(byte[] mac, SecretKey masterKey,
AuthorId introducerId, AuthorId localAuthorId,
AuthorId remoteAuthorId, long acceptTimestamp,
long remoteAcceptTimestamp, byte[] ephemeralPublicKey,
byte[] remoteEphemeralPublicKey,
Map<TransportId, TransportProperties> transportProperties,
Map<TransportId, TransportProperties> remoteTransportProperties,
boolean alice) throws GeneralSecurityException {
SecretKey macKey = deriveMacKey(masterKey, alice);
byte[] inputs =
getMacInputs(introducerId, localAuthorId, remoteAuthorId,
acceptTimestamp, remoteAcceptTimestamp,
ephemeralPublicKey, remoteEphemeralPublicKey,
transportProperties, remoteTransportProperties, !alice);
if (!crypto.verifyMac(mac, LABEL_AUTH_MAC, macKey, inputs)) {
throw new GeneralSecurityException();
}
}
private byte[] getMacInputs(AuthorId introducerId,
AuthorId localAuthorId, AuthorId remoteAuthorId,
long acceptTimestamp, long remoteAcceptTimestamp,
byte[] ephemeralPublicKey, byte[] remoteEphemeralPublicKey,
Map<TransportId, TransportProperties> transportProperties,
Map<TransportId, TransportProperties> remoteTransportProperties,
boolean alice) {
BdfList localInfo = BdfList.of(
localAuthorId,
acceptTimestamp,
@@ -142,43 +193,10 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
alice ? localInfo : remoteInfo,
alice ? remoteInfo : localInfo
);
return crypto.mac(
LABEL_AUTH_MAC,
macKey,
clientHelper.toByteArray(macList)
);
}
@Override
@SuppressWarnings("ConstantConditions")
public void verifyMac(byte[] mac, IntroduceeSession s,
AuthorId localAuthorId)
throws GeneralSecurityException, FormatException {
boolean alice = isAlice(localAuthorId, s.getRemoteAuthor().getId());
verifyMac(mac, new SecretKey(s.getMasterKey()),
s.getIntroducer().getId(), localAuthorId,
s.getRemoteAuthor().getId(), s.getAcceptTimestamp(),
s.getRemoteAcceptTimestamp(), s.getEphemeralPublicKey(),
s.getRemotePublicKey(), s.getTransportProperties(),
s.getRemoteTransportProperties(), !alice);
}
void verifyMac(byte[] mac, SecretKey masterKey,
AuthorId introducerId, AuthorId localAuthorId,
AuthorId remoteAuthorId, long acceptTimestamp,
long remoteAcceptTimestamp, byte[] ephemeralPublicKey,
byte[] remoteEphemeralPublicKey,
Map<TransportId, TransportProperties> transportProperties,
Map<TransportId, TransportProperties> remoteTransportProperties,
boolean alice) throws GeneralSecurityException, FormatException {
SecretKey macKey = deriveMacKey(masterKey, alice);
byte[] calculatedMac =
mac(macKey, introducerId, localAuthorId, remoteAuthorId,
acceptTimestamp, remoteAcceptTimestamp,
ephemeralPublicKey, remoteEphemeralPublicKey,
transportProperties, remoteTransportProperties, !alice);
if (!Arrays.equals(mac, calculatedMac)) {
throw new GeneralSecurityException();
try {
return clientHelper.toByteArray(macList);
} catch (FormatException e) {
throw new AssertionError();
}
}
@@ -204,7 +222,8 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
void verifySignature(SecretKey macKey, byte[] publicKey,
byte[] signature) throws GeneralSecurityException {
byte[] nonce = getNonce(macKey);
if (!crypto.verify(LABEL_AUTH_SIGN, nonce, publicKey, signature)) {
if (!crypto.verifySignature(signature, LABEL_AUTH_SIGN, nonce,
publicKey)) {
throw new GeneralSecurityException();
}
}

View File

@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
@@ -37,6 +38,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@@ -59,6 +61,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
implements IntroductionManager, Client, ContactHook {
private final ContactGroupFactory contactGroupFactory;
private final ContactManager contactManager;
private final MessageParser messageParser;
private final SessionEncoder sessionEncoder;
private final SessionParser sessionParser;
@@ -74,6 +77,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
MetadataParser metadataParser,
MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory,
ContactManager contactManager,
MessageParser messageParser,
SessionEncoder sessionEncoder,
SessionParser sessionParser,
@@ -83,6 +87,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
IdentityManager identityManager) {
super(db, clientHelper, metadataParser, messageTracker);
this.contactGroupFactory = contactGroupFactory;
this.contactManager = contactManager;
this.messageParser = messageParser;
this.sessionEncoder = sessionEncoder;
this.sessionParser = sessionParser;
@@ -107,8 +112,6 @@ class IntroductionManagerImpl extends ConversationClientImpl
public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact
Group g = getContactGroup(c);
// Return if we've already set things up for this contact
if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact
db.addGroup(txn, g);
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
@@ -124,12 +127,9 @@ class IntroductionManagerImpl extends ConversationClientImpl
@Override
public void removingContact(Transaction txn, Contact c) throws DbException {
try {
removeSessionWithIntroducer(txn, c);
abortOrRemoveSessionWithIntroducee(txn, c);
} catch (FormatException e) {
throw new AssertionError();
}
removeSessionWithIntroducer(txn, c);
abortOrRemoveSessionWithIntroducee(txn, c);
// Remove the contact group (all messages will be removed with it)
db.removeGroup(txn, getContactGroup(c));
}
@@ -185,12 +185,12 @@ class IntroductionManagerImpl extends ConversationClientImpl
Message m, BdfList body) throws DbException, FormatException {
ContactId introducerId = getContactId(txn, m.getGroupId());
Author introducer = db.getContact(txn, introducerId).getAuthor();
Author alice = identityManager.getLocalAuthor(txn);
Author bob = messageParser.parseRequestMessage(m, body).getAuthor();
if (alice.equals(bob)) throw new FormatException();
SessionId sessionId = crypto.getSessionId(introducer, alice, bob);
Author local = identityManager.getLocalAuthor(txn);
Author remote = messageParser.parseRequestMessage(m, body).getAuthor();
if (local.equals(remote)) throw new FormatException();
SessionId sessionId = crypto.getSessionId(introducer, local, remote);
return IntroduceeSession
.getInitial(m.getGroupId(), sessionId, introducer, bob);
.getInitial(m.getGroupId(), sessionId, introducer, remote);
}
private <S extends Session> S handleMessage(Transaction txn, Message m,
@@ -249,7 +249,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
}
private void storeSession(Transaction txn, MessageId storageId,
Session session) throws DbException, FormatException {
Session session) throws DbException {
BdfDictionary d;
if (session.getRole() == INTRODUCER) {
d = sessionEncoder
@@ -260,7 +260,11 @@ class IntroductionManagerImpl extends ConversationClientImpl
} else {
throw new AssertionError();
}
clientHelper.mergeMessageMetadata(txn, storageId, d);
try {
clientHelper.mergeMessageMetadata(txn, storageId, d);
} catch (FormatException e) {
throw new AssertionError();
}
}
@Override
@@ -356,7 +360,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
Map<MessageId, BdfDictionary> results = clientHelper
.getMessageMetadataAsDictionary(txn, contactGroupId, query);
messages = new ArrayList<>(results.size());
for (Map.Entry<MessageId, BdfDictionary> e : results.entrySet()) {
for (Entry<MessageId, BdfDictionary> e : results.entrySet()) {
MessageId m = e.getKey();
MessageMetadata meta =
messageParser.parseMetadata(e.getValue());
@@ -394,11 +398,11 @@ class IntroductionManagerImpl extends ConversationClientImpl
Role role = sessionParser.getRole(bdfSession);
SessionId sessionId;
Author author;
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
if (role == INTRODUCER) {
IntroducerSession session =
sessionParser.parseIntroducerSession(bdfSession);
sessionId = session.getSessionId();
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
if (localAuthor.equals(session.getIntroducee1().author)) {
author = session.getIntroducee2().author;
} else {
@@ -410,8 +414,14 @@ class IntroductionManagerImpl extends ConversationClientImpl
sessionId = session.getSessionId();
author = session.getRemoteAuthor();
} else throw new AssertionError();
String message = ""; // TODO
boolean contactExists = false; // TODO
Message msg = clientHelper.getMessage(txn, m);
BdfList body = clientHelper.getMessageAsList(txn, m);
if (msg == null || body == null) throw new AssertionError();
RequestMessage rm = messageParser.parseRequestMessage(msg, body);
String message = rm.getMessage();
boolean contactExists = contactManager
.contactExists(txn, rm.getAuthor().getId(),
localAuthor.getId());
return new IntroductionRequest(sessionId, m, contactGroupId,
role, meta.getTimestamp(), meta.isLocal(),
@@ -449,12 +459,16 @@ class IntroductionManagerImpl extends ConversationClientImpl
}
private void removeSessionWithIntroducer(Transaction txn,
Contact introducer) throws DbException, FormatException {
Contact introducer) throws DbException {
BdfDictionary query = sessionEncoder
.getIntroduceeSessionsByIntroducerQuery(introducer.getAuthor());
Map<MessageId, BdfDictionary> sessions = clientHelper
.getMessageMetadataAsDictionary(txn, getLocalGroup().getId(),
query);
Map<MessageId, BdfDictionary> sessions;
try {
sessions = clientHelper.getMessageMetadataAsDictionary(txn,
getLocalGroup().getId(), query);
} catch (FormatException e) {
throw new AssertionError(e);
}
for (MessageId id : sessions.keySet()) {
db.deleteMessageMetadata(txn, id); // TODO needed?
db.removeMessage(txn, id);
@@ -462,16 +476,23 @@ class IntroductionManagerImpl extends ConversationClientImpl
}
private void abortOrRemoveSessionWithIntroducee(Transaction txn,
Contact c) throws DbException, FormatException {
Contact c) throws DbException {
BdfDictionary query = sessionEncoder.getIntroducerSessionsQuery();
Map<MessageId, BdfDictionary> sessions = clientHelper
.getMessageMetadataAsDictionary(txn, getLocalGroup().getId(),
query);
Map<MessageId, BdfDictionary> sessions;
try {
sessions = clientHelper.getMessageMetadataAsDictionary(txn,
getLocalGroup().getId(), query);
} catch (FormatException e) {
throw new AssertionError();
}
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
for (Map.Entry<MessageId, BdfDictionary> session : sessions
.entrySet()) {
IntroducerSession s =
sessionParser.parseIntroducerSession(session.getValue());
for (Entry<MessageId, BdfDictionary> session : sessions.entrySet()) {
IntroducerSession s;
try {
s = sessionParser.parseIntroducerSession(session.getValue());
} catch (FormatException e) {
throw new AssertionError();
}
if (s.getIntroducee1().author.equals(c.getAuthor())) {
abortOrRemoveSessionWithIntroducee(txn, s, session.getKey(),
s.getIntroducee2(), localAuthor);
@@ -484,7 +505,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
private void abortOrRemoveSessionWithIntroducee(Transaction txn,
IntroducerSession s, MessageId storageId, Introducee i,
LocalAuthor localAuthor) throws DbException, FormatException {
LocalAuthor localAuthor) throws DbException {
if (db.containsContact(txn, i.author.getId(), localAuthor.getId())) {
IntroducerSession session = introducerEngine.onAbortAction(txn, s);
storeSession(txn, storageId, session);

View File

@@ -78,8 +78,7 @@ class IntroductionValidator extends BdfMessageValidator {
checkLength(msg, 1, MAX_REQUEST_MESSAGE_LENGTH);
BdfDictionary meta = messageEncoder
.encodeRequestMetadata(m.getTimestamp(), false, false,
false, false);
.encodeRequestMetadata(m.getTimestamp(), false, false, false);
if (previousMessageId == null) {
return new BdfMessageContext(meta);
} else {

View File

@@ -18,7 +18,7 @@ import javax.annotation.Nullable;
interface MessageEncoder {
BdfDictionary encodeRequestMetadata(long timestamp, boolean local,
boolean read, boolean available, boolean accepted);
boolean read, boolean available);
BdfDictionary encodeMetadata(MessageType type,
@Nullable SessionId sessionId, long timestamp, boolean local,
@@ -30,8 +30,6 @@ interface MessageEncoder {
void setAvailableToAnswer(BdfDictionary meta, boolean available);
void setInvitationAccepted(BdfDictionary meta, boolean accepted);
Message encodeRequestMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, Author author,
@Nullable String message);

View File

@@ -21,7 +21,6 @@ import javax.inject.Inject;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_INVITATION_ACCEPTED;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_SESSION_ID;
@@ -49,12 +48,10 @@ class MessageEncoderImpl implements MessageEncoder {
@Override
public BdfDictionary encodeRequestMetadata(long timestamp,
boolean local, boolean read, boolean available,
boolean accepted) {
boolean local, boolean read, boolean available) {
BdfDictionary meta =
encodeMetadata(REQUEST, null, timestamp, local, read, false);
meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, available);
meta.put(MSG_KEY_INVITATION_ACCEPTED, accepted);
return meta;
}
@@ -90,11 +87,6 @@ class MessageEncoderImpl implements MessageEncoder {
meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, available);
}
@Override
public void setInvitationAccepted(BdfDictionary meta, boolean accepted) {
meta.put(MSG_KEY_INVITATION_ACCEPTED, accepted);
}
@Override
public Message encodeRequestMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, Author author,

View File

@@ -14,11 +14,11 @@ class MessageMetadata {
@Nullable
private final SessionId sessionId;
private final long timestamp;
private final boolean local, read, visible, available, accepted;
private final boolean local, read, visible, available;
MessageMetadata(MessageType type, @Nullable SessionId sessionId,
long timestamp, boolean local, boolean read, boolean visible,
boolean available, boolean accepted) {
boolean available) {
this.type = type;
this.sessionId = sessionId;
this.timestamp = timestamp;
@@ -26,7 +26,6 @@ class MessageMetadata {
this.read = read;
this.visible = visible;
this.available = available;
this.accepted = accepted;
}
MessageType getMessageType() {
@@ -58,8 +57,4 @@ class MessageMetadata {
return available;
}
public boolean wasAccepted() {
return accepted;
}
}

View File

@@ -12,7 +12,7 @@ interface MessageParser {
BdfDictionary getMessagesVisibleInUiQuery();
BdfDictionary getInvitesAvailableToAnswerQuery(SessionId sessionId);
BdfDictionary getRequestsAvailableToAnswerQuery(SessionId sessionId);
MessageMetadata parseMetadata(BdfDictionary meta) throws FormatException;

View File

@@ -19,7 +19,6 @@ import javax.inject.Inject;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_INVITATION_ACCEPTED;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.briar.introduction.IntroductionConstants.MSG_KEY_SESSION_ID;
@@ -43,7 +42,7 @@ class MessageParserImpl implements MessageParser {
}
@Override
public BdfDictionary getInvitesAvailableToAnswerQuery(SessionId sessionId) {
public BdfDictionary getRequestsAvailableToAnswerQuery(SessionId sessionId) {
return BdfDictionary.of(
new BdfEntry(MSG_KEY_AVAILABLE_TO_ANSWER, true),
new BdfEntry(MSG_KEY_MESSAGE_TYPE, REQUEST.getValue()),
@@ -64,9 +63,8 @@ class MessageParserImpl implements MessageParser {
boolean read = d.getBoolean(MSG_KEY_READ);
boolean visible = d.getBoolean(MSG_KEY_VISIBLE_IN_UI);
boolean available = d.getBoolean(MSG_KEY_AVAILABLE_TO_ANSWER, false);
boolean accepted = d.getBoolean(MSG_KEY_INVITATION_ACCEPTED, false);
return new MessageMetadata(type, sessionId, timestamp, local, read,
visible, available, accepted);
visible, available);
}
@Override

View File

@@ -397,7 +397,7 @@ public class IntroductionValidatorTest extends ValidatorTestCase {
context.checking(new Expectations() {{
oneOf(messageEncoder)
.encodeRequestMetadata(message.getTimestamp(), false, false,
false, false);
false);
will(returnValue(meta));
}});
}

View File

@@ -74,8 +74,7 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
@Test
public void testRequestMessageMetadata() throws FormatException {
BdfDictionary d = messageEncoder
.encodeRequestMetadata(timestamp, true, false, false,
true);
.encodeRequestMetadata(timestamp, true, false, false);
MessageMetadata meta = messageParser.parseMetadata(d);
assertEquals(REQUEST, meta.getMessageType());
@@ -85,7 +84,6 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
assertFalse(meta.isRead());
assertFalse(meta.isVisibleInConversation());
assertFalse(meta.isAvailableToAnswer());
assertTrue(meta.wasAccepted());
}
@Test
@@ -102,7 +100,6 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
assertTrue(meta.isRead());
assertFalse(meta.isVisibleInConversation());
assertFalse(meta.isAvailableToAnswer());
assertFalse(meta.wasAccepted());
}
@Test