diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/SecretKey.java b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/SecretKey.java index 319f9305b..b88bc01da 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/SecretKey.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/SecretKey.java @@ -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; } } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/LifecycleManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/LifecycleManager.java index dcf8e8ce9..12bc36248 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/LifecycleManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/LifecycleManager.java @@ -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. - *
- * 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. - * - * 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); diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/system/Clock.java b/bramble-api/src/main/java/org/briarproject/bramble/api/system/Clock.java index 2b55c4196..1835aa2bc 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/system/Clock.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/system/Clock.java @@ -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. + * + * 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. + * + * 1 Jan 2121, 00:00:00 UTC + */ + long MAX_REASONABLE_TIME_MS = 4_765_132_800_000L; + /** * @see System#currentTimeMillis() */ diff --git a/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java b/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java index e39e9aa62..3e171904e 100644 --- a/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java +++ b/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java @@ -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); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeManagerImpl.java index 9e3bd03a7..27870ca23 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactExchangeManagerImpl.java @@ -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, diff --git a/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java index 5efab84ca..992f05bdb 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java @@ -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; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/agreement/TransportKeyAgreementValidator.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/agreement/TransportKeyAgreementValidator.java index 1f4053b2f..c371fca77 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/transport/agreement/TransportKeyAgreementValidator.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/agreement/TransportKeyAgreementValidator.java @@ -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); diff --git a/bramble-core/src/test/java/org/briarproject/bramble/lifecycle/LifecycleManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/lifecycle/LifecycleManagerImplTest.java index 079a02b5e..d057573fc 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/lifecycle/LifecycleManagerImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/lifecycle/LifecycleManagerImplTest.java @@ -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; diff --git a/bramble-core/src/test/java/org/briarproject/bramble/transport/agreement/TransportKeyAgreementValidatorTest.java b/bramble-core/src/test/java/org/briarproject/bramble/transport/agreement/TransportKeyAgreementValidatorTest.java index 0437a026c..0a8be77ee 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/transport/agreement/TransportKeyAgreementValidatorTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/transport/agreement/TransportKeyAgreementValidatorTest.java @@ -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)); 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 41a3d71af..d198a3bd3 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,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