Address first round of review comments for new IntroductionClient

parent a9b678df
......@@ -8,7 +8,6 @@ import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
// TODO still needed?
public class IntroductionSucceededEvent extends Event {
private final Contact contact;
......
......@@ -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);
}
}
......@@ -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;
......
......@@ -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);
}
}
}
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))
......
......@@ -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";
......
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
......
......@@ -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,28 +123,16 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
byte[] ephemeralPublicKey, byte[] remoteEphemeralPublicKey,
Map<TransportId, TransportProperties> transportProperties,
Map<TransportId, TransportProperties> remoteTransportProperties,
boolean alice) throws FormatException {
BdfList localInfo = BdfList.of(
localAuthorId,
acceptTimestamp,
ephemeralPublicKey,
clientHelper.toDictionary(transportProperties)
);
BdfList remoteInfo = BdfList.of(
remoteAuthorId,
remoteAcceptTimestamp,
remoteEphemeralPublicKey,
clientHelper.toDictionary(remoteTransportProperties)
);
BdfList macList = BdfList.of(
introducerId,
alice ? localInfo : remoteInfo,
alice ? remoteInfo : localInfo
);
boolean alice) {
byte[] inputs =
getMacInputs(introducerId, localAuthorId, remoteAuthorId,
acceptTimestamp, remoteAcceptTimestamp,
ephemeralPublicKey, remoteEphemeralPublicKey,
transportProperties, remoteTransportProperties, alice);
return crypto.mac(
LABEL_AUTH_MAC,
macKey,
clientHelper.toByteArray(macList)
inputs
);
}
......@@ -153,7 +140,7 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
@SuppressWarnings("ConstantConditions")
public void verifyMac(byte[] mac, IntroduceeSession s,
AuthorId localAuthorId)
throws GeneralSecurityException, FormatException {
throws GeneralSecurityException {
boolean alice = isAlice(localAuthorId, s.getRemoteAuthor().getId());
verifyMac(mac, new SecretKey(s.getMasterKey()),
s.getIntroducer().getId(), localAuthorId,
......@@ -170,18 +157,49 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
byte[] remoteEphemeralPublicKey,
Map<TransportId, TransportProperties> transportProperties,
Map<TransportId, TransportProperties> remoteTransportProperties,
boolean alice) throws GeneralSecurityException, FormatException {
boolean alice) throws GeneralSecurityException {
SecretKey macKey = deriveMacKey(masterKey, alice);
byte[] calculatedMac =
mac(macKey, introducerId, localAuthorId, remoteAuthorId,
byte[] inputs =
getMacInputs(introducerId, localAuthorId, remoteAuthorId,
acceptTimestamp, remoteAcceptTimestamp,
ephemeralPublicKey, remoteEphemeralPublicKey,
transportProperties, remoteTransportProperties, !alice);
if (!Arrays.equals(mac, calculatedMac)) {
if (!crypto.verifyMac(mac, LABEL_AUTH_MAC, macKey, inputs)) {
throw new GeneralSecurityException();
}
}