diff --git a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java index 44eb0aad30e4bef22adc6ef6536ba4f67ed7b039..aad72c171eed9cbd5b4c1711df9f3a640386f9d6 100644 --- a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java +++ b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java @@ -244,7 +244,8 @@ NoContactsDialog.Listener { FragmentManager fm = getSupportFragmentManager(); noContactsDialog.show(fm, "NoContactsDialog"); } else { - startActivity(new Intent(this, WritePrivateMessageActivity.class)); + startActivity(new Intent(this, + WritePrivateMessageActivity.class)); } } else if(view == shareButton) { String apkPath = getPackageCodePath(); diff --git a/briar-android/src/net/sf/briar/android/contact/ReadPrivateMessageActivity.java b/briar-android/src/net/sf/briar/android/contact/ReadPrivateMessageActivity.java index 677b12db52346307ddd5d0b80b83488c713e225d..64927e0c699353b72bb89fc8803e25851fb84e25 100644 --- a/briar-android/src/net/sf/briar/android/contact/ReadPrivateMessageActivity.java +++ b/briar-android/src/net/sf/briar/android/contact/ReadPrivateMessageActivity.java @@ -60,6 +60,7 @@ implements OnClickListener { @Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor; @Inject private volatile LifecycleManager lifecycleManager; private volatile MessageId messageId = null; + private volatile long timestamp = -1; @Override public void onCreate(Bundle state) { @@ -79,7 +80,7 @@ implements OnClickListener { messageId = new MessageId(b); String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE"); if(contentType == null) throw new IllegalStateException(); - long timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1); + timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1); if(timestamp == -1) throw new IllegalStateException(); if(state == null) { @@ -262,6 +263,7 @@ implements OnClickListener { Intent i = new Intent(this, WritePrivateMessageActivity.class); i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt()); i.putExtra("net.sf.briar.PARENT_ID", messageId.getBytes()); + i.putExtra("net.sf.briar.TIMESTAMP", timestamp); startActivity(i); setResult(RESULT_REPLY); finish(); diff --git a/briar-android/src/net/sf/briar/android/contact/WritePrivateMessageActivity.java b/briar-android/src/net/sf/briar/android/contact/WritePrivateMessageActivity.java index 51e6720860cd710d2459952189d2d7bec61763d0..38891115c5ae22182d9042442c83cad48071a3b0 100644 --- a/briar-android/src/net/sf/briar/android/contact/WritePrivateMessageActivity.java +++ b/briar-android/src/net/sf/briar/android/contact/WritePrivateMessageActivity.java @@ -66,6 +66,7 @@ implements OnItemSelectedListener, OnClickListener { private volatile LocalAuthor localAuthor = null; private volatile ContactId contactId = null; private volatile MessageId parentId = null; + private volatile long timestamp = -1; @Override public void onCreate(Bundle state) { @@ -76,6 +77,7 @@ implements OnItemSelectedListener, OnClickListener { if(id != -1) contactId = new ContactId(id); byte[] b = i.getByteArrayExtra("net.sf.briar.PARENT_ID"); if(b != null) parentId = new MessageId(b); + timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1); if(state != null) { id = state.getInt("net.sf.briar.CONTACT_ID", -1); @@ -262,8 +264,11 @@ implements OnItemSelectedListener, OnClickListener { public void run() { try { lifecycleManager.waitForDatabase(); + // Don't use an earlier timestamp than the parent + long time = System.currentTimeMillis(); + time = Math.max(time, timestamp + 1); Message m = messageFactory.createPrivateMessage(parentId, - "text/plain", body); + "text/plain", time, body); long now = System.currentTimeMillis(); db.addLocalPrivateMessage(m, contactId); long duration = System.currentTimeMillis() - now; diff --git a/briar-android/src/net/sf/briar/android/groups/ReadGroupPostActivity.java b/briar-android/src/net/sf/briar/android/groups/ReadGroupPostActivity.java index 3d9c94a130a7a6a1e1fa7bd1c718b9ad998dd4fa..34da4493e83b97f790f3bcfadab55c1817b31470 100644 --- a/briar-android/src/net/sf/briar/android/groups/ReadGroupPostActivity.java +++ b/briar-android/src/net/sf/briar/android/groups/ReadGroupPostActivity.java @@ -60,6 +60,7 @@ implements OnClickListener { @Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor; @Inject private volatile LifecycleManager lifecycleManager; private volatile MessageId messageId = null; + private volatile long timestamp = -1; @Override public void onCreate(Bundle state) { @@ -78,7 +79,7 @@ implements OnClickListener { String authorName = i.getStringExtra("net.sf.briar.AUTHOR_NAME"); String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE"); if(contentType == null) throw new IllegalStateException(); - long timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1); + timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1); if(timestamp == -1) throw new IllegalStateException(); if(state == null) { @@ -266,6 +267,7 @@ implements OnClickListener { Intent i = new Intent(this, WriteGroupPostActivity.class); i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes()); i.putExtra("net.sf.briar.PARENT_ID", messageId.getBytes()); + i.putExtra("net.sf.briar.TIMESTAMP", timestamp); startActivity(i); setResult(RESULT_REPLY); finish(); diff --git a/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java b/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java index 8a94109454a50e4766ef2ca5b3d63c80fe6c25b7..4ea20fef52b92b22d34d9a59d1acd47f06359805 100644 --- a/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java +++ b/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java @@ -74,6 +74,7 @@ implements OnItemSelectedListener, OnClickListener { private volatile LocalAuthor localAuthor = null; private volatile Group group = null; private volatile MessageId parentId = null; + private volatile long timestamp = -1; @Override public void onCreate(Bundle state) { @@ -84,6 +85,7 @@ implements OnItemSelectedListener, OnClickListener { if(b != null) groupId = new GroupId(b); b = i.getByteArrayExtra("net.sf.briar.PARENT_ID"); if(b != null) parentId = new MessageId(b); + timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1); if(state != null) { b = state.getByteArray("net.sf.briar.LOCAL_AUTHOR_ID"); @@ -318,15 +320,18 @@ implements OnItemSelectedListener, OnClickListener { // FIXME: This should happen on a CryptoExecutor thread private Message createMessage(byte[] body) throws IOException, GeneralSecurityException { + // Don't use an earlier timestamp than the parent + long time = System.currentTimeMillis(); + time = Math.max(time, timestamp + 1); if(localAuthor == null) { return messageFactory.createAnonymousMessage(parentId, group, - "text/plain", body); + "text/plain", time, body); } else { KeyParser keyParser = crypto.getSignatureKeyParser(); byte[] authorKeyBytes = localAuthor.getPrivateKey(); PrivateKey authorKey = keyParser.parsePrivateKey(authorKeyBytes); return messageFactory.createPseudonymousMessage(parentId, - group, localAuthor, authorKey, "text/plain", body); + group, localAuthor, authorKey, "text/plain", time, body); } } diff --git a/briar-api/src/net/sf/briar/api/messaging/MessageFactory.java b/briar-api/src/net/sf/briar/api/messaging/MessageFactory.java index 8ee5a07ca4aa8197e339fec733f6b42d332ec49f..6e4dc2dd2561c867c9f4852bb467e044139725d8 100644 --- a/briar-api/src/net/sf/briar/api/messaging/MessageFactory.java +++ b/briar-api/src/net/sf/briar/api/messaging/MessageFactory.java @@ -10,15 +10,17 @@ public interface MessageFactory { /** Creates a private message. */ Message createPrivateMessage(MessageId parent, String contentType, - byte[] body) throws IOException, GeneralSecurityException; + long timestamp, byte[] body) throws IOException, + GeneralSecurityException; /** Creates an anonymous group message. */ Message createAnonymousMessage(MessageId parent, Group group, - String contentType, byte[] body) throws IOException, + String contentType, long timestamp, byte[] body) throws IOException, GeneralSecurityException; /** Creates a pseudonymous group message. */ Message createPseudonymousMessage(MessageId parent, Group group, Author author, PrivateKey privateKey, String contentType, - byte[] body) throws IOException, GeneralSecurityException; + long timestamp, byte[] body) throws IOException, + GeneralSecurityException; } diff --git a/briar-core/src/net/sf/briar/messaging/MessageFactoryImpl.java b/briar-core/src/net/sf/briar/messaging/MessageFactoryImpl.java index 0640a1354d4d51f3eeadc0aa7f6b5208492cbacd..6f4ab5b11bf8c73c2a3ee9c267151f5b24c8351d 100644 --- a/briar-core/src/net/sf/briar/messaging/MessageFactoryImpl.java +++ b/briar-core/src/net/sf/briar/messaging/MessageFactoryImpl.java @@ -17,7 +17,6 @@ import java.security.SecureRandom; import javax.inject.Inject; import net.sf.briar.api.Author; -import net.sf.briar.api.clock.Clock; import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.MessageDigest; import net.sf.briar.api.crypto.PrivateKey; @@ -39,39 +38,40 @@ class MessageFactoryImpl implements MessageFactory { private final SecureRandom random; private final MessageDigest messageDigest; private final WriterFactory writerFactory; - private final Clock clock; @Inject - MessageFactoryImpl(CryptoComponent crypto, WriterFactory writerFactory, - Clock clock) { + MessageFactoryImpl(CryptoComponent crypto, WriterFactory writerFactory) { signature = crypto.getSignature(); random = crypto.getSecureRandom(); messageDigest = crypto.getMessageDigest(); this.writerFactory = writerFactory; - this.clock = clock; } public Message createPrivateMessage(MessageId parent, String contentType, - byte[] body) throws IOException, GeneralSecurityException { - return createMessage(parent, null, null, null, contentType, body); + long timestamp, byte[] body) throws IOException, + GeneralSecurityException { + return createMessage(parent, null, null, null, contentType, timestamp, + body); } public Message createAnonymousMessage(MessageId parent, Group group, - String contentType, byte[] body) throws IOException, + String contentType, long timestamp, byte[] body) throws IOException, GeneralSecurityException { - return createMessage(parent, group, null, null, contentType, body); + return createMessage(parent, group, null, null, contentType, timestamp, + body); } public Message createPseudonymousMessage(MessageId parent, Group group, Author author, PrivateKey privateKey, String contentType, - byte[] body) throws IOException, GeneralSecurityException { + long timestamp, byte[] body) throws IOException, + GeneralSecurityException { return createMessage(parent, group, author, privateKey, contentType, - body); + timestamp, body); } private Message createMessage(MessageId parent, Group group, Author author, - PrivateKey privateKey, String contentType, byte[] body) - throws IOException, GeneralSecurityException { + PrivateKey privateKey, String contentType, long timestamp, + byte[] body) throws IOException, GeneralSecurityException { // Validate the arguments if((author == null) != (privateKey == null)) throw new IllegalArgumentException(); @@ -102,7 +102,6 @@ class MessageFactoryImpl implements MessageFactory { if(author == null) w.writeNull(); else writeAuthor(w, author); w.writeString(contentType); - long timestamp = clock.currentTimeMillis(); w.writeIntAny(timestamp); byte[] salt = new byte[MESSAGE_SALT_LENGTH]; random.nextBytes(salt); diff --git a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java index 2d4cbcb9503765fc9c8e62376951369cb40c8a8e..1a4b1c6590d79122443860e81997d2f0549faa74 100644 --- a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java +++ b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java @@ -71,6 +71,7 @@ public class ProtocolIntegrationTest extends BriarTestCase { private final Message message, message1; private final String authorName = "Alice"; private final String contentType = "text/plain"; + private final long timestamp = System.currentTimeMillis(); private final String messageBody = "Hello world"; private final Collection<MessageId> messageIds; private final TransportId transportId; @@ -104,9 +105,9 @@ public class ProtocolIntegrationTest extends BriarTestCase { // Create two messages to the group: one anonymous, one pseudonymous MessageFactory messageFactory = i.getInstance(MessageFactory.class); message = messageFactory.createAnonymousMessage(null, group, - contentType, messageBody.getBytes("UTF-8")); + contentType, timestamp, messageBody.getBytes("UTF-8")); message1 = messageFactory.createPseudonymousMessage(null, group, - author, authorKeyPair.getPrivate(), contentType, + author, authorKeyPair.getPrivate(), contentType, timestamp, messageBody.getBytes("UTF-8")); messageIds = Arrays.asList(message.getId(), message1.getId()); // Create some transport properties diff --git a/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java b/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java index db00cdebd5c0ba34628bc595c946f61802e17b28..5534c73d95a3afd2193a196ea4ad9a602c1fa743 100644 --- a/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java +++ b/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java @@ -131,9 +131,10 @@ public class ConstantsTest extends BriarTestCase { PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate(); String contentType = TestUtils.createRandomString(MAX_CONTENT_TYPE_LENGTH); + long timestamp = Long.MAX_VALUE; byte[] body = new byte[MAX_BODY_LENGTH]; Message message = messageFactory.createPseudonymousMessage(parent, - group, author, privateKey, contentType, body); + group, author, privateKey, contentType, timestamp, body); // Check the size of the serialised message int length = message.getSerialised().length; assertTrue(length > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH diff --git a/briar-tests/src/net/sf/briar/messaging/simplex/SimplexMessagingIntegrationTest.java b/briar-tests/src/net/sf/briar/messaging/simplex/SimplexMessagingIntegrationTest.java index 5a0cd6067998a1156ff48d0d1dc5a8bb0e3be4d4..5d0d325fb8cee45b55602f012e642e66f48145cc 100644 --- a/briar-tests/src/net/sf/briar/messaging/simplex/SimplexMessagingIntegrationTest.java +++ b/briar-tests/src/net/sf/briar/messaging/simplex/SimplexMessagingIntegrationTest.java @@ -123,10 +123,11 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { km.endpointAdded(ep, LATENCY, initialSecret.clone()); // Send Bob a message String contentType = "text/plain"; + long timestamp = System.currentTimeMillis(); byte[] body = "Hi Bob!".getBytes("UTF-8"); MessageFactory messageFactory = alice.getInstance(MessageFactory.class); Message message = messageFactory.createPrivateMessage(null, contentType, - body); + timestamp, body); db.addLocalPrivateMessage(message, contactId); // Create an outgoing simplex connection ByteArrayOutputStream out = new ByteArrayOutputStream();