From 0224733b101c868e14c75e3d140df55308c54c2e Mon Sep 17 00:00:00 2001 From: ameba23 Date: Wed, 5 May 2021 14:35:30 +0200 Subject: [PATCH] RemoteWipeManager WIP --- .../api/remotewipe/RemoteWipeManager.java | 44 ++++ .../briar/remotewipe/MessageType.java | 29 ++ .../remotewipe/RemoteWipeManagerImpl.java | 247 ++++++++++++++++++ 3 files changed, 320 insertions(+) create mode 100644 briar-api/src/main/java/org/briarproject/briar/api/remotewipe/RemoteWipeManager.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/remotewipe/MessageType.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/remotewipe/RemoteWipeManagerImpl.java diff --git a/briar-api/src/main/java/org/briarproject/briar/api/remotewipe/RemoteWipeManager.java b/briar-api/src/main/java/org/briarproject/briar/api/remotewipe/RemoteWipeManager.java new file mode 100644 index 000000000..f9e7d36db --- /dev/null +++ b/briar-api/src/main/java/org/briarproject/briar/api/remotewipe/RemoteWipeManager.java @@ -0,0 +1,44 @@ +package org.briarproject.briar.api.remotewipe; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.contact.Contact; +import org.briarproject.bramble.api.contact.ContactId; +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.sync.ClientId; +import org.briarproject.briar.api.conversation.ConversationManager; +import org.briarproject.briar.api.conversation.ConversationMessageHeader; + +import java.util.Collection; +import java.util.List; + +@NotNullByDefault +public interface RemoteWipeManager extends ConversationManager.ConversationClient { + + /** + * The unique ID of the remote wipe client. + */ + ClientId CLIENT_ID = new ClientId("pw.darkcrystal.remotewipe"); + + /** + * The current major version of the remote wipe client. + */ + int MAJOR_VERSION = 0; + + /** + * The current minor version of the remote wipe client. + */ + int MINOR_VERSION = 0; + + void setup(Transaction txn, List wipers) + throws DbException, FormatException; + + void wipe(Transaction txn, Contact contact) + throws DbException, FormatException; + + @Override + Collection getMessageHeaders( + Transaction txn, ContactId contactId) throws DbException; + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/remotewipe/MessageType.java b/briar-core/src/main/java/org/briarproject/briar/remotewipe/MessageType.java new file mode 100644 index 000000000..dafc0c8e5 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/remotewipe/MessageType.java @@ -0,0 +1,29 @@ +package org.briarproject.briar.remotewipe; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +enum MessageType { + + SETUP(0), WIPE(1); + + private final int value; + + MessageType(int value) { + this.value = value; + } + + int getValue() { + return value; + } + + static MessageType fromValue(int value) throws + FormatException { + for (MessageType m : values()) if (m.value == value) return m; + throw new FormatException(); + } +} diff --git a/briar-core/src/main/java/org/briarproject/briar/remotewipe/RemoteWipeManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/remotewipe/RemoteWipeManagerImpl.java new file mode 100644 index 000000000..5fcbb5dc9 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/remotewipe/RemoteWipeManagerImpl.java @@ -0,0 +1,247 @@ +package org.briarproject.briar.remotewipe; + +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.data.BdfDictionary; +import org.briarproject.bramble.api.data.BdfEntry; +import org.briarproject.bramble.api.data.BdfList; +import org.briarproject.bramble.api.data.MetadataParser; +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.lifecycle.LifecycleManager; +import org.briarproject.bramble.api.sync.Group; +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.sync.MessageStatus; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.briar.api.attachment.AttachmentHeader; +import org.briarproject.briar.api.client.MessageTracker; +import org.briarproject.briar.api.conversation.ConversationMessageHeader; +import org.briarproject.briar.api.conversation.DeletionResult; +import org.briarproject.briar.api.remotewipe.RemoteWipeManager; +import org.briarproject.briar.api.socialbackup.ShardMessageHeader; +import org.briarproject.briar.client.ConversationClientImpl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; + +import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ; +import static org.briarproject.briar.remotewipe.MessageType.SETUP; +import static org.briarproject.briar.remotewipe.MessageType.WIPE; +import static org.briarproject.briar.socialbackup.SocialBackupConstants.GROUP_KEY_CONTACT_ID; +import static org.briarproject.briar.socialbackup.SocialBackupConstants.MSG_KEY_LOCAL; +import static org.briarproject.briar.socialbackup.SocialBackupConstants.MSG_KEY_MESSAGE_TYPE; +import static org.briarproject.briar.socialbackup.SocialBackupConstants.MSG_KEY_TIMESTAMP; + +public class RemoteWipeManagerImpl extends ConversationClientImpl + implements RemoteWipeManager, LifecycleManager.OpenDatabaseHook { + + private final Group localGroup; + private final Clock clock; + private final ContactGroupFactory contactGroupFactory; + private final ContactManager contactManager; + + @Inject + protected RemoteWipeManagerImpl( + DatabaseComponent db, + ClientHelper clientHelper, + MetadataParser metadataParser, + MessageTracker messageTracker, + Clock clock, + ContactManager contactManager, + ContactGroupFactory contactGroupFactory) { + super(db, clientHelper, metadataParser, messageTracker); + this.clock = clock; + this.contactGroupFactory = contactGroupFactory; + this.contactManager = contactManager; + localGroup = + contactGroupFactory.createLocalGroup(CLIENT_ID, MAJOR_VERSION); + } + + @Override + protected boolean incomingMessage(Transaction txn, Message m, BdfList body, + BdfDictionary meta) throws DbException, FormatException { + + MessageType type = MessageType.fromValue(body.getLong(0).intValue()); + if (type == SETUP) { + + + messageTracker.trackIncomingMessage(txn, m); +// message.getGroupId turn into contactid +// txn.attach event + } else if (type == WIPE) { + + ContactId contactId = getContactId(txn, m.getGroupId()); + // check if contact is in list of wipers + // if so, increment counter + // check if counter = threshold + } + return false; + } + + public void setup(Transaction txn, List wipers) + throws DbException, FormatException { + // TODO if (we already have a set of wipers?) do something + if (wipers.size() < 2) throw new FormatException(); + for (ContactId c : wipers) { + sendSetupMessage(txn, contactManager.getContact(c)); + } + + // TODO Make some sort of record of this + // clientHelper.mergeGroupMetadata(txn, localGroup.getId(), meta) + } + + private void sendSetupMessage(Transaction txn, Contact contact) + throws DbException, FormatException { + Group group = getContactGroup(contact); + GroupId g = group.getId(); + if (!db.containsGroup(txn, g)) db.addGroup(txn, group); + long timestamp = clock.currentTimeMillis(); + + byte[] body = "setup message".getBytes(); // TODO + + Message m = clientHelper.createMessage(g, timestamp, body); + // TODO remote-wipe versions of MESSAGE_KEY + BdfDictionary meta = BdfDictionary.of( + new BdfEntry(MSG_KEY_MESSAGE_TYPE, SETUP.getValue()), + new BdfEntry(MSG_KEY_LOCAL, true), + new BdfEntry(MSG_KEY_TIMESTAMP, timestamp) + ); + clientHelper.addLocalMessage(txn, m, meta, true, false); + messageTracker.trackOutgoingMessage(txn, m); + } + + + public void wipe(Transaction txn, Contact contact) + throws DbException, FormatException { + // TODO check that we have a SETUP message from contact + Group group = getContactGroup(contact); + GroupId g = group.getId(); + if (!db.containsGroup(txn, g)) db.addGroup(txn, group); + long timestamp = clock.currentTimeMillis(); + + byte[] body = "wipe message".getBytes(); // TODO + + Message m = clientHelper.createMessage(g, timestamp, body); + // TODO remote-wipe versions of MESSAGE_KEY + BdfDictionary meta = BdfDictionary.of( + new BdfEntry(MSG_KEY_MESSAGE_TYPE, SETUP.getValue()), + new BdfEntry(MSG_KEY_LOCAL, true), + new BdfEntry(MSG_KEY_TIMESTAMP, timestamp) + ); + clientHelper.addLocalMessage(txn, m, meta, true, false); + messageTracker.trackOutgoingMessage(txn, m); + } + + @Override + public void onDatabaseOpened(Transaction txn) throws DbException { + + } + + @Override + public Group getContactGroup(Contact c) { + return contactGroupFactory.createContactGroup(CLIENT_ID, + MAJOR_VERSION, c); + } + + @Override + public Collection getMessageHeaders( + Transaction txn, ContactId contactId) throws DbException { + try { + Contact contact = db.getContact(txn, contactId); + GroupId contactGroupId = getContactGroup(contact).getId(); + Map messages = clientHelper + .getMessageMetadataAsDictionary(txn, contactGroupId); + List headers = + new ArrayList<>(); + for (Map.Entry messageEntry : messages + .entrySet()) { + BdfDictionary meta = messageEntry.getValue(); + if (meta.getLong(MSG_KEY_MESSAGE_TYPE).intValue() == + SETUP.getValue()) { + Message message = clientHelper + .getMessage(txn, messageEntry.getKey()); + MessageStatus status = db.getMessageStatus(txn, contactId, + messageEntry.getKey()); + headers.add( + createSetupMessageHeader(message, meta, status)); + } + } + return headers; + } catch (FormatException e) { + throw new DbException(e); + } + } + + // TODO create a SetupMessageHeader + private ShardMessageHeader createSetupMessageHeader( + Message message, BdfDictionary meta, MessageStatus status + ) + throws FormatException { + + boolean isLocal = meta.getBoolean(MSG_KEY_LOCAL); + boolean read = meta.getBoolean(MSG_KEY_READ, false); + long timestamp; + if (isLocal) { + timestamp = meta.getLong(MSG_KEY_TIMESTAMP); + } else { + timestamp = message.getTimestamp(); + } + List attachmentHeaders = + new ArrayList<>(); + return new ShardMessageHeader( + message.getId(), message.getGroupId(), timestamp, + isLocal, read, status.isSent(), status.isSeen(), + attachmentHeaders); + } + + @Override + public Set getMessageIds(Transaction txn, ContactId contactId) + throws DbException { + Contact contact = db.getContact(txn, contactId); + GroupId contactGroupId = getContactGroup(contact).getId(); + try { + Map messages = clientHelper + .getMessageMetadataAsDictionary(txn, contactGroupId); + return messages.keySet(); + } catch (FormatException e) { + throw new DbException(e); + } + } + + @Override + public DeletionResult deleteAllMessages(Transaction txn, ContactId c) + throws DbException { + DeletionResult result = new DeletionResult(); + return result; + } + + @Override + public DeletionResult deleteMessages(Transaction txn, ContactId c, + Set messageIds) throws DbException { + DeletionResult result = new DeletionResult(); + return result; + } + + private ContactId getContactId(Transaction txn, GroupId g) + throws DbException { + try { + BdfDictionary meta = + clientHelper.getGroupMetadataAsDictionary(txn, g); + return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue()); + } catch (FormatException e) { + throw new DbException(e); + } + } +}