diff --git a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionConstants.java index 9b454218d5e736f5d17546e4bbe67775e40684f1..87681d525076125d90ed43e7437911d4aea8f97e 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionConstants.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionConstants.java @@ -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"; + } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java index 300d3f6fb1e3f638066ada01a52c9599cc7ec1d8..75af1b87ca73cfe6e04a61506b709f282bee37e7 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java @@ -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; } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/ActivateMessage.java b/briar-core/src/main/java/org/briarproject/briar/introduction/ActivateMessage.java index e7498dec2781c0ed36f8bebd8077ebabb47d2f5e..5f767737d90fe68029f80e2e0ab4d87932df13ce 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/ActivateMessage.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/ActivateMessage.java @@ -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; + } + } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java index 44714170064ecda2ff3afa2b60d51577292cbf67..ce7d5f76c2c45f2268aa9624f7c8336ab790c76c 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java @@ -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(); diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java index 1aadc39ad564fe22c44f6a111fda939f3146ee6d..92b06abc05a36f5f50c714850a9dfe74befe2c33 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeSession.java @@ -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); + } + } + } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java index 2fbe0cab8b11e92ea60e2b702ca939b1476f763a..7115ec089232ddfda5cdd56f25b3aa540c67713a 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java @@ -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; diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionConstants.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionConstants.java index afb1ff3e70ef31e643b99f61bb6e2e3283a65636..522759a55bcafa40e32b2cbe0efa691064d19893 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionConstants.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionConstants.java @@ -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"; } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCrypto.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCrypto.java index 3573f348a7f9911fd84db234fe5be9bad58d48ff..7504270d91d871f26c6a53530e6c5d56044ed390 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCrypto.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCrypto.java @@ -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; + } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCryptoImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCryptoImpl.java index 18e2cbe1200440ff14c52e572d3167a0a0daaeef..6712a3153aae4b214aca863180647b5ca1e82833 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCryptoImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionCryptoImpl.java @@ -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(); + } + } + } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java index 959d1d92bda4d295ac4ae54a38f450ee075602c4..d14bd46c70c5fab3317c21b5212c4da7a26d1790 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java @@ -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(), diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionValidator.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionValidator.java index 43027b40b138a49f0ab5b2dea7c573be86c71ba3..6b59a64ae4fb954b485e126e846d2fc887c6018c 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionValidator.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionValidator.java @@ -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); diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoder.java b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoder.java index 42eb41c0f4cc439737fb1736b6356a247493ad35..2eed3057a0678723cded22c787391569a51e3ac8 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoder.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoder.java @@ -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); diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoderImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoderImpl.java index 861569ac5ec9ffa7e5243a1c773b44abfa8c3d65..89d3e40e5912324bdc33a13debc54183336f41ed 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoderImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageEncoderImpl.java @@ -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; - } - } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageParserImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageParserImpl.java index ef97282dd894e63069e65b08488fb37a5a7874d2..69ddd242d1cab3137d7e312905d16388362a629f 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/MessageParserImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/MessageParserImpl.java @@ -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 diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/Session.java b/briar-core/src/main/java/org/briarproject/briar/introduction/Session.java index ddf04d044eb9e95522686d0c2113745a556ed91d..086dfb1a2975defaf177206dbce5cb65cac7edcb 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/Session.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/Session.java @@ -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; } diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/SessionEncoderImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/SessionEncoderImpl.java index 2ffee61d88f0743eb4f908c080ed93c00c4fd27f..9023f0d94d3ebb466b9229038766c7d903dd6bf4 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/SessionEncoderImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/SessionEncoderImpl.java @@ -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()); diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/SessionParserImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/SessionParserImpl.java index 7c5b2192596a77b1e89d8ce1a52f7e94d6a70248..52c12e5477ae040cc77be6193eeed5108d600662 100644 --- a/briar-core/src/main/java/org/briarproject/briar/introduction/SessionParserImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/introduction/SessionParserImpl.java @@ -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 { diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionCryptoImplTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionCryptoImplTest.java index b8c636400d1ee102312f0fea368315d22728a426..59cf3b4f4ae9760312e0b7771753e9e3f8b445e0 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionCryptoImplTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionCryptoImplTest.java @@ -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); + } + } diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java index 1adfb9207e6a76fc086ecb9424efff87ea1a7e1a..3dccbbc8dec17248f29b337b176263c7b6be2fd6 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java @@ -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); diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionValidatorTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionValidatorTest.java index fbebeece406644f002922b5006550e942f28d38f..7614121cf54a19d6a5c23f553c6e9a163e923791 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionValidatorTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionValidatorTest.java @@ -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); } diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/MessageEncoderParserIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/MessageEncoderParserIntegrationTest.java index a4c155897960c43571e2933cbe95ed3584f7b843..f3480f15832cb6b3d80edb2281ee9dca09a986bc 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/MessageEncoderParserIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/MessageEncoderParserIntegrationTest.java @@ -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()); } } diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/SessionEncoderParserIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/SessionEncoderParserIntegrationTest.java index 9d773bac0c4fbd0bcd6a2e354a2fd562b300b52c..ffbcaa94369cd587837143c338c9198e7dfdbcbe 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/SessionEncoderParserIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/SessionEncoderParserIntegrationTest.java @@ -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)); - } - }