Add a MAC to the ACTIVATE message to prevent the introducer to fake them

A fake ACTIVATE message would cause us to activate the transport keys
before the contact has received our auth message,
which would compromise forward secrecy.
parent 0e04044e
......@@ -26,4 +26,7 @@ public interface IntroductionConstants {
String LABEL_AUTH_NONCE = "org.briarproject.briar.introduction/AUTH_NONCE";
String LABEL_ACTIVATE_MAC =
"org.briarproject.briar.introduction/ACTIVATE_MAC";
}
......@@ -110,11 +110,11 @@ abstract class AbstractProtocolEngine<S extends Session>
return m;
}
Message sendActivateMessage(Transaction txn, PeerSession s, long timestamp)
throws DbException {
Message sendActivateMessage(Transaction txn, PeerSession s, long timestamp,
byte[] mac) throws DbException {
Message m = messageEncoder
.encodeActivateMessage(s.getContactGroupId(), timestamp,
s.getLastLocalMessageId(), s.getSessionId());
s.getLastLocalMessageId(), s.getSessionId(), mac);
sendMessage(txn, ACTIVATE, s.getSessionId(), m, false);
return m;
}
......
......@@ -12,15 +12,22 @@ import javax.annotation.concurrent.Immutable;
class ActivateMessage extends AbstractIntroductionMessage {
private final SessionId sessionId;
private final byte[] mac;
protected ActivateMessage(MessageId messageId, GroupId groupId,
long timestamp, MessageId previousMessageId, SessionId sessionId) {
long timestamp, MessageId previousMessageId, SessionId sessionId,
byte[] mac) {
super(messageId, groupId, timestamp, previousMessageId);
this.sessionId = sessionId;
this.mac = mac;
}
public SessionId getSessionId() {
return sessionId;
}
public byte[] getMac() {
return mac;
}
}
......@@ -4,7 +4,6 @@ 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.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.SecretKey;
......@@ -13,7 +12,6 @@ import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
......@@ -354,7 +352,7 @@ class IntroduceeProtocolEngine
IntroductionResponse request =
new IntroductionResponse(s.getSessionId(), m.getMessageId(),
m.getGroupId(), INTRODUCEE, m.getTimestamp(), false,
false, false, false, s.getRemoteAuthor().getName(),
false, false, false, s.getRemote().author.getName(),
false);
IntroductionResponseReceivedEvent e =
new IntroductionResponseReceivedEvent(c.getId(), request);
......@@ -383,16 +381,18 @@ class IntroduceeProtocolEngine
private IntroduceeSession onLocalAuth(Transaction txn, IntroduceeSession s)
throws DbException {
boolean alice = isAlice(txn, s);
byte[] mac;
byte[] signature;
SecretKey masterKey;
SecretKey masterKey, aliceMacKey, bobMacKey;
try {
masterKey = crypto.deriveMasterKey(s, alice);
SecretKey macKey = crypto.deriveMacKey(masterKey, alice);
masterKey = crypto.deriveMasterKey(s);
aliceMacKey = crypto.deriveMacKey(masterKey, true);
bobMacKey = crypto.deriveMacKey(masterKey, false);
SecretKey ourMacKey = s.getLocal().alice ? aliceMacKey : bobMacKey;
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
mac = crypto.mac(macKey, s, localAuthor.getId(), alice);
signature = crypto.sign(macKey, localAuthor.getPrivateKey());
mac = crypto.authMac(ourMacKey, s, localAuthor.getId(),
s.getLocal().alice);
signature = crypto.sign(ourMacKey, localAuthor.getPrivateKey());
} catch (GeneralSecurityException e) {
// TODO
return abort(txn, s);
......@@ -400,7 +400,8 @@ class IntroduceeProtocolEngine
if (s.getState() != AWAIT_AUTH) throw new AssertionError();
Message sent = sendAuthMessage(txn, s, getLocalTimestamp(s), mac,
signature);
return IntroduceeSession.addLocalAuth(s, AWAIT_AUTH, masterKey, sent);
return IntroduceeSession.addLocalAuth(s, AWAIT_AUTH, sent, masterKey,
aliceMacKey, bobMacKey);
}
private IntroduceeSession onRemoteAuth(Transaction txn,
......@@ -411,33 +412,50 @@ class IntroduceeProtocolEngine
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
try {
crypto.verifyMac(m.getMac(), s, localAuthor.getId());
crypto.verifyAuthMac(m.getMac(), s, localAuthor.getId());
crypto.verifySignature(m.getSignature(), s, localAuthor.getId());
} catch (GeneralSecurityException e) {
return abort(txn, s);
}
long timestamp =
Math.min(s.getAcceptTimestamp(), s.getRemoteAcceptTimestamp());
long timestamp = Math.min(s.getLocal().acceptTimestamp,
s.getRemote().acceptTimestamp);
if (timestamp == -1) throw new AssertionError();
Map<TransportId, KeySetId> keys = null;
boolean contactAdded = false;
try {
ContactId c = contactManager
.addContact(txn, s.getRemoteAuthor(), localAuthor.getId(),
false, false);
if (s.getRemoteTransportProperties() == null ||
s.getMasterKey() == null) throw new AssertionError();
transportPropertyManager.addRemoteProperties(txn, c,
s.getRemoteTransportProperties());
keys = keyManager
.addUnboundKeys(txn, new SecretKey(s.getMasterKey()),
timestamp, isAlice(txn, s));
contactManager
.addContact(txn, s.getRemote().author, localAuthor.getId(),
false, true);
contactAdded = true;
} catch (ContactExistsException e) {
// Ignore this and continue without adding transport properties
// or unbound transport keys. Continue with keys as null.
// Ignore this, because the other introducee might have deleted us.
// So we still want updated transport properties
// and new transport keys.
}
Contact c = contactManager.getContact(txn, s.getRemote().author.getId(),
localAuthor.getId());
// bind the keys to the new (or existing) contact
//noinspection ConstantConditions
Map<TransportId, KeySetId> keys = keyManager
.addUnboundKeys(txn, new SecretKey(s.getMasterKey()),
timestamp, s.getRemote().alice);
keyManager.bindKeys(txn, c.getId(), keys);
// add signed transport properties for the contact
//noinspection ConstantConditions
transportPropertyManager.addRemoteProperties(txn, c.getId(),
s.getRemote().transportProperties);
Message sent = sendActivateMessage(txn, s, getLocalTimestamp(s));
// send ACTIVATE message with a MAC
byte[] mac = crypto.activateMac(s);
Message sent = sendActivateMessage(txn, s, getLocalTimestamp(s), mac);
if (contactAdded) {
// Broadcast IntroductionSucceededEvent, because contact got added
IntroductionSucceededEvent e = new IntroductionSucceededEvent(c);
txn.attach(e);
}
// Move to AWAIT_ACTIVATE state and clear key material from session
return IntroduceeSession.awaitActivate(s, m, sent, keys);
......@@ -449,23 +467,17 @@ class IntroduceeProtocolEngine
if (isInvalidDependency(s, m.getPreviousMessageId()))
return abort(txn, s);
// Only bind keys if contact did not exist during AUTH
if (s.getTransportKeys() != null) {
Contact c =
contactManager.getContact(txn, s.getRemoteAuthor().getId(),
identityManager.getLocalAuthor(txn).getId());
keyManager.bindKeys(txn, c.getId(), s.getTransportKeys());
keyManager.activateKeys(txn, s.getTransportKeys());
// TODO remove when concept of inactive contacts is removed
contactManager.setContactActive(txn, c.getId(), true);
// TODO move this to AUTH step when concept of inactive contacts is removed
// Broadcast IntroductionSucceededEvent
IntroductionSucceededEvent e = new IntroductionSucceededEvent(c);
txn.attach(e);
// Validate MAC
try {
crypto.verifyActivateMac(m.getMac(), s);
} catch (GeneralSecurityException e) {
// TODO remove transport keys?
return abort(txn, s);
}
// Activate transport keys
keyManager.activateKeys(txn, s.getTransportKeys());
// Move back to START state
return IntroduceeSession
.clear(s, s.getLastLocalMessageId(), s.getLocalTimestamp(),
......@@ -513,12 +525,6 @@ class IntroduceeProtocolEngine
s.getRequestTimestamp());
}
private boolean isAlice(Transaction txn, IntroduceeSession s)
throws DbException {
Author localAuthor = identityManager.getLocalAuthor(txn);
return crypto.isAlice(localAuthor.getId(), s.getRemoteAuthor().getId());
}
private void addSessionId(Transaction txn, MessageId m, SessionId sessionId)
throws DbException {
BdfDictionary meta = new BdfDictionary();
......
......@@ -440,7 +440,7 @@ class IntroducerProtocolEngine
// Forward ACTIVATE message
Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i);
Message sent = sendActivateMessage(txn, i, timestamp);
Message sent = sendActivateMessage(txn, i, timestamp, m.getMac());
// Move to the next state
IntroducerState state = START;
......
......@@ -30,16 +30,19 @@ interface IntroductionConstants {
// Session Keys Introducee
String SESSION_KEY_INTRODUCER = "introducer";
String SESSION_KEY_LOCAL = "local";
String SESSION_KEY_REMOTE = "remote";
String SESSION_KEY_MASTER_KEY = "masterKey";
String SESSION_KEY_TRANSPORT_KEYS = "transportKeys";
String SESSION_KEY_ALICE = "alice";
String SESSION_KEY_EPHEMERAL_PUBLIC_KEY = "ephemeralPublicKey";
String SESSION_KEY_EPHEMERAL_PRIVATE_KEY = "ephemeralPrivateKey";
String SESSION_KEY_TRANSPORT_PROPERTIES = "transportProperties";
String SESSION_KEY_ACCEPT_TIMESTAMP = "acceptTimestamp";
String SESSION_KEY_MASTER_KEY = "masterKey";
String SESSION_KEY_MAC_KEY = "macKey";
String SESSION_KEY_REMOTE_AUTHOR = "remoteAuthor";
String SESSION_KEY_REMOTE_EPHEMERAL_PUBLIC_KEY = "remoteEphemeralPublicKey";
String SESSION_KEY_REMOTE_TRANSPORT_PROPERTIES =
"remoteTransportProperties";
String SESSION_KEY_REMOTE_ACCEPT_TIMESTAMP = "remoteAcceptTimestamp";
String SESSION_KEY_TRANSPORT_KEYS = "transportKeys";
}
......@@ -30,16 +30,15 @@ interface IntroductionCrypto {
/**
* Derives a session master key for Alice or Bob.
*
* @param alice true if the session owner is Alice
* @return The secret master key
*/
SecretKey deriveMasterKey(IntroduceeSession s, boolean alice)
SecretKey deriveMasterKey(IntroduceeSession s)
throws GeneralSecurityException;
/**
* Derives a MAC key from the session's master key for Alice or Bob.
*
* @param masterKey The key returned by {@link #deriveMasterKey(IntroduceeSession, boolean)}
* @param masterKey The key returned by {@link #deriveMasterKey(IntroduceeSession)}
* @param alice true for Alice's MAC key, false for Bob's
* @return The MAC key
*/
......@@ -49,17 +48,17 @@ interface IntroductionCrypto {
* 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);
byte[] authMac(SecretKey macKey, IntroduceeSession s,
AuthorId localAuthorId, boolean alice);
/**
* Verifies a received MAC
*
* @param mac The MAC to verify
* as returned by {@link #deriveMasterKey(IntroduceeSession, boolean)}
* as returned by {@link #deriveMasterKey(IntroduceeSession)}
* @throws GeneralSecurityException if the verification fails
*/
void verifyMac(byte[] mac, IntroduceeSession s, AuthorId localAuthorId)
void verifyAuthMac(byte[] mac, IntroduceeSession s, AuthorId localAuthorId)
throws GeneralSecurityException;
/**
......@@ -82,4 +81,17 @@ interface IntroductionCrypto {
void verifySignature(byte[] signature, IntroduceeSession s,
AuthorId localAuthorId) throws GeneralSecurityException;
/**
* Generates a MAC using the local MAC key.
*/
byte[] activateMac(IntroduceeSession s);
/**
* Verifies a MAC from an ACTIVATE message.
*
* @throws GeneralSecurityException if the verification fails
*/
void verifyActivateMac(byte[] mac, IntroduceeSession s)
throws GeneralSecurityException;
}
......@@ -23,6 +23,7 @@ import java.util.Map;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_ACTIVATE_MAC;
import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_ALICE_MAC_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_AUTH_MAC;
import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_AUTH_NONCE;
......@@ -74,10 +75,14 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
@Override
@SuppressWarnings("ConstantConditions")
public SecretKey deriveMasterKey(IntroduceeSession s, boolean alice)
public SecretKey deriveMasterKey(IntroduceeSession s)
throws GeneralSecurityException {
return deriveMasterKey(s.getEphemeralPublicKey(),
s.getEphemeralPrivateKey(), s.getRemotePublicKey(), alice);
return deriveMasterKey(
s.getLocal().ephemeralPublicKey,
s.getLocal().ephemeralPrivateKey,
s.getRemote().ephemeralPublicKey,
s.getLocal().alice
);
}
SecretKey deriveMasterKey(byte[] publicKey, byte[] privateKey,
......@@ -108,16 +113,17 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
@Override
@SuppressWarnings("ConstantConditions")
public byte[] mac(SecretKey macKey, IntroduceeSession s,
public byte[] authMac(SecretKey macKey, IntroduceeSession s,
AuthorId localAuthorId, boolean alice) {
return mac(macKey, s.getIntroducer().getId(), localAuthorId,
s.getRemoteAuthor().getId(), s.getAcceptTimestamp(),
s.getRemoteAcceptTimestamp(), s.getEphemeralPublicKey(),
s.getRemotePublicKey(), s.getTransportProperties(),
s.getRemoteTransportProperties(), alice);
return authMac(macKey, s.getIntroducer().getId(), localAuthorId,
s.getRemote().author.getId(), s.getLocal().acceptTimestamp,
s.getRemote().acceptTimestamp, s.getLocal().ephemeralPublicKey,
s.getRemote().ephemeralPublicKey,
s.getLocal().transportProperties,
s.getRemote().transportProperties, alice);
}
byte[] mac(SecretKey macKey, AuthorId introducerId,
byte[] authMac(SecretKey macKey, AuthorId introducerId,
AuthorId localAuthorId, AuthorId remoteAuthorId,
long acceptTimestamp, long remoteAcceptTimestamp,
byte[] ephemeralPublicKey, byte[] remoteEphemeralPublicKey,
......@@ -125,7 +131,7 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
Map<TransportId, TransportProperties> remoteTransportProperties,
boolean alice) {
byte[] inputs =
getMacInputs(introducerId, localAuthorId, remoteAuthorId,
getAuthMacInputs(introducerId, localAuthorId, remoteAuthorId,
acceptTimestamp, remoteAcceptTimestamp,
ephemeralPublicKey, remoteEphemeralPublicKey,
transportProperties, remoteTransportProperties, alice);
......@@ -138,19 +144,20 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
@Override
@SuppressWarnings("ConstantConditions")
public void verifyMac(byte[] mac, IntroduceeSession s,
public void verifyAuthMac(byte[] mac, IntroduceeSession s,
AuthorId localAuthorId)
throws GeneralSecurityException {
boolean alice = isAlice(localAuthorId, s.getRemoteAuthor().getId());
verifyMac(mac, new SecretKey(s.getMasterKey()),
boolean alice = isAlice(localAuthorId, s.getRemote().author.getId());
verifyAuthMac(mac, new SecretKey(s.getRemote().macKey),
s.getIntroducer().getId(), localAuthorId,
s.getRemoteAuthor().getId(), s.getAcceptTimestamp(),
s.getRemoteAcceptTimestamp(), s.getEphemeralPublicKey(),
s.getRemotePublicKey(), s.getTransportProperties(),
s.getRemoteTransportProperties(), !alice);
s.getRemote().author.getId(), s.getLocal().acceptTimestamp,
s.getRemote().acceptTimestamp, s.getLocal().ephemeralPublicKey,
s.getRemote().ephemeralPublicKey,
s.getLocal().transportProperties,
s.getRemote().transportProperties, !alice);
}
void verifyMac(byte[] mac, SecretKey masterKey,
void verifyAuthMac(byte[] mac, SecretKey macKey,
AuthorId introducerId, AuthorId localAuthorId,
AuthorId remoteAuthorId, long acceptTimestamp,
long remoteAcceptTimestamp, byte[] ephemeralPublicKey,
......@@ -158,9 +165,8 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
Map<TransportId, TransportProperties> transportProperties,
Map<TransportId, TransportProperties> remoteTransportProperties,
boolean alice) throws GeneralSecurityException {
SecretKey macKey = deriveMacKey(masterKey, alice);
byte[] inputs =
getMacInputs(introducerId, localAuthorId, remoteAuthorId,
getAuthMacInputs(introducerId, localAuthorId, remoteAuthorId,
acceptTimestamp, remoteAcceptTimestamp,
ephemeralPublicKey, remoteEphemeralPublicKey,
transportProperties, remoteTransportProperties, !alice);
......@@ -169,7 +175,7 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
}
}
private byte[] getMacInputs(AuthorId introducerId,
private byte[] getAuthMacInputs(AuthorId introducerId,
AuthorId localAuthorId, AuthorId remoteAuthorId,
long acceptTimestamp, long remoteAcceptTimestamp,
byte[] ephemeralPublicKey, byte[] remoteEphemeralPublicKey,
......@@ -214,9 +220,8 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
@SuppressWarnings("ConstantConditions")
public void verifySignature(byte[] signature, IntroduceeSession s,
AuthorId localAuthorId) throws GeneralSecurityException {
boolean alice = isAlice(s.getRemoteAuthor().getId(), localAuthorId);
SecretKey macKey = deriveMacKey(new SecretKey(s.getMasterKey()), alice);
verifySignature(macKey, s.getRemoteAuthor().getPublicKey(), signature);
SecretKey macKey = new SecretKey(s.getRemote().macKey);
verifySignature(macKey, s.getRemote().author.getPublicKey(), signature);
}
void verifySignature(SecretKey macKey, byte[] publicKey,
......@@ -232,4 +237,33 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
return crypto.mac(LABEL_AUTH_NONCE, macKey);
}
@Override
public byte[] activateMac(IntroduceeSession s) {
if (s.getLocal().macKey == null)
throw new AssertionError("Local MAC key is null");
return activateMac(new SecretKey(s.getLocal().macKey));
}
byte[] activateMac(SecretKey macKey) {
return crypto.mac(
LABEL_ACTIVATE_MAC,
macKey
);
}
@Override
public void verifyActivateMac(byte[] mac, IntroduceeSession s)
throws GeneralSecurityException {
if (s.getRemote().macKey == null)
throw new AssertionError("Remote MAC key is null");
verifyActivateMac(mac, new SecretKey(s.getRemote().macKey));
}
void verifyActivateMac(byte[] mac, SecretKey macKey)
throws GeneralSecurityException {
if (!crypto.verifyMac(mac, LABEL_ACTIVATE_MAC, macKey)) {
throw new GeneralSecurityException();
}
}
}
......@@ -190,8 +190,10 @@ class IntroductionManagerImpl extends ConversationClientImpl
Author remote = messageParser.parseRequestMessage(m, body).getAuthor();
if (local.equals(remote)) throw new FormatException();
SessionId sessionId = crypto.getSessionId(introducer, local, remote);
boolean alice = crypto.isAlice(local.getId(), remote.getId());
return IntroduceeSession
.getInitial(m.getGroupId(), sessionId, introducer, remote);
.getInitial(m.getGroupId(), sessionId, introducer, alice,
remote);
}
private <S extends Session> S handleMessage(Transaction txn, Message m,
......@@ -441,7 +443,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
IntroduceeSession session = sessionParser
.parseIntroduceeSession(contactGroupId, bdfSession);
sessionId = session.getSessionId();
author = session.getRemoteAuthor();
author = session.getRemote().author;
} else throw new AssertionError();
Message msg = clientHelper.getMessage(txn, m);
BdfList body = clientHelper.getMessageAsList(txn, m);
......@@ -481,7 +483,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
IntroduceeSession session = sessionParser
.parseIntroduceeSession(contactGroupId, bdfSession);
sessionId = session.getSessionId();
author = session.getRemoteAuthor();
author = session.getRemote().author;
} else throw new AssertionError();
return new IntroductionResponse(sessionId, m, contactGroupId,
role, meta.getTimestamp(), meta.isLocal(), status.isSent(),
......
......@@ -27,6 +27,7 @@ import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_REQUEST_MESSAGE_LENGTH;
import static org.briarproject.briar.introduction.MessageType.ACCEPT;
import static org.briarproject.briar.introduction.MessageType.ACTIVATE;
import static org.briarproject.briar.introduction.MessageType.AUTH;
......@@ -55,8 +56,9 @@ class IntroductionValidator extends BdfMessageValidator {
return validateAcceptMessage(m, body);
case AUTH:
return validateAuthMessage(m, body);
case DECLINE:
case ACTIVATE:
return validateActivateMessage(m, body);
case DECLINE:
case ABORT:
return validateOtherMessage(type, m, body);
default:
......@@ -149,6 +151,32 @@ class IntroductionValidator extends BdfMessageValidator {
Collections.singletonList(dependency));
}
private BdfMessageContext validateActivateMessage(Message m, BdfList body)
throws FormatException {
checkSize(body, 4);
byte[] sessionIdBytes = body.getRaw(1);
checkLength(sessionIdBytes, UniqueId.LENGTH);
byte[] previousMessageId = body.getOptionalRaw(2);
checkLength(previousMessageId, UniqueId.LENGTH);
byte[] mac = body.getOptionalRaw(3);
checkLength(mac, MAC_BYTES);
SessionId sessionId = new SessionId(sessionIdBytes);
BdfDictionary meta = messageEncoder
.encodeMetadata(ACTIVATE, sessionId, m.getTimestamp(), false,
false, false);
if (previousMessageId == null) {
return new BdfMessageContext(meta);
} else {
MessageId dependency = new MessageId(previousMessageId);
return new BdfMessageContext(meta,
Collections.singletonList(dependency));
}
}
private BdfMessageContext validateOtherMessage(MessageType type,
Message m, BdfList body) throws FormatException {
checkSize(body, 3);
......
......@@ -47,7 +47,8 @@ interface MessageEncoder {
byte[] mac, byte[] signature);
Message encodeActivateMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId);
@Nullable MessageId previousMessageId, SessionId sessionId,
byte[] mac);
Message encodeAbortMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId);
......
......@@ -100,12 +100,7 @@ class MessageEncoderImpl implements MessageEncoder {
clientHelper.toList(author),
message
);
try {
return messageFactory.createMessage(contactGroupId, timestamp,
clientHelper.toByteArray(body));
} catch (FormatException e) {
throw new AssertionError(e);
}
return createMessage(contactGroupId, timestamp, body);
}
@Override
......@@ -119,14 +114,9 @@ class MessageEncoderImpl implements MessageEncoder {
previousMessageId,
ephemeralPublicKey,
acceptTimestamp,
encodeTransportProperties(transportProperties)
clientHelper.toDictionary(transportProperties)
);
try {
return messageFactory.createMessage(contactGroupId, timestamp,
clientHelper.toByteArray(body));
} catch (FormatException e) {
throw new AssertionError(e);
}
return createMessage(contactGroupId, timestamp, body);
}
@Override
......@@ -147,19 +137,20 @@ class MessageEncoderImpl implements MessageEncoder {
mac,
signature
);
try {
return messageFactory.createMessage(contactGroupId, timestamp,
clientHelper.toByteArray(body));
} catch (FormatException e) {
throw new AssertionError(e);
}
return createMessage(contactGroupId, timestamp, body);
}
@Override
public Message encodeActivateMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, SessionId sessionId) {
return encodeMessage(ACTIVATE, contactGroupId, sessionId, timestamp,
previousMessageId);
@Nullable MessageId previousMessageId, SessionId sessionId,
byte[] mac) {
BdfList body = BdfList.of(
ACTIVATE.getValue(),
sessionId,
previousMessageId,