mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Merge branch '2079-reject-old-timestamps' into '1802-sync-via-removable-storage'
Reject old timestamps when deriving rotation mode keys See merge request briar/briar!1481
This commit is contained in:
@@ -1,23 +1,19 @@
|
||||
package org.briarproject.bramble.api.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
|
||||
/**
|
||||
* A secret key used for encryption and/or authentication.
|
||||
*/
|
||||
public class SecretKey {
|
||||
public class SecretKey extends Bytes {
|
||||
|
||||
/**
|
||||
* The length of a secret key in bytes.
|
||||
*/
|
||||
public static final int LENGTH = 32;
|
||||
|
||||
private final byte[] key;
|
||||
|
||||
public SecretKey(byte[] key) {
|
||||
super(key);
|
||||
if (key.length != LENGTH) throw new IllegalArgumentException();
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ 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.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.Wakeful;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -30,26 +31,6 @@ public interface LifecycleManager {
|
||||
SUCCESS
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimum reasonable value for the system clock, in milliseconds
|
||||
* since the Unix epoch. {@link #startServices(SecretKey)} will return
|
||||
* {@link StartResult#CLOCK_ERROR} if the system clock reports an earlier
|
||||
* time.
|
||||
* <p/>
|
||||
* 1 Jan 2021, 00:00:00 UTC
|
||||
*/
|
||||
long MIN_REASONABLE_TIME_MS = 1_609_459_200_000L;
|
||||
|
||||
/**
|
||||
* The maximum reasonable value for the system clock, in milliseconds
|
||||
* since the Unix epoch. {@link #startServices(SecretKey)} will return
|
||||
* {@link StartResult#CLOCK_ERROR} if the system clock reports a later
|
||||
* time.
|
||||
* <p/>
|
||||
* 1 Jan 2121, 00:00:00 UTC
|
||||
*/
|
||||
long MAX_REASONABLE_TIME_MS = 4_765_132_800_000L;
|
||||
|
||||
/**
|
||||
* The state the lifecycle can be in.
|
||||
* Returned by {@link #getLifecycleState()}
|
||||
@@ -86,6 +67,10 @@ public interface LifecycleManager {
|
||||
/**
|
||||
* Opens the {@link DatabaseComponent} using the given key and starts any
|
||||
* registered {@link Service Services}.
|
||||
*
|
||||
* @return {@link StartResult#CLOCK_ERROR} if the system clock is earlier
|
||||
* than {@link Clock#MIN_REASONABLE_TIME_MS} or later than
|
||||
* {@link Clock#MAX_REASONABLE_TIME_MS}.
|
||||
*/
|
||||
@Wakeful
|
||||
StartResult startServices(SecretKey dbKey);
|
||||
|
||||
@@ -6,6 +6,22 @@ package org.briarproject.bramble.api.system;
|
||||
*/
|
||||
public interface Clock {
|
||||
|
||||
/**
|
||||
* The minimum reasonable value for the system clock, in milliseconds
|
||||
* since the Unix epoch.
|
||||
* <p/>
|
||||
* 1 Jan 2021, 00:00:00 UTC
|
||||
*/
|
||||
long MIN_REASONABLE_TIME_MS = 1_609_459_200_000L;
|
||||
|
||||
/**
|
||||
* The maximum reasonable value for the system clock, in milliseconds
|
||||
* since the Unix epoch.
|
||||
* <p/>
|
||||
* 1 Jan 2121, 00:00:00 UTC
|
||||
*/
|
||||
long MAX_REASONABLE_TIME_MS = 4_765_132_800_000L;
|
||||
|
||||
/**
|
||||
* @see System#currentTimeMillis()
|
||||
*/
|
||||
|
||||
@@ -163,10 +163,15 @@ public class TestUtils {
|
||||
|
||||
public static Message getMessage(GroupId groupId) {
|
||||
int bodyLength = 1 + random.nextInt(MAX_MESSAGE_BODY_LENGTH);
|
||||
return getMessage(groupId, bodyLength);
|
||||
return getMessage(groupId, bodyLength, timestamp);
|
||||
}
|
||||
|
||||
public static Message getMessage(GroupId groupId, int bodyLength) {
|
||||
return getMessage(groupId, bodyLength, timestamp);
|
||||
}
|
||||
|
||||
public static Message getMessage(GroupId groupId, int bodyLength,
|
||||
long timestamp) {
|
||||
MessageId id = new MessageId(getRandomId());
|
||||
byte[] body = getRandomBytes(bodyLength);
|
||||
return new Message(id, groupId, timestamp, body);
|
||||
|
||||
@@ -47,6 +47,7 @@ import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
||||
import static org.briarproject.bramble.api.system.Clock.MIN_REASONABLE_TIME_MS;
|
||||
import static org.briarproject.bramble.contact.ContactExchangeConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.contact.ContactExchangeRecordTypes.CONTACT_INFO;
|
||||
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
||||
@@ -184,6 +185,10 @@ class ContactExchangeManagerImpl implements ContactExchangeManager {
|
||||
|
||||
// The agreed timestamp is the minimum of the peers' timestamps
|
||||
long timestamp = Math.min(localTimestamp, remoteInfo.timestamp);
|
||||
if (timestamp < MIN_REASONABLE_TIME_MS) {
|
||||
LOG.warning("Timestamp is too old");
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
// Add the contact
|
||||
Contact contact = addContact(p, remoteInfo.author, localAuthor,
|
||||
|
||||
@@ -41,6 +41,8 @@ import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResul
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DB_ERROR;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SERVICE_ERROR;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
||||
import static org.briarproject.bramble.api.system.Clock.MAX_REASONABLE_TIME_MS;
|
||||
import static org.briarproject.bramble.api.system.Clock.MIN_REASONABLE_TIME_MS;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
|
||||
@@ -19,6 +19,7 @@ import javax.annotation.concurrent.Immutable;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
||||
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
|
||||
import static org.briarproject.bramble.api.system.Clock.MIN_REASONABLE_TIME_MS;
|
||||
import static org.briarproject.bramble.transport.agreement.MessageType.ACTIVATE;
|
||||
import static org.briarproject.bramble.transport.agreement.MessageType.KEY;
|
||||
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.MSG_KEY_PUBLIC_KEY;
|
||||
@@ -42,13 +43,14 @@ class TransportKeyAgreementValidator extends BdfMessageValidator {
|
||||
protected BdfMessageContext validateMessage(Message m, Group g,
|
||||
BdfList body) throws FormatException {
|
||||
MessageType type = MessageType.fromValue(body.getLong(0).intValue());
|
||||
if (type == KEY) return validateKeyMessage(body);
|
||||
if (type == KEY) return validateKeyMessage(m.getTimestamp(), body);
|
||||
else if (type == ACTIVATE) return validateActivateMessage(body);
|
||||
else throw new AssertionError();
|
||||
}
|
||||
|
||||
private BdfMessageContext validateKeyMessage(BdfList body)
|
||||
private BdfMessageContext validateKeyMessage(long timestamp, BdfList body)
|
||||
throws FormatException {
|
||||
if (timestamp < MIN_REASONABLE_TIME_MS) throw new FormatException();
|
||||
// Message type, transport ID, public key
|
||||
checkSize(body, 3);
|
||||
String transportId = body.getString(1);
|
||||
|
||||
@@ -15,10 +15,10 @@ import org.junit.Test;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.MAX_REASONABLE_TIME_MS;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.MIN_REASONABLE_TIME_MS;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.CLOCK_ERROR;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
||||
import static org.briarproject.bramble.api.system.Clock.MAX_REASONABLE_TIME_MS;
|
||||
import static org.briarproject.bramble.api.system.Clock.MIN_REASONABLE_TIME_MS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
||||
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
|
||||
import static org.briarproject.bramble.api.system.Clock.MIN_REASONABLE_TIME_MS;
|
||||
import static org.briarproject.bramble.api.versioning.ClientVersioningManager.CLIENT_ID;
|
||||
import static org.briarproject.bramble.api.versioning.ClientVersioningManager.MAJOR_VERSION;
|
||||
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
||||
@@ -109,6 +110,27 @@ public class TransportKeyAgreementValidatorTest extends BrambleMockTestCase {
|
||||
assertArrayEquals(publicKey, d.getRaw(MSG_KEY_PUBLIC_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptsMinTimestampKeyMsg() throws Exception {
|
||||
Message message =
|
||||
getMessage(group.getId(), 1234, MIN_REASONABLE_TIME_MS);
|
||||
TransportId transportId = new TransportId(getRandomString(1));
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(messageEncoder)
|
||||
.encodeMessageMetadata(transportId, KEY, false);
|
||||
will(returnValue(new BdfDictionary()));
|
||||
}});
|
||||
|
||||
byte[] publicKey = getRandomBytes(1);
|
||||
BdfList body =
|
||||
BdfList.of(KEY.getValue(), transportId.getString(), publicKey);
|
||||
BdfMessageContext msgCtx =
|
||||
validator.validateMessage(message, group, body);
|
||||
assertEquals(emptyList(), msgCtx.getDependencies());
|
||||
BdfDictionary d = msgCtx.getDictionary();
|
||||
assertArrayEquals(publicKey, d.getRaw(MSG_KEY_PUBLIC_KEY));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooLongKeyMsg() throws Exception {
|
||||
BdfList body = BdfList.of(KEY.getValue(), getRandomString(1),
|
||||
@@ -168,6 +190,15 @@ public class TransportKeyAgreementValidatorTest extends BrambleMockTestCase {
|
||||
validator.validateMessage(message, group, body);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooOldTimestampKeyMsg() throws Exception {
|
||||
Message message =
|
||||
getMessage(group.getId(), 1234, MIN_REASONABLE_TIME_MS - 1);
|
||||
BdfList body = BdfList.of(KEY.getValue(), getRandomString(1),
|
||||
getRandomBytes(1));
|
||||
validator.validateMessage(message, group, body);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptsActivateMsg() throws Exception {
|
||||
TransportId transportId = new TransportId(getRandomString(1));
|
||||
|
||||
@@ -4,6 +4,7 @@ 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.PrivateKey;
|
||||
@@ -48,6 +49,7 @@ import javax.inject.Inject;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.api.system.Clock.MIN_REASONABLE_TIME_MS;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_AUTH;
|
||||
import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_RESPONSES;
|
||||
@@ -453,30 +455,30 @@ class IntroduceeProtocolEngine
|
||||
long timestamp = Math.min(s.getLocal().acceptTimestamp,
|
||||
s.getRemote().acceptTimestamp);
|
||||
if (timestamp == -1) throw new AssertionError();
|
||||
if (timestamp < MIN_REASONABLE_TIME_MS) {
|
||||
LOG.warning("Timestamp is too old");
|
||||
return abort(txn, s, m.getMessageId());
|
||||
}
|
||||
|
||||
Map<TransportId, KeySetId> keys = null;
|
||||
try {
|
||||
contactManager.addContact(txn, s.getRemote().author,
|
||||
localAuthor.getId(), false);
|
||||
ContactId contactId = contactManager.addContact(txn,
|
||||
s.getRemote().author, localAuthor.getId(), false);
|
||||
|
||||
// Only add transport properties and keys when the contact was added
|
||||
// This will be changed once we have a way to reset state for peers
|
||||
// that were contacts already at some point in the past.
|
||||
Contact c = contactManager.getContact(txn,
|
||||
s.getRemote().author.getId(), localAuthor.getId());
|
||||
|
||||
// add the keys to the new contact
|
||||
keys = keyManager.addRotationKeys(txn, c.getId(),
|
||||
keys = keyManager.addRotationKeys(txn, contactId,
|
||||
new SecretKey(s.getMasterKey()), timestamp,
|
||||
s.getLocal().alice, false);
|
||||
|
||||
// add signed transport properties for the contact
|
||||
transportPropertyManager.addRemoteProperties(txn, c.getId(),
|
||||
transportPropertyManager.addRemoteProperties(txn, contactId,
|
||||
s.getRemote().transportProperties);
|
||||
} catch (ContactExistsException e) {
|
||||
// Ignore this, because the other introducee might have deleted us.
|
||||
// So we still want updated transport properties
|
||||
// and new transport keys.
|
||||
// Ignore this, because the other introducee might have deleted us
|
||||
}
|
||||
|
||||
// send ACTIVATE message with a MAC
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
package org.briarproject.briar.introduction;
|
||||
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.client.ContactGroupFactory;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
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.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.transport.KeyManager;
|
||||
import org.briarproject.bramble.api.transport.KeySetId;
|
||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
|
||||
import org.briarproject.briar.api.client.MessageTracker;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
import org.briarproject.briar.api.conversation.ConversationManager;
|
||||
import org.briarproject.briar.api.identity.AuthorManager;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Random;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAC_BYTES;
|
||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_BYTES;
|
||||
import static org.briarproject.bramble.api.system.Clock.MIN_REASONABLE_TIME_MS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
|
||||
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
|
||||
import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_ACTIVATE;
|
||||
import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_AUTH;
|
||||
import static org.briarproject.briar.introduction.IntroduceeState.START;
|
||||
import static org.briarproject.briar.introduction.MessageType.ABORT;
|
||||
import static org.briarproject.briar.introduction.MessageType.ACTIVATE;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class IntroduceeProtocolEngineTest extends BrambleMockTestCase {
|
||||
|
||||
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
|
||||
private final ContactManager contactManager =
|
||||
context.mock(ContactManager.class);
|
||||
private final ContactGroupFactory contactGroupFactory =
|
||||
context.mock(ContactGroupFactory.class);
|
||||
private final MessageTracker messageTracker =
|
||||
context.mock(MessageTracker.class);
|
||||
private final IdentityManager identityManager =
|
||||
context.mock(IdentityManager.class);
|
||||
private final AuthorManager authorManager =
|
||||
context.mock(AuthorManager.class);
|
||||
private final MessageParser messageParser =
|
||||
context.mock(MessageParser.class);
|
||||
private final MessageEncoder messageEncoder =
|
||||
context.mock(MessageEncoder.class);
|
||||
private final IntroductionCrypto crypto =
|
||||
context.mock(IntroductionCrypto.class);
|
||||
private final KeyManager keyManager = context.mock(KeyManager.class);
|
||||
private final TransportPropertyManager transportPropertyManager =
|
||||
context.mock(TransportPropertyManager.class);
|
||||
private final ClientVersioningManager clientVersioningManager =
|
||||
context.mock(ClientVersioningManager.class);
|
||||
private final AutoDeleteManager autoDeleteManager =
|
||||
context.mock(AutoDeleteManager.class);
|
||||
private final ConversationManager conversationManager =
|
||||
context.mock(ConversationManager.class);
|
||||
private final Clock clock = context.mock(Clock.class);
|
||||
|
||||
private final Author introducer = getAuthor();
|
||||
private final Author remoteIntroducee = getAuthor();
|
||||
private final LocalAuthor localIntroducee = getLocalAuthor();
|
||||
private final ContactId contactId = getContactId();
|
||||
private final GroupId contactGroupId = new GroupId(getRandomId());
|
||||
private final SessionId sessionId = new SessionId(getRandomId());
|
||||
private final boolean alice = new Random().nextBoolean();
|
||||
private final long now = System.currentTimeMillis();
|
||||
private final long requestTimestamp = now - 12345;
|
||||
private final long localAcceptTimestamp = requestTimestamp + 123;
|
||||
private final long remoteAuthTimestamp = localAcceptTimestamp + 123;
|
||||
private final MessageId lastLocalMessageId = new MessageId(getRandomId());
|
||||
private final MessageId lastRemoteMessageId = new MessageId(getRandomId());
|
||||
private final PublicKey localPublicKey = getAgreementPublicKey();
|
||||
private final PrivateKey localPrivateKey = getAgreementPrivateKey();
|
||||
private final PublicKey remotePublicKey = getAgreementPublicKey();
|
||||
private final SecretKey localMacKey = getSecretKey();
|
||||
private final SecretKey remoteMacKey = getSecretKey();
|
||||
private final SecretKey masterKey = getSecretKey();
|
||||
private final byte[] remoteMac = getRandomBytes(MAC_BYTES);
|
||||
private final byte[] localMac = getRandomBytes(MAC_BYTES);
|
||||
private final byte[] remoteSignature = getRandomBytes(MAX_SIGNATURE_BYTES);
|
||||
|
||||
private final IntroduceeProtocolEngine engine =
|
||||
new IntroduceeProtocolEngine(db, clientHelper, contactManager,
|
||||
contactGroupFactory, messageTracker, identityManager,
|
||||
authorManager, messageParser, messageEncoder, crypto,
|
||||
keyManager, transportPropertyManager,
|
||||
clientVersioningManager, autoDeleteManager,
|
||||
conversationManager, clock);
|
||||
|
||||
@Test
|
||||
public void testDoesNotAbortSessionIfTimestampIsMaxAge() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
long remoteAcceptTimestamp = MIN_REASONABLE_TIME_MS;
|
||||
IntroduceeSession session =
|
||||
createAwaitAuthSession(remoteAcceptTimestamp);
|
||||
|
||||
AuthMessage authMessage = new AuthMessage(new MessageId(getRandomId()),
|
||||
contactGroupId, remoteAuthTimestamp, lastRemoteMessageId,
|
||||
sessionId, remoteMac, remoteSignature);
|
||||
|
||||
Message activateMessage = getMessage(contactGroupId, 1234, now);
|
||||
BdfDictionary activateMeta = new BdfDictionary();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
// Verify the auth message
|
||||
oneOf(identityManager).getLocalAuthor(txn);
|
||||
will(returnValue(localIntroducee));
|
||||
oneOf(crypto).verifyAuthMac(remoteMac, session,
|
||||
localIntroducee.getId());
|
||||
oneOf(crypto).verifySignature(remoteSignature, session);
|
||||
// Add the contact
|
||||
oneOf(contactManager).addContact(txn, remoteIntroducee,
|
||||
localIntroducee.getId(), false);
|
||||
will(returnValue(contactId));
|
||||
oneOf(keyManager).addRotationKeys(txn, contactId, masterKey,
|
||||
remoteAcceptTimestamp, alice, false);
|
||||
will(returnValue(emptyMap()));
|
||||
oneOf(transportPropertyManager).addRemoteProperties(txn, contactId,
|
||||
emptyMap());
|
||||
// Send the activate message
|
||||
oneOf(crypto).activateMac(session);
|
||||
will(returnValue(localMac));
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(messageEncoder).encodeActivateMessage(contactGroupId, now,
|
||||
lastLocalMessageId, sessionId, localMac);
|
||||
will(returnValue(activateMessage));
|
||||
oneOf(messageEncoder).encodeMetadata(ACTIVATE, sessionId, now,
|
||||
true, true, false, NO_AUTO_DELETE_TIMER, false);
|
||||
will(returnValue(activateMeta));
|
||||
oneOf(clientHelper).addLocalMessage(txn, activateMessage,
|
||||
activateMeta, true, false);
|
||||
}});
|
||||
|
||||
IntroduceeSession after =
|
||||
engine.onAuthMessage(txn, session, authMessage);
|
||||
|
||||
assertEquals(AWAIT_ACTIVATE, after.getState());
|
||||
assertNull(after.getMasterKey());
|
||||
assertEquals(Collections.<TransportId, KeySetId>emptyMap(),
|
||||
after.getTransportKeys());
|
||||
|
||||
IntroduceeSession.Local afterLocal = after.getLocal();
|
||||
assertEquals(activateMessage.getId(), afterLocal.lastMessageId);
|
||||
assertEquals(now, afterLocal.lastMessageTimestamp);
|
||||
|
||||
IntroduceeSession.Remote afterRemote = after.getRemote();
|
||||
assertEquals(authMessage.getMessageId(), afterRemote.lastMessageId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbortsSessionIfTimestampIsTooOld() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
long remoteAcceptTimestamp = MIN_REASONABLE_TIME_MS - 1;
|
||||
IntroduceeSession session =
|
||||
createAwaitAuthSession(remoteAcceptTimestamp);
|
||||
|
||||
AuthMessage authMessage = new AuthMessage(new MessageId(getRandomId()),
|
||||
contactGroupId, remoteAuthTimestamp, lastRemoteMessageId,
|
||||
sessionId, remoteMac, remoteSignature);
|
||||
|
||||
BdfDictionary query = new BdfDictionary();
|
||||
Message abortMessage = getMessage(contactGroupId, 123, now);
|
||||
BdfDictionary abortMeta = new BdfDictionary();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
// Verify the auth message
|
||||
oneOf(identityManager).getLocalAuthor(txn);
|
||||
will(returnValue(localIntroducee));
|
||||
oneOf(crypto).verifyAuthMac(remoteMac, session,
|
||||
localIntroducee.getId());
|
||||
oneOf(crypto).verifySignature(remoteSignature, session);
|
||||
// Abort the session
|
||||
oneOf(messageParser).getRequestsAvailableToAnswerQuery(sessionId);
|
||||
will(returnValue(query));
|
||||
oneOf(clientHelper).getMessageIds(txn, contactGroupId, query);
|
||||
will(returnValue(emptyList()));
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(messageEncoder).encodeAbortMessage(contactGroupId, now,
|
||||
lastLocalMessageId, sessionId);
|
||||
will(returnValue(abortMessage));
|
||||
oneOf(messageEncoder).encodeMetadata(ABORT, sessionId, now,
|
||||
true, true, false, NO_AUTO_DELETE_TIMER, false);
|
||||
will(returnValue(abortMeta));
|
||||
oneOf(clientHelper).addLocalMessage(txn, abortMessage, abortMeta,
|
||||
true, false);
|
||||
}});
|
||||
|
||||
IntroduceeSession after =
|
||||
engine.onAuthMessage(txn, session, authMessage);
|
||||
|
||||
assertEquals(START, after.getState());
|
||||
assertNull(after.getMasterKey());
|
||||
assertNull(after.getTransportKeys());
|
||||
|
||||
IntroduceeSession.Local afterLocal = after.getLocal();
|
||||
assertEquals(abortMessage.getId(), afterLocal.lastMessageId);
|
||||
assertEquals(now, afterLocal.lastMessageTimestamp);
|
||||
assertNull(afterLocal.ephemeralPublicKey);
|
||||
assertNull(afterLocal.ephemeralPrivateKey);
|
||||
assertNull(afterLocal.macKey);
|
||||
|
||||
IntroduceeSession.Remote afterRemote = after.getRemote();
|
||||
assertEquals(authMessage.getMessageId(), afterRemote.lastMessageId);
|
||||
assertNull(afterRemote.ephemeralPublicKey);
|
||||
assertNull(afterRemote.macKey);
|
||||
}
|
||||
|
||||
private IntroduceeSession createAwaitAuthSession(
|
||||
long remoteAcceptTimestamp) {
|
||||
IntroduceeSession.Local local = new IntroduceeSession.Local(alice,
|
||||
lastLocalMessageId, localAcceptTimestamp, localPublicKey,
|
||||
localPrivateKey, emptyMap(), localAcceptTimestamp,
|
||||
localMacKey.getBytes());
|
||||
IntroduceeSession.Remote remote = new IntroduceeSession.Remote(!alice,
|
||||
remoteIntroducee, lastRemoteMessageId, remotePublicKey,
|
||||
emptyMap(), remoteAcceptTimestamp, remoteMacKey.getBytes());
|
||||
return new IntroduceeSession(sessionId,
|
||||
AWAIT_AUTH, requestTimestamp, contactGroupId, introducer,
|
||||
local, remote, masterKey.getBytes(), emptyMap());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user