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.
This commit is contained in:
Torsten Grote
2018-04-26 16:17:38 -03:00
parent 0e04044ebb
commit bd5504de26
22 changed files with 614 additions and 417 deletions

View File

@@ -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";
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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());
Message sent = sendActivateMessage(txn, s, getLocalTimestamp(s));
// 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);
// 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();

View File

@@ -27,68 +27,44 @@ class IntroduceeSession extends Session<IntroduceeState>
implements PeerSession {
private final GroupId contactGroupId;
private final long localTimestamp, acceptTimestamp, remoteAcceptTimestamp;
private final Author introducer;
private final Local local;
private final Remote remote;
@Nullable
private final MessageId lastLocalMessageId, lastRemoteMessageId;
private final Author introducer, remoteAuthor;
@Nullable
private final byte[] ephemeralPublicKey, ephemeralPrivateKey;
@Nullable
private final byte[] masterKey, remoteEphemeralPublicKey;
@Nullable
private final Map<TransportId, TransportProperties> transportProperties;
@Nullable
private final Map<TransportId, TransportProperties>
remoteTransportProperties;
private final byte[] masterKey;
@Nullable
private final Map<TransportId, KeySetId> transportKeys;
IntroduceeSession(SessionId sessionId, IntroduceeState state,
long requestTimestamp, GroupId contactGroupId,
@Nullable MessageId lastLocalMessageId, long localTimestamp,
@Nullable MessageId lastRemoteMessageId, Author introducer,
@Nullable byte[] ephemeralPublicKey,
@Nullable byte[] ephemeralPrivateKey,
@Nullable Map<TransportId, TransportProperties> transportProperties,
long acceptTimestamp, @Nullable byte[] masterKey,
Author remoteAuthor,
@Nullable byte[] remoteEphemeralPublicKey, @Nullable
Map<TransportId, TransportProperties> remoteTransportProperties,
long remoteAcceptTimestamp,
long requestTimestamp, GroupId contactGroupId, Author introducer,
Local local, Remote remote, @Nullable byte[] masterKey,
@Nullable Map<TransportId, KeySetId> transportKeys) {
super(sessionId, state, requestTimestamp);
this.contactGroupId = contactGroupId;
this.lastLocalMessageId = lastLocalMessageId;
this.localTimestamp = localTimestamp;
this.lastRemoteMessageId = lastRemoteMessageId;
this.introducer = introducer;
this.ephemeralPublicKey = ephemeralPublicKey;
this.ephemeralPrivateKey = ephemeralPrivateKey;
this.transportProperties = transportProperties;
this.acceptTimestamp = acceptTimestamp;
this.local = local;
this.remote = remote;
this.masterKey = masterKey;
this.remoteAuthor = remoteAuthor;
this.remoteEphemeralPublicKey = remoteEphemeralPublicKey;
this.remoteTransportProperties = remoteTransportProperties;
this.remoteAcceptTimestamp = remoteAcceptTimestamp;
this.transportKeys = transportKeys;
}
static IntroduceeSession getInitial(GroupId contactGroupId,
SessionId sessionId, Author introducer, Author remoteAuthor) {
return new IntroduceeSession(sessionId, START, -1, contactGroupId, null,
-1, null, introducer, null, null, null, -1, null, remoteAuthor,
null, null, -1, null);
SessionId sessionId, Author introducer, boolean localIsAlice,
Author remoteAuthor) {
Local local =
new Local(localIsAlice, null, -1, null, null, null, -1, null);
Remote remote =
new Remote(!localIsAlice, remoteAuthor, null, null, null, -1,
null);
return new IntroduceeSession(sessionId, START, -1, contactGroupId,
introducer, local, remote, null, null);
}
static IntroduceeSession addRemoteRequest(IntroduceeSession s,
IntroduceeState state, RequestMessage m) {
Remote remote = new Remote(s.remote, m.getMessageId());
return new IntroduceeSession(s.getSessionId(), state, m.getTimestamp(),
s.contactGroupId, s.lastLocalMessageId, s.localTimestamp,
m.getMessageId(), s.introducer, s.ephemeralPublicKey,
s.ephemeralPrivateKey, s.transportProperties, s.acceptTimestamp,
s.masterKey, s.remoteAuthor, s.remoteEphemeralPublicKey,
s.remoteTransportProperties, s.remoteAcceptTimestamp,
s.contactGroupId, s.introducer, s.local, remote, s.masterKey,
s.transportKeys);
}
@@ -97,57 +73,66 @@ class IntroduceeSession extends Session<IntroduceeState>
byte[] ephemeralPublicKey, byte[] ephemeralPrivateKey,
long acceptTimestamp,
Map<TransportId, TransportProperties> transportProperties) {
Local local = new Local(s.local.alice, acceptMessage.getId(),
acceptMessage.getTimestamp(), ephemeralPublicKey,
ephemeralPrivateKey, transportProperties, acceptTimestamp,
null);
return new IntroduceeSession(s.getSessionId(), state,
s.getRequestTimestamp(), s.contactGroupId,
acceptMessage.getId(), acceptMessage.getTimestamp(),
s.lastRemoteMessageId, s.introducer, ephemeralPublicKey,
ephemeralPrivateKey, transportProperties,
acceptTimestamp, s.masterKey, s.remoteAuthor,
s.remoteEphemeralPublicKey, s.remoteTransportProperties,
s.remoteAcceptTimestamp, s.transportKeys);
s.getRequestTimestamp(), s.contactGroupId, s.introducer, local,
s.remote, s.masterKey, s.transportKeys);
}
static IntroduceeSession addRemoteAccept(IntroduceeSession s,
IntroduceeState state, AcceptMessage acceptMessage) {
IntroduceeState state, AcceptMessage m) {
Remote remote =
new Remote(s.remote.alice, s.remote.author, m.getMessageId(),
m.getEphemeralPublicKey(), m.getTransportProperties(),
m.getAcceptTimestamp(), s.remote.macKey);
return new IntroduceeSession(s.getSessionId(), state,
s.getRequestTimestamp(), s.contactGroupId, s.lastLocalMessageId,
s.localTimestamp, acceptMessage.getMessageId(), s.introducer,
s.ephemeralPublicKey, s.ephemeralPrivateKey,
s.transportProperties, s.acceptTimestamp, s.masterKey,
s.remoteAuthor, acceptMessage.getEphemeralPublicKey(),
acceptMessage.getTransportProperties(),
acceptMessage.getAcceptTimestamp(), s.transportKeys);
s.getRequestTimestamp(), s.contactGroupId, s.introducer,
s.local, remote, s.masterKey, s.transportKeys);
}
static IntroduceeSession addLocalAuth(IntroduceeSession s,
IntroduceeState state, SecretKey masterKey, Message m) {
IntroduceeState state, Message m, SecretKey masterKey,
SecretKey aliceMacKey, SecretKey bobMacKey) {
// add mac key and sent message
Local local = new Local(s.local.alice, m.getId(), m.getTimestamp(),
s.local.ephemeralPublicKey, s.local.ephemeralPrivateKey,
s.local.transportProperties, s.local.acceptTimestamp,
s.local.alice ? aliceMacKey.getBytes() : bobMacKey.getBytes());
// just add the mac key
Remote remote = new Remote(s.remote.alice, s.remote.author,
s.remote.lastMessageId, s.remote.ephemeralPublicKey,
s.remote.transportProperties, s.remote.acceptTimestamp,
s.remote.alice ? aliceMacKey.getBytes() : bobMacKey.getBytes());
// add master key
return new IntroduceeSession(s.getSessionId(), state,
s.getRequestTimestamp(), s.contactGroupId, m.getId(),
m.getTimestamp(), s.lastRemoteMessageId, s.introducer,
s.ephemeralPublicKey, s.ephemeralPrivateKey,
s.transportProperties, s.acceptTimestamp, masterKey.getBytes(),
s.remoteAuthor, s.remoteEphemeralPublicKey,
s.remoteTransportProperties, s.remoteAcceptTimestamp,
s.transportKeys);
s.getRequestTimestamp(), s.contactGroupId, s.introducer, local,
remote, masterKey.getBytes(), s.transportKeys);
}
static IntroduceeSession awaitActivate(IntroduceeSession s, AuthMessage m,
Message sent, @Nullable Map<TransportId, KeySetId> transportKeys) {
Local local = new Local(s.local, sent.getId(), sent.getTimestamp());
Remote remote = new Remote(s.remote, m.getMessageId());
return new IntroduceeSession(s.getSessionId(), AWAIT_ACTIVATE,
s.getRequestTimestamp(), s.contactGroupId, sent.getId(),
sent.getTimestamp(), m.getMessageId(), s.introducer, null, null,
null, s.acceptTimestamp, null, s.getRemoteAuthor(), null, null,
s.remoteAcceptTimestamp, transportKeys);
s.getRequestTimestamp(), s.contactGroupId, s.introducer, local,
remote, null, transportKeys);
}
static IntroduceeSession clear(IntroduceeSession s,
@Nullable MessageId lastLocalMessageId, long localTimestamp,
@Nullable MessageId lastRemoteMessageId) {
Local local =
new Local(s.local.alice, lastLocalMessageId, localTimestamp,
null, null, null, -1, null);
Remote remote =
new Remote(s.remote.alice, s.remote.author, lastRemoteMessageId,
null, null, -1, null);
return new IntroduceeSession(s.getSessionId(), START,
s.getRequestTimestamp(), s.getContactGroupId(),
lastLocalMessageId, localTimestamp, lastRemoteMessageId,
s.getIntroducer(), null, null, null, -1, null,
s.getRemoteAuthor(), null, null, -1, null);
s.getRequestTimestamp(), s.contactGroupId, s.introducer, local,
remote, null, null);
}
@Override
@@ -155,45 +140,38 @@ class IntroduceeSession extends Session<IntroduceeState>
return INTRODUCEE;
}
@Override
public GroupId getContactGroupId() {
return contactGroupId;
}
@Override
public long getLocalTimestamp() {
return localTimestamp;
return local.lastMessageTimestamp;
}
@Nullable
@Override
public MessageId getLastLocalMessageId() {
return lastLocalMessageId;
return local.lastMessageId;
}
@Nullable
@Override
public MessageId getLastRemoteMessageId() {
return lastRemoteMessageId;
return remote.lastMessageId;
}
Author getIntroducer() {
return introducer;
}
@Nullable
byte[] getEphemeralPublicKey() {
return ephemeralPublicKey;
public Local getLocal() {
return local;
}
@Nullable
byte[] getEphemeralPrivateKey() {
return ephemeralPrivateKey;
}
@Nullable
Map<TransportId, TransportProperties> getTransportProperties() {
return transportProperties;
}
long getAcceptTimestamp() {
return acceptTimestamp;
public Remote getRemote() {
return remote;
}
@Nullable
@@ -201,27 +179,77 @@ class IntroduceeSession extends Session<IntroduceeState>
return masterKey;
}
Author getRemoteAuthor() {
return remoteAuthor;
}
@Nullable
byte[] getRemotePublicKey() {
return remoteEphemeralPublicKey;
}
@Nullable
Map<TransportId, TransportProperties> getRemoteTransportProperties() {
return remoteTransportProperties;
}
long getRemoteAcceptTimestamp() {
return remoteAcceptTimestamp;
}
@Nullable
Map<TransportId, KeySetId> getTransportKeys() {
return transportKeys;
}
abstract static class Common {
final boolean alice;
@Nullable
final MessageId lastMessageId;
@Nullable
final byte[] ephemeralPublicKey;
@Nullable
final Map<TransportId, TransportProperties> transportProperties;
final long acceptTimestamp;
@Nullable
final byte[] macKey;
private Common(boolean alice, @Nullable MessageId lastMessageId,
@Nullable byte[] ephemeralPublicKey, @Nullable
Map<TransportId, TransportProperties> transportProperties,
long acceptTimestamp, @Nullable byte[] macKey) {
this.alice = alice;
this.lastMessageId = lastMessageId;
this.ephemeralPublicKey = ephemeralPublicKey;
this.transportProperties = transportProperties;
this.acceptTimestamp = acceptTimestamp;
this.macKey = macKey;
}
}
static class Local extends Common {
final long lastMessageTimestamp;
@Nullable
final byte[] ephemeralPrivateKey;
Local(boolean alice, @Nullable MessageId lastMessageId,
long lastMessageTimestamp, @Nullable byte[] ephemeralPublicKey,
@Nullable byte[] ephemeralPrivateKey, @Nullable
Map<TransportId, TransportProperties> transportProperties,
long acceptTimestamp, @Nullable byte[] macKey) {
super(alice, lastMessageId, ephemeralPublicKey, transportProperties,
acceptTimestamp, macKey);
this.lastMessageTimestamp = lastMessageTimestamp;
this.ephemeralPrivateKey = ephemeralPrivateKey;
}
private Local(Local s, @Nullable MessageId lastMessageId,
long lastMessageTimestamp) {
this(s.alice, lastMessageId, lastMessageTimestamp,
s.ephemeralPublicKey, s.ephemeralPrivateKey,
s.transportProperties, s.acceptTimestamp, s.macKey);
}
}
static class Remote extends Common {
final Author author;
Remote(boolean alice, Author author,
@Nullable MessageId lastMessageId,
@Nullable byte[] ephemeralPublicKey, @Nullable
Map<TransportId, TransportProperties> transportProperties,
long acceptTimestamp, @Nullable byte[] macKey) {
super(alice, lastMessageId, ephemeralPublicKey, transportProperties,
acceptTimestamp, macKey);
this.author = author;
}
private Remote(Remote s, @Nullable MessageId lastMessageId) {
this(s.alice, s.author, lastMessageId, s.ephemeralPublicKey,
s.transportProperties, s.acceptTimestamp, s.macKey);
}
}
}

View File

@@ -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;

View File

@@ -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";
}

View File

@@ -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;
}

View File

@@ -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();
}
}
}

View File

@@ -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(),

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,
mac
);
return createMessage(contactGroupId, timestamp, body);
}
@Override
@@ -177,6 +168,11 @@ class MessageEncoderImpl implements MessageEncoder {
sessionId,
previousMessageId
);
return createMessage(contactGroupId, timestamp, body);
}
private Message createMessage(GroupId contactGroupId, long timestamp,
BdfList body) {
try {
return messageFactory.createMessage(contactGroupId, timestamp,
clientHelper.toByteArray(body));
@@ -185,13 +181,4 @@ class MessageEncoderImpl implements MessageEncoder {
}
}
private BdfDictionary encodeTransportProperties(
Map<TransportId, TransportProperties> map) {
BdfDictionary d = new BdfDictionary();
for (Map.Entry<TransportId, TransportProperties> e : map.entrySet()) {
d.put(e.getKey().getString(), e.getValue());
}
return d;
}
}

View File

@@ -124,8 +124,9 @@ class MessageParserImpl implements MessageParser {
SessionId sessionId = new SessionId(body.getRaw(1));
byte[] previousMsgBytes = body.getRaw(2);
MessageId previousMessageId = new MessageId(previousMsgBytes);
byte[] mac = body.getRaw(3);
return new ActivateMessage(m.getId(), m.getGroupId(), m.getTimestamp(),
previousMessageId, sessionId);
previousMessageId, sessionId, mac);
}
@Override

View File

@@ -12,7 +12,7 @@ abstract class Session<S extends State> {
private final SessionId sessionId;
private final S state;
private long requestTimestamp;
private final long requestTimestamp;
Session(SessionId sessionId, S state, long requestTimestamp) {
this.sessionId = sessionId;
@@ -30,7 +30,7 @@ abstract class Session<S extends State> {
return state;
}
public long getRequestTimestamp() {
long getRequestTimestamp() {
return requestTimestamp;
}

View File

@@ -7,6 +7,9 @@ import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.briar.introduction.IntroduceeSession.Common;
import org.briarproject.briar.introduction.IntroduceeSession.Local;
import org.briarproject.briar.introduction.IntroduceeSession.Remote;
import org.briarproject.briar.introduction.IntroducerSession.Introducee;
import java.util.Map;
@@ -19,6 +22,7 @@ import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
import static org.briarproject.briar.api.introduction.Role.INTRODUCEE;
import static org.briarproject.briar.api.introduction.Role.INTRODUCER;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_ACCEPT_TIMESTAMP;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_ALICE;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_AUTHOR;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_EPHEMERAL_PRIVATE_KEY;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_EPHEMERAL_PUBLIC_KEY;
@@ -28,12 +32,12 @@ import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCER;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LOCAL;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LOCAL_TIMESTAMP;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_MAC_KEY;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_MASTER_KEY;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_REMOTE_ACCEPT_TIMESTAMP;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_REMOTE;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_REMOTE_AUTHOR;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_REMOTE_EPHEMERAL_PUBLIC_KEY;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_REMOTE_TRANSPORT_PROPERTIES;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_REQUEST_TIMESTAMP;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_ROLE;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_SESSION_ID;
@@ -91,34 +95,43 @@ class SessionEncoderImpl implements SessionEncoder {
@Override
public BdfDictionary encodeIntroduceeSession(IntroduceeSession s) {
BdfDictionary d = encodeSession(s);
d.put(SESSION_KEY_LOCAL_TIMESTAMP, s.getLocalTimestamp());
putNullable(d, SESSION_KEY_LAST_LOCAL_MESSAGE_ID,
s.getLastLocalMessageId());
putNullable(d, SESSION_KEY_LAST_REMOTE_MESSAGE_ID,
s.getLastRemoteMessageId());
d.put(SESSION_KEY_INTRODUCER, clientHelper.toList(s.getIntroducer()));
d.put(SESSION_KEY_REMOTE_AUTHOR,
clientHelper.toList(s.getRemoteAuthor()));
putNullable(d, SESSION_KEY_EPHEMERAL_PUBLIC_KEY,
s.getEphemeralPublicKey());
putNullable(d, SESSION_KEY_EPHEMERAL_PRIVATE_KEY,
s.getEphemeralPrivateKey());
putNullable(d, SESSION_KEY_TRANSPORT_PROPERTIES,
s.getTransportProperties() == null ? null :
clientHelper.toDictionary(s.getTransportProperties()));
d.put(SESSION_KEY_ACCEPT_TIMESTAMP, s.getAcceptTimestamp());
d.put(SESSION_KEY_LOCAL, encodeLocal(s.getLocal()));
d.put(SESSION_KEY_REMOTE, encodeRemote(s.getRemote()));
putNullable(d, SESSION_KEY_MASTER_KEY, s.getMasterKey());
putNullable(d, SESSION_KEY_REMOTE_EPHEMERAL_PUBLIC_KEY,
s.getRemotePublicKey());
putNullable(d, SESSION_KEY_REMOTE_TRANSPORT_PROPERTIES,
s.getRemoteTransportProperties() == null ? null : clientHelper
.toDictionary(s.getRemoteTransportProperties()));
d.put(SESSION_KEY_REMOTE_ACCEPT_TIMESTAMP, s.getRemoteAcceptTimestamp());
putNullable(d, SESSION_KEY_TRANSPORT_KEYS,
encodeTransportKeys(s.getTransportKeys()));
return d;
}
private BdfDictionary encodeCommon(Common s) {
BdfDictionary d = new BdfDictionary();
d.put(SESSION_KEY_ALICE, s.alice);
putNullable(d, SESSION_KEY_EPHEMERAL_PUBLIC_KEY, s.ephemeralPublicKey);
putNullable(d, SESSION_KEY_TRANSPORT_PROPERTIES,
s.transportProperties == null ? null :
clientHelper.toDictionary(s.transportProperties));
d.put(SESSION_KEY_ACCEPT_TIMESTAMP, s.acceptTimestamp);
putNullable(d, SESSION_KEY_MAC_KEY, s.macKey);
return d;
}
private BdfDictionary encodeLocal(Local s) {
BdfDictionary d = encodeCommon(s);
d.put(SESSION_KEY_LOCAL_TIMESTAMP, s.lastMessageTimestamp);
putNullable(d, SESSION_KEY_LAST_LOCAL_MESSAGE_ID, s.lastMessageId);
putNullable(d, SESSION_KEY_EPHEMERAL_PRIVATE_KEY,
s.ephemeralPrivateKey);
return d;
}
private BdfDictionary encodeRemote(Remote s) {
BdfDictionary d = encodeCommon(s);
d.put(SESSION_KEY_REMOTE_AUTHOR, clientHelper.toList(s.author));
putNullable(d, SESSION_KEY_LAST_REMOTE_MESSAGE_ID, s.lastMessageId);
return d;
}
private BdfDictionary encodeSession(Session s) {
BdfDictionary d = new BdfDictionary();
d.put(SESSION_KEY_SESSION_ID, s.getSessionId());

View File

@@ -13,6 +13,8 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.introduction.Role;
import org.briarproject.briar.introduction.IntroduceeSession.Local;
import org.briarproject.briar.introduction.IntroduceeSession.Remote;
import org.briarproject.briar.introduction.IntroducerSession.Introducee;
import java.util.HashMap;
@@ -22,7 +24,10 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.briar.api.introduction.Role.INTRODUCEE;
import static org.briarproject.briar.api.introduction.Role.INTRODUCER;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_ACCEPT_TIMESTAMP;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_ALICE;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_AUTHOR;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_EPHEMERAL_PRIVATE_KEY;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_EPHEMERAL_PUBLIC_KEY;
@@ -32,20 +37,18 @@ import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_INTRODUCER;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LOCAL;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_LOCAL_TIMESTAMP;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_MAC_KEY;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_MASTER_KEY;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_REMOTE_ACCEPT_TIMESTAMP;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_REMOTE;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_REMOTE_AUTHOR;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_REMOTE_EPHEMERAL_PUBLIC_KEY;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_REMOTE_TRANSPORT_PROPERTIES;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_REQUEST_TIMESTAMP;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_ROLE;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_SESSION_ID;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_STATE;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_TRANSPORT_KEYS;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_TRANSPORT_PROPERTIES;
import static org.briarproject.briar.api.introduction.Role.INTRODUCEE;
import static org.briarproject.briar.api.introduction.Role.INTRODUCER;
@Immutable
@NotNullByDefault
@@ -103,42 +106,55 @@ class SessionParserImpl implements SessionParser {
SessionId sessionId = getSessionId(d);
IntroduceeState state = IntroduceeState.fromValue(getState(d));
long requestTimestamp = d.getLong(SESSION_KEY_REQUEST_TIMESTAMP);
Author introducer = getAuthor(d, SESSION_KEY_INTRODUCER);
Local local = parseLocal(d.getDictionary(SESSION_KEY_LOCAL));
Remote remote = parseRemote(d.getDictionary(SESSION_KEY_REMOTE));
byte[] masterKey = d.getOptionalRaw(SESSION_KEY_MASTER_KEY);
Map<TransportId, KeySetId> transportKeys = parseTransportKeys(
d.getOptionalDictionary(SESSION_KEY_TRANSPORT_KEYS));
return new IntroduceeSession(sessionId, state, requestTimestamp,
introducerGroupId, introducer, local, remote,
masterKey, transportKeys);
}
private Local parseLocal(BdfDictionary d) throws FormatException {
boolean alice = d.getBoolean(SESSION_KEY_ALICE);
MessageId lastLocalMessageId =
getMessageId(d, SESSION_KEY_LAST_LOCAL_MESSAGE_ID);
long localTimestamp = d.getLong(SESSION_KEY_LOCAL_TIMESTAMP);
MessageId lastRemoteMessageId =
getMessageId(d, SESSION_KEY_LAST_REMOTE_MESSAGE_ID);
Author introducer = getAuthor(d, SESSION_KEY_INTRODUCER);
byte[] ephemeralPublicKey =
d.getOptionalRaw(SESSION_KEY_EPHEMERAL_PUBLIC_KEY);
BdfDictionary tpDict =
d.getOptionalDictionary(SESSION_KEY_TRANSPORT_PROPERTIES);
byte[] ephemeralPrivateKey =
d.getOptionalRaw(SESSION_KEY_EPHEMERAL_PRIVATE_KEY);
Map<TransportId, TransportProperties> transportProperties =
tpDict == null ? null : clientHelper
.parseAndValidateTransportPropertiesMap(tpDict);
long acceptTimestamp = d.getLong(SESSION_KEY_ACCEPT_TIMESTAMP);
byte[] macKey = d.getOptionalRaw(SESSION_KEY_MAC_KEY);
return new Local(alice, lastLocalMessageId, localTimestamp,
ephemeralPublicKey, ephemeralPrivateKey, transportProperties,
acceptTimestamp, macKey);
}
private Remote parseRemote(BdfDictionary d) throws FormatException {
boolean alice = d.getBoolean(SESSION_KEY_ALICE);
Author remoteAuthor = getAuthor(d, SESSION_KEY_REMOTE_AUTHOR);
MessageId lastRemoteMessageId =
getMessageId(d, SESSION_KEY_LAST_REMOTE_MESSAGE_ID);
byte[] ephemeralPublicKey =
d.getOptionalRaw(SESSION_KEY_EPHEMERAL_PUBLIC_KEY);
BdfDictionary tpDict =
d.getOptionalDictionary(SESSION_KEY_TRANSPORT_PROPERTIES);
Map<TransportId, TransportProperties> transportProperties =
tpDict == null ? null : clientHelper
.parseAndValidateTransportPropertiesMap(tpDict);
long acceptTimestamp = d.getLong(SESSION_KEY_ACCEPT_TIMESTAMP);
byte[] masterKey = d.getOptionalRaw(SESSION_KEY_MASTER_KEY);
Author remoteAuthor = getAuthor(d, SESSION_KEY_REMOTE_AUTHOR);
byte[] remoteEphemeralPublicKey =
d.getOptionalRaw(SESSION_KEY_REMOTE_EPHEMERAL_PUBLIC_KEY);
BdfDictionary rptDict = d.getOptionalDictionary(
SESSION_KEY_REMOTE_TRANSPORT_PROPERTIES);
Map<TransportId, TransportProperties> remoteTransportProperties =
rptDict == null ? null : clientHelper
.parseAndValidateTransportPropertiesMap(rptDict);
long remoteAcceptTimestamp =
d.getLong(SESSION_KEY_REMOTE_ACCEPT_TIMESTAMP);
Map<TransportId, KeySetId> transportKeys = parseTransportKeys(
d.getOptionalDictionary(SESSION_KEY_TRANSPORT_KEYS));
return new IntroduceeSession(sessionId, state, requestTimestamp,
introducerGroupId, lastLocalMessageId, localTimestamp,
lastRemoteMessageId, introducer, ephemeralPublicKey,
ephemeralPrivateKey, transportProperties, acceptTimestamp,
masterKey, remoteAuthor, remoteEphemeralPublicKey,
remoteTransportProperties, remoteAcceptTimestamp,
transportKeys);
byte[] macKey = d.getOptionalRaw(SESSION_KEY_MAC_KEY);
return new Remote(alice, remoteAuthor, lastRemoteMessageId,
ephemeralPublicKey, transportProperties, acceptTimestamp,
macKey);
}
private int getState(BdfDictionary d) throws FormatException {

View File

@@ -96,35 +96,35 @@ public class IntroductionCryptoImplTest extends BrambleTestCase {
}
@Test
public void testAliceMac() throws Exception {
public void testAliceAuthMac() throws Exception {
SecretKey aliceMacKey = crypto.deriveMacKey(masterKey, true);
byte[] aliceMac =
crypto.mac(aliceMacKey, introducer.getId(), alice.getId(),
crypto.authMac(aliceMacKey, introducer.getId(), alice.getId(),
bob.getId(), aliceAcceptTimestamp, bobAcceptTimestamp,
aliceEphemeral.getPublic().getEncoded(),
bobEphemeral.getPublic().getEncoded(), aliceTransport,
bobTransport, true);
crypto.verifyMac(aliceMac, masterKey, introducer.getId(), bob.getId(),
alice.getId(), bobAcceptTimestamp, aliceAcceptTimestamp,
bobEphemeral.getPublic().getEncoded(),
crypto.verifyAuthMac(aliceMac, aliceMacKey, introducer.getId(),
bob.getId(), alice.getId(), bobAcceptTimestamp,
aliceAcceptTimestamp, bobEphemeral.getPublic().getEncoded(),
aliceEphemeral.getPublic().getEncoded(), bobTransport,
aliceTransport, true);
}
@Test
public void testBobMac() throws Exception {
public void testBobAuthMac() throws Exception {
SecretKey bobMacKey = crypto.deriveMacKey(masterKey, false);
byte[] bobMac =
crypto.mac(bobMacKey, introducer.getId(), bob.getId(),
crypto.authMac(bobMacKey, introducer.getId(), bob.getId(),
alice.getId(), bobAcceptTimestamp, aliceAcceptTimestamp,
bobEphemeral.getPublic().getEncoded(),
aliceEphemeral.getPublic().getEncoded(), bobTransport,
aliceTransport, false);
crypto.verifyMac(bobMac, masterKey, introducer.getId(), alice.getId(),
bob.getId(), aliceAcceptTimestamp, bobAcceptTimestamp,
aliceEphemeral.getPublic().getEncoded(),
crypto.verifyAuthMac(bobMac, bobMacKey, introducer.getId(),
alice.getId(), bob.getId(), aliceAcceptTimestamp,
bobAcceptTimestamp, aliceEphemeral.getPublic().getEncoded(),
bobEphemeral.getPublic().getEncoded(), aliceTransport,
bobTransport, false);
}
@@ -139,4 +139,20 @@ public class IntroductionCryptoImplTest extends BrambleTestCase {
signature);
}
@Test
public void testAliceActivateMac() throws Exception {
SecretKey aliceMacKey = crypto.deriveMacKey(masterKey, true);
byte[] aliceMac = crypto.activateMac(aliceMacKey);
crypto.verifyActivateMac(aliceMac, aliceMacKey);
}
@Test
public void testBobActivateMac() throws Exception {
SecretKey bobMacKey = crypto.deriveMacKey(masterKey, false);
byte[] bobMac = crypto.activateMac(bobMacKey);
crypto.verifyActivateMac(bobMac, bobMacKey);
}
}

View File

@@ -170,16 +170,6 @@ public class IntroductionIntegrationTest
sync1To0(1, true);
sync0To2(1, true);
// assert that introducee2 added introducee1
Contact contact1From2 = c2.getContactManager()
.getContact(author1.getId(), author2.getId());
// assert that introducee2 did add transport properties
// TODO check when notion of inactive contacts has been removed
// TransportProperties tp2 = c2.getTransportPropertyManager()
// .getRemoteProperties(contact1From2.getId(), TRANSPORT_ID);
// assertFalse(tp2.isEmpty());
// assert that introducee2 did add the transport keys
IntroduceeSession session2 = getIntroduceeSession(c2.getClientHelper(),
introductionManager2.getContactGroup(contact0From2).getId());
@@ -194,7 +184,7 @@ public class IntroductionIntegrationTest
IntroduceeSession session1 = getIntroduceeSession(c1.getClientHelper(),
introductionManager1.getContactGroup(contact0From1).getId());
assertNull(session1.getMasterKey());
assertNull(session1.getEphemeralPrivateKey());
assertNull(session1.getLocal().ephemeralPrivateKey);
assertNull(session1.getTransportKeys());
// sync second ACTIVATE and its forward
@@ -533,16 +523,6 @@ public class IntroductionIntegrationTest
sync1To0(1, true);
sync0To2(1, true);
// assert that introducee2 did not add any transport properties
TransportProperties tp2 = c2.getTransportPropertyManager()
.getRemoteProperties(contactId1From2, TRANSPORT_ID);
assertTrue(tp2.isEmpty());
// assert that introducee2 did not add any transport keys
IntroduceeSession session2 = getIntroduceeSession(c2.getClientHelper(),
introductionManager2.getContactGroup(contact0From2).getId());
assertNull(session2.getTransportKeys());
// sync second AUTH and its forward as well as the following ACTIVATE
sync2To0(2, true);
sync0To1(2, true);

View File

@@ -312,7 +312,7 @@ public class IntroductionValidatorTest extends ValidatorTestCase {
@Test
public void testAcceptsActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),
previousMsgId.getBytes());
previousMsgId.getBytes(), mac);
expectEncodeMetadata(ACTIVATE);
BdfMessageContext messageContext =
@@ -323,27 +323,37 @@ public class IntroductionValidatorTest extends ValidatorTestCase {
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes());
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),
previousMsgId.getBytes());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), null);
previousMsgId.getBytes(), mac, null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidSessionIdForActivate() throws Exception {
BdfList body =
BdfList.of(ACTIVATE.getValue(), null, previousMsgId.getBytes());
BdfList.of(ACTIVATE.getValue(), null, previousMsgId.getBytes(),
mac);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidPreviousMsgIdForActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(), 1);
BdfList body =
BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(), 1, mac);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsInvalidMacForActivate() throws Exception {
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),
previousMsgId.getBytes(), getRandomBytes(MAC_BYTES - 1));
validator.validateMessage(message, group, body);
}

View File

@@ -165,17 +165,17 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
sessionId, ephemeralPublicKey, acceptTimestamp,
transportProperties);
validator.validateMessage(m, group, clientHelper.toList(m));
AcceptMessage rm =
AcceptMessage am =
messageParser.parseAcceptMessage(m, clientHelper.toList(m));
assertEquals(m.getId(), rm.getMessageId());
assertEquals(m.getGroupId(), rm.getGroupId());
assertEquals(m.getTimestamp(), rm.getTimestamp());
assertEquals(previousMsgId, rm.getPreviousMessageId());
assertEquals(sessionId, rm.getSessionId());
assertArrayEquals(ephemeralPublicKey, rm.getEphemeralPublicKey());
assertEquals(acceptTimestamp, rm.getAcceptTimestamp());
assertEquals(transportProperties, rm.getTransportProperties());
assertEquals(m.getId(), am.getMessageId());
assertEquals(m.getGroupId(), am.getGroupId());
assertEquals(m.getTimestamp(), am.getTimestamp());
assertEquals(previousMsgId, am.getPreviousMessageId());
assertEquals(sessionId, am.getSessionId());
assertArrayEquals(ephemeralPublicKey, am.getEphemeralPublicKey());
assertEquals(acceptTimestamp, am.getAcceptTimestamp());
assertEquals(transportProperties, am.getTransportProperties());
}
@Test
@@ -184,14 +184,14 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
.encodeDeclineMessage(groupId, timestamp, previousMsgId,
sessionId);
validator.validateMessage(m, group, clientHelper.toList(m));
DeclineMessage rm =
DeclineMessage dm =
messageParser.parseDeclineMessage(m, clientHelper.toList(m));
assertEquals(m.getId(), rm.getMessageId());
assertEquals(m.getGroupId(), rm.getGroupId());
assertEquals(m.getTimestamp(), rm.getTimestamp());
assertEquals(previousMsgId, rm.getPreviousMessageId());
assertEquals(sessionId, rm.getSessionId());
assertEquals(m.getId(), dm.getMessageId());
assertEquals(m.getGroupId(), dm.getGroupId());
assertEquals(m.getTimestamp(), dm.getTimestamp());
assertEquals(previousMsgId, dm.getPreviousMessageId());
assertEquals(sessionId, dm.getSessionId());
}
@Test
@@ -200,32 +200,33 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
.encodeAuthMessage(groupId, timestamp, previousMsgId,
sessionId, mac, signature);
validator.validateMessage(m, group, clientHelper.toList(m));
AuthMessage rm =
AuthMessage am =
messageParser.parseAuthMessage(m, clientHelper.toList(m));
assertEquals(m.getId(), rm.getMessageId());
assertEquals(m.getGroupId(), rm.getGroupId());
assertEquals(m.getTimestamp(), rm.getTimestamp());
assertEquals(previousMsgId, rm.getPreviousMessageId());
assertEquals(sessionId, rm.getSessionId());
assertArrayEquals(mac, rm.getMac());
assertArrayEquals(signature, rm.getSignature());
assertEquals(m.getId(), am.getMessageId());
assertEquals(m.getGroupId(), am.getGroupId());
assertEquals(m.getTimestamp(), am.getTimestamp());
assertEquals(previousMsgId, am.getPreviousMessageId());
assertEquals(sessionId, am.getSessionId());
assertArrayEquals(mac, am.getMac());
assertArrayEquals(signature, am.getSignature());
}
@Test
public void testActivateMessage() throws Exception {
Message m = messageEncoder
.encodeActivateMessage(groupId, timestamp, previousMsgId,
sessionId);
sessionId, mac);
validator.validateMessage(m, group, clientHelper.toList(m));
ActivateMessage rm =
ActivateMessage am =
messageParser.parseActivateMessage(m, clientHelper.toList(m));
assertEquals(m.getId(), rm.getMessageId());
assertEquals(m.getGroupId(), rm.getGroupId());
assertEquals(m.getTimestamp(), rm.getTimestamp());
assertEquals(previousMsgId, rm.getPreviousMessageId());
assertEquals(sessionId, rm.getSessionId());
assertEquals(m.getId(), am.getMessageId());
assertEquals(m.getGroupId(), am.getGroupId());
assertEquals(m.getTimestamp(), am.getTimestamp());
assertEquals(previousMsgId, am.getPreviousMessageId());
assertEquals(sessionId, am.getSessionId());
assertArrayEquals(mac, am.getMac());
}
@Test
@@ -234,14 +235,14 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
.encodeAbortMessage(groupId, timestamp, previousMsgId,
sessionId);
validator.validateMessage(m, group, clientHelper.toList(m));
AbortMessage rm =
AbortMessage am =
messageParser.parseAbortMessage(m, clientHelper.toList(m));
assertEquals(m.getId(), rm.getMessageId());
assertEquals(m.getGroupId(), rm.getGroupId());
assertEquals(m.getTimestamp(), rm.getTimestamp());
assertEquals(previousMsgId, rm.getPreviousMessageId());
assertEquals(sessionId, rm.getSessionId());
assertEquals(m.getId(), am.getMessageId());
assertEquals(m.getGroupId(), am.getGroupId());
assertEquals(m.getTimestamp(), am.getTimestamp());
assertEquals(previousMsgId, am.getPreviousMessageId());
assertEquals(sessionId, am.getSessionId());
}
}

View File

@@ -28,15 +28,19 @@ import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.briarproject.bramble.test.TestUtils.getTransportPropertiesMap;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.introduction.Role.INTRODUCEE;
import static org.briarproject.briar.api.introduction.Role.INTRODUCER;
import static org.briarproject.briar.introduction.IntroduceeSession.Local;
import static org.briarproject.briar.introduction.IntroduceeSession.Remote;
import static org.briarproject.briar.introduction.IntroduceeState.LOCAL_ACCEPTED;
import static org.briarproject.briar.introduction.IntroducerState.AWAIT_AUTHS;
import static org.briarproject.briar.introduction.IntroductionConstants.SESSION_KEY_ROLE;
import static org.briarproject.briar.api.introduction.Role.INTRODUCEE;
import static org.briarproject.briar.api.introduction.Role.INTRODUCER;
import static org.briarproject.briar.test.BriarTestUtils.getRealAuthor;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class SessionEncoderParserIntegrationTest extends BrambleTestCase {
@@ -74,6 +78,8 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase {
private final Map<TransportId, TransportProperties>
remoteTransportProperties = getTransportPropertiesMap(3);
private final Map<TransportId, KeySetId> transportKeys = new HashMap<>();
private final byte[] localMacKey = getRandomBytes(SecretKey.LENGTH);
private final byte[] remoteMacKey = getRandomBytes(SecretKey.LENGTH);
public SessionEncoderParserIntegrationTest() {
BriarIntegrationTestComponent component =
@@ -82,8 +88,8 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase {
sessionEncoder = new SessionEncoderImpl(clientHelper);
sessionParser = new SessionParserImpl(clientHelper);
author1 = getRealAuthor();
author2 = getRealAuthor();
author1 = getRealAuthor(authorFactory);
author2 = getRealAuthor(authorFactory);
transportKeys.put(getTransportId(), new KeySetId(1));
transportKeys.put(getTransportId(), new KeySetId(2));
transportKeys.put(getTransportId(), new KeySetId(3));
@@ -167,48 +173,70 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase {
assertEquals(s1.getSessionId(), s2.getSessionId());
assertEquals(groupId1, s1.getContactGroupId());
assertEquals(s1.getContactGroupId(), s2.getContactGroupId());
assertEquals(author1, s1.getIntroducer());
assertEquals(s1.getIntroducer(), s2.getIntroducer());
assertArrayEquals(masterKey, s1.getMasterKey());
assertArrayEquals(s1.getMasterKey(), s2.getMasterKey());
assertEquals(transportKeys, s1.getTransportKeys());
assertEquals(s1.getTransportKeys(), s2.getTransportKeys());
assertEquals(localTimestamp, s1.getLocalTimestamp());
assertEquals(s1.getLocalTimestamp(), s2.getLocalTimestamp());
assertEquals(lastLocalMessageId, s1.getLastLocalMessageId());
assertEquals(s1.getLastLocalMessageId(), s2.getLastLocalMessageId());
assertEquals(lastRemoteMessageId, s1.getLastRemoteMessageId());
assertEquals(s1.getLastRemoteMessageId(), s2.getLastRemoteMessageId());
assertEquals(author1, s1.getIntroducer());
assertEquals(s1.getIntroducer(), s2.getIntroducer());
assertEquals(author2, s1.getRemoteAuthor());
assertEquals(s1.getRemoteAuthor(), s2.getRemoteAuthor());
assertArrayEquals(ephemeralPublicKey, s1.getEphemeralPublicKey());
assertArrayEquals(s1.getEphemeralPublicKey(),
s2.getEphemeralPublicKey());
assertArrayEquals(ephemeralPrivateKey, s1.getEphemeralPrivateKey());
assertArrayEquals(s1.getEphemeralPrivateKey(),
s2.getEphemeralPrivateKey());
assertEquals(acceptTimestamp, s1.getAcceptTimestamp());
assertEquals(s1.getAcceptTimestamp(), s2.getAcceptTimestamp());
assertArrayEquals(masterKey, s1.getMasterKey());
assertArrayEquals(s1.getMasterKey(), s2.getMasterKey());
assertArrayEquals(remoteEphemeralPublicKey, s1.getRemotePublicKey());
assertArrayEquals(s1.getRemotePublicKey(),
s2.getRemotePublicKey());
assertEquals(transportProperties, s1.getTransportProperties());
assertEquals(s1.getTransportProperties(), s2.getTransportProperties());
// check local
assertTrue(s1.getLocal().alice);
assertEquals(s1.getLocal().alice, s2.getLocal().alice);
assertEquals(lastLocalMessageId, s1.getLocal().lastMessageId);
assertEquals(s1.getLocal().lastMessageId, s2.getLocal().lastMessageId);
assertEquals(localTimestamp, s1.getLocal().lastMessageTimestamp);
assertEquals(s1.getLocal().lastMessageTimestamp,
s2.getLocal().lastMessageTimestamp);
assertArrayEquals(ephemeralPublicKey, s1.getLocal().ephemeralPublicKey);
assertArrayEquals(s1.getLocal().ephemeralPublicKey,
s2.getLocal().ephemeralPublicKey);
assertArrayEquals(ephemeralPrivateKey,
s1.getLocal().ephemeralPrivateKey);
assertArrayEquals(s1.getLocal().ephemeralPrivateKey,
s2.getLocal().ephemeralPrivateKey);
assertEquals(transportProperties, s1.getLocal().transportProperties);
assertEquals(s1.getLocal().transportProperties,
s2.getLocal().transportProperties);
assertEquals(acceptTimestamp, s1.getLocal().acceptTimestamp);
assertEquals(s1.getLocal().acceptTimestamp,
s2.getLocal().acceptTimestamp);
assertArrayEquals(localMacKey, s1.getLocal().macKey);
assertArrayEquals(s1.getLocal().macKey, s2.getLocal().macKey);
// check remote
assertFalse(s1.getRemote().alice);
assertEquals(s1.getRemote().alice, s2.getRemote().alice);
assertEquals(author2, s1.getRemote().author);
assertEquals(s1.getRemote().author, s2.getRemote().author);
assertEquals(lastRemoteMessageId, s1.getRemote().lastMessageId);
assertEquals(s1.getRemote().lastMessageId,
s2.getRemote().lastMessageId);
assertArrayEquals(remoteEphemeralPublicKey,
s1.getRemote().ephemeralPublicKey);
assertArrayEquals(s1.getRemote().ephemeralPublicKey,
s2.getRemote().ephemeralPublicKey);
assertEquals(remoteTransportProperties,
s1.getRemoteTransportProperties());
assertEquals(s1.getRemoteTransportProperties(),
s2.getRemoteTransportProperties());
assertEquals(remoteAcceptTimestamp, s1.getRemoteAcceptTimestamp());
assertEquals(s1.getRemoteAcceptTimestamp(), s2.getRemoteAcceptTimestamp());
assertEquals(transportKeys, s1.getTransportKeys());
assertEquals(s1.getTransportKeys(), s2.getTransportKeys());
s1.getRemote().transportProperties);
assertEquals(s1.getRemote().transportProperties,
s2.getRemote().transportProperties);
assertEquals(remoteAcceptTimestamp, s1.getRemote().acceptTimestamp);
assertEquals(s1.getRemote().acceptTimestamp,
s2.getRemote().acceptTimestamp);
assertArrayEquals(remoteMacKey, s1.getRemote().macKey);
assertArrayEquals(s1.getRemote().macKey, s2.getRemote().macKey);
}
@Test
public void testIntroduceeSessionWithNulls() throws FormatException {
IntroduceeSession s1 =
new IntroduceeSession(sessionId, LOCAL_ACCEPTED,
requestTimestamp, groupId1, null, localTimestamp, null,
author1, null, null, null, acceptTimestamp, null,
author2, null, null, remoteAcceptTimestamp, null);
IntroduceeSession s1 = IntroduceeSession
.getInitial(groupId1, sessionId, author1, false, author2);
BdfDictionary d = sessionEncoder.encodeIntroduceeSession(s1);
IntroduceeSession s2 =
@@ -218,14 +246,38 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase {
assertEquals(s1.getLastLocalMessageId(), s2.getLastLocalMessageId());
assertNull(s1.getLastRemoteMessageId());
assertEquals(s1.getLastRemoteMessageId(), s2.getLastRemoteMessageId());
assertNull(s1.getEphemeralPublicKey());
assertArrayEquals(s1.getEphemeralPublicKey(),
s2.getEphemeralPublicKey());
assertNull(s1.getEphemeralPrivateKey());
assertArrayEquals(s1.getEphemeralPrivateKey(),
s2.getEphemeralPrivateKey());
assertNull(s1.getMasterKey());
assertEquals(s1.getMasterKey(), s2.getMasterKey());
assertNull(s1.getTransportKeys());
assertEquals(s1.getTransportKeys(), s2.getTransportKeys());
// check local
assertNull(s1.getLocal().lastMessageId);
assertEquals(s1.getLocal().lastMessageId, s2.getLocal().lastMessageId);
assertNull(s1.getLocal().ephemeralPublicKey);
assertEquals(s1.getLocal().ephemeralPublicKey,
s2.getLocal().ephemeralPublicKey);
assertNull(s1.getLocal().ephemeralPrivateKey);
assertEquals(s1.getLocal().ephemeralPrivateKey,
s2.getLocal().ephemeralPrivateKey);
assertNull(s1.getLocal().transportProperties);
assertEquals(s1.getLocal().transportProperties,
s2.getLocal().transportProperties);
assertNull(s1.getLocal().macKey);
assertEquals(s1.getLocal().macKey, s2.getLocal().macKey);
// check remote
assertNull(s1.getRemote().lastMessageId);
assertEquals(s1.getRemote().lastMessageId,
s2.getRemote().lastMessageId);
assertNull(s1.getRemote().ephemeralPublicKey);
assertEquals(s1.getRemote().ephemeralPublicKey,
s2.getRemote().ephemeralPublicKey);
assertNull(s1.getRemote().transportProperties);
assertEquals(s1.getRemote().transportProperties,
s2.getRemote().transportProperties);
assertNull(s1.getRemote().macKey);
assertEquals(s1.getRemote().macKey, s2.getRemote().macKey);
}
@Test(expected = FormatException.class)
@@ -256,13 +308,15 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase {
}
private IntroduceeSession getIntroduceeSession() {
Local local = new Local(true, lastLocalMessageId, localTimestamp,
ephemeralPublicKey, ephemeralPrivateKey, transportProperties,
acceptTimestamp, localMacKey);
Remote remote = new Remote(false, author2, lastRemoteMessageId,
remoteEphemeralPublicKey, remoteTransportProperties,
remoteAcceptTimestamp, remoteMacKey);
return new IntroduceeSession(sessionId, LOCAL_ACCEPTED,
requestTimestamp, groupId1, lastLocalMessageId, localTimestamp,
lastRemoteMessageId, author1, ephemeralPublicKey,
ephemeralPrivateKey, transportProperties, acceptTimestamp,
masterKey, author2, remoteEphemeralPublicKey,
remoteTransportProperties, remoteAcceptTimestamp,
transportKeys);
requestTimestamp, groupId1, author1, local, remote,
masterKey, transportKeys);
}
private void assertIntroduceeEquals(Introducee i1, Introducee i2) {
@@ -273,9 +327,4 @@ public class SessionEncoderParserIntegrationTest extends BrambleTestCase {
assertEquals(i1.lastRemoteMessageId, i2.lastRemoteMessageId);
}
private Author getRealAuthor() {
return authorFactory.createAuthor(getRandomString(5),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
}
}