mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 12:19:54 +01:00
Resolve merge conflict with social-backup-poc
This commit is contained in:
@@ -4,6 +4,7 @@ import org.briarproject.briar.avatar.AvatarModule;
|
||||
import org.briarproject.briar.blog.BlogModule;
|
||||
import org.briarproject.briar.feed.FeedModule;
|
||||
import org.briarproject.briar.forum.ForumModule;
|
||||
import org.briarproject.briar.handshakekeyexchange.HandshakeKeyExchangeModule;
|
||||
import org.briarproject.briar.identity.IdentityModule;
|
||||
import org.briarproject.briar.introduction.IntroductionModule;
|
||||
import org.briarproject.briar.messaging.MessagingModule;
|
||||
@@ -12,7 +13,6 @@ import org.briarproject.briar.privategroup.invitation.GroupInvitationModule;
|
||||
import org.briarproject.briar.remotewipe.RemoteWipeModule;
|
||||
import org.briarproject.briar.sharing.SharingModule;
|
||||
import org.briarproject.briar.socialbackup.SocialBackupModule;
|
||||
//import org.briarproject.briar.socialbackup.DefaultSocialBackupModule;
|
||||
|
||||
public interface BriarCoreEagerSingletons {
|
||||
|
||||
@@ -40,6 +40,8 @@ public interface BriarCoreEagerSingletons {
|
||||
|
||||
void inject(RemoteWipeModule.EagerSingletons init);
|
||||
|
||||
void inject(HandshakeKeyExchangeModule.EagerSingletons init);
|
||||
|
||||
class Helper {
|
||||
|
||||
public static void injectEagerSingletons(BriarCoreEagerSingletons c) {
|
||||
@@ -55,6 +57,7 @@ public interface BriarCoreEagerSingletons {
|
||||
c.inject(new IntroductionModule.EagerSingletons());
|
||||
c.inject(new SocialBackupModule.EagerSingletons());
|
||||
c.inject(new RemoteWipeModule.EagerSingletons());
|
||||
c.inject(new HandshakeKeyExchangeModule.EagerSingletons());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.briarproject.briar.client.BriarClientModule;
|
||||
import org.briarproject.briar.feed.DnsModule;
|
||||
import org.briarproject.briar.feed.FeedModule;
|
||||
import org.briarproject.briar.forum.ForumModule;
|
||||
import org.briarproject.briar.handshakekeyexchange.HandshakeKeyExchangeModule;
|
||||
import org.briarproject.briar.identity.IdentityModule;
|
||||
import org.briarproject.briar.introduction.IntroductionModule;
|
||||
import org.briarproject.briar.messaging.MessagingModule;
|
||||
@@ -35,6 +36,7 @@ import dagger.Module;
|
||||
RemoteWipeModule.class,
|
||||
SharingModule.class,
|
||||
SocialBackupModule.class,
|
||||
HandshakeKeyExchangeModule.class,
|
||||
TestModule.class
|
||||
})
|
||||
public class BriarCoreModule {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.briarproject.briar.handshakekeyexchange;
|
||||
|
||||
public interface HandshakeKeyExchangeConstants {
|
||||
|
||||
// Group metadata keys
|
||||
String GROUP_KEY_CONTACT_ID = "contactId";
|
||||
|
||||
// Message metadata keys
|
||||
String MSG_KEY_TIMESTAMP = "timestamp";
|
||||
String MSG_KEY_MESSAGE_TYPE = "messageType";
|
||||
String MSG_KEY_LOCAL = "local";
|
||||
String MSG_KEY_VERSION = "version";
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
package org.briarproject.briar.handshakekeyexchange;
|
||||
|
||||
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.AgreementPublicKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
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.identity.IdentityManager;
|
||||
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.system.Clock;
|
||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||
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.handshakekeyexchange.HandshakeKeyExchangeManager;
|
||||
import org.briarproject.briar.client.ConversationClientImpl;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.briar.handshakekeyexchange.HandshakeKeyExchangeConstants.GROUP_KEY_CONTACT_ID;
|
||||
import static org.briarproject.briar.handshakekeyexchange.HandshakeKeyExchangeConstants.MSG_KEY_LOCAL;
|
||||
import static org.briarproject.briar.handshakekeyexchange.HandshakeKeyExchangeConstants.MSG_KEY_TIMESTAMP;
|
||||
|
||||
public class HandshakeKeyExchangeManagerImpl extends ConversationClientImpl
|
||||
implements
|
||||
HandshakeKeyExchangeManager, LifecycleManager.OpenDatabaseHook,
|
||||
ContactManager.ContactHook,
|
||||
ClientVersioningManager.ClientVersioningHook {
|
||||
|
||||
private final ClientVersioningManager clientVersioningManager;
|
||||
private final ContactGroupFactory contactGroupFactory;
|
||||
private final ContactManager contactManager;
|
||||
private final IdentityManager identityManager;
|
||||
private final Group localGroup;
|
||||
private final Clock clock;
|
||||
private PublicKey handshakePublicKey;
|
||||
private static final Logger LOG =
|
||||
getLogger(HandshakeKeyExchangeManager.class.getName());
|
||||
|
||||
|
||||
@Inject
|
||||
protected HandshakeKeyExchangeManagerImpl(
|
||||
DatabaseComponent db,
|
||||
ClientHelper clientHelper,
|
||||
MetadataParser metadataParser,
|
||||
MessageTracker messageTracker,
|
||||
ClientVersioningManager clientVersioningManager,
|
||||
ContactGroupFactory contactGroupFactory,
|
||||
ContactManager contactManager,
|
||||
IdentityManager identityManager,
|
||||
Clock clock
|
||||
) {
|
||||
super(db, clientHelper, metadataParser, messageTracker);
|
||||
this.clientVersioningManager = clientVersioningManager;
|
||||
this.contactGroupFactory = contactGroupFactory;
|
||||
this.contactManager = contactManager;
|
||||
this.identityManager = identityManager;
|
||||
this.clock = clock;
|
||||
localGroup =
|
||||
contactGroupFactory.createLocalGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDatabaseOpened(Transaction txn) throws DbException {
|
||||
// Get our own handshake public key
|
||||
handshakePublicKey = identityManager.getHandshakeKeys(txn).getPublic();
|
||||
|
||||
if (!db.containsGroup(txn, localGroup.getId())) {
|
||||
db.addGroup(txn, localGroup);
|
||||
|
||||
// Set things up for any pre-existing contacts
|
||||
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
|
||||
} else {
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
if (c.getHandshakePublicKey() == null) {
|
||||
sendHandshakePublicKey(txn, c);
|
||||
} else {
|
||||
LOG.info("Have pk for contact " + c.getAuthor().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getContactGroup(Contact c) {
|
||||
return contactGroupFactory.createContactGroup(CLIENT_ID,
|
||||
MAJOR_VERSION, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConversationMessageHeader> getMessageHeaders(
|
||||
Transaction txn, ContactId contactId) throws DbException {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<MessageId> getMessageIds(Transaction txn, ContactId contactId)
|
||||
throws DbException {
|
||||
Contact contact = db.getContact(txn, contactId);
|
||||
GroupId contactGroupId = getContactGroup(contact).getId();
|
||||
try {
|
||||
Map<MessageId, BdfDictionary> 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 {
|
||||
GroupId g = getContactGroup(db.getContact(txn, c)).getId();
|
||||
for (MessageId messageId : db.getMessageIds(txn, g)) {
|
||||
db.deleteMessage(txn, messageId);
|
||||
db.deleteMessageMetadata(txn, messageId);
|
||||
}
|
||||
messageTracker.initializeGroupCount(txn, g);
|
||||
return new DeletionResult();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DeletionResult deleteMessages(Transaction txn, ContactId c,
|
||||
Set<MessageId> messageIds) throws DbException {
|
||||
for (MessageId m : messageIds) {
|
||||
db.deleteMessage(txn, m);
|
||||
db.deleteMessageMetadata(txn, m);
|
||||
}
|
||||
return new DeletionResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean incomingMessage(Transaction txn, Message m, BdfList body,
|
||||
BdfDictionary meta) throws DbException, FormatException {
|
||||
LOG.info("Incoming HandshakeKeyExchange message");
|
||||
ContactId contactId = getContactId(txn, m.getGroupId());
|
||||
Contact c = contactManager.getContact(txn, contactId);
|
||||
if (c.getHandshakePublicKey() != null) {
|
||||
LOG.info("Already have public key - ignoring message");
|
||||
return false;
|
||||
}
|
||||
LOG.info("Adding contact's handshake public key");
|
||||
PublicKey handshakePublicKey = new AgreementPublicKey(body.getRaw(0));
|
||||
|
||||
try {
|
||||
contactManager
|
||||
.setHandshakePublicKey(txn, contactId, handshakePublicKey);
|
||||
} catch (GeneralSecurityException e) {
|
||||
LOG.warning("Security exception when adding remote handshake public key");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private ContactId getContactId(Transaction txn, GroupId g)
|
||||
throws DbException {
|
||||
try {
|
||||
BdfDictionary meta =
|
||||
clientHelper.getGroupMetadataAsDictionary(txn, g);
|
||||
return new ContactId(meta.getLong(
|
||||
HandshakeKeyExchangeConstants.GROUP_KEY_CONTACT_ID)
|
||||
.intValue());
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addingContact(Transaction txn, Contact c) throws DbException {
|
||||
// Create a group to share with the contact
|
||||
Group g = getContactGroup(c);
|
||||
db.addGroup(txn, g);
|
||||
// Apply the client's visibility to the contact group
|
||||
Group.Visibility client =
|
||||
clientVersioningManager.getClientVisibility(txn,
|
||||
c.getId(), CLIENT_ID, MAJOR_VERSION);
|
||||
db.setGroupVisibility(txn, c.getId(), g.getId(), client);
|
||||
// Attach the contact ID to the group
|
||||
setContactId(txn, g.getId(), c.getId());
|
||||
|
||||
if (c.getHandshakePublicKey() == null) {
|
||||
sendHandshakePublicKey(txn, c);
|
||||
} else {
|
||||
LOG.info("Have pk for contact " + c.getAuthor().getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void sendHandshakePublicKey(Transaction txn, Contact c)
|
||||
throws DbException {
|
||||
LOG.info("Sending our handshake public key to " + c.getAuthor().getName());
|
||||
Group group = getContactGroup(c);
|
||||
GroupId g = group.getId();
|
||||
if (!db.containsGroup(txn, g)) db.addGroup(txn, group);
|
||||
long timestamp = clock.currentTimeMillis();
|
||||
|
||||
BdfList bodyList = new BdfList();
|
||||
bodyList.add(handshakePublicKey);
|
||||
try {
|
||||
byte[] body = clientHelper.toByteArray(bodyList);
|
||||
Message m = clientHelper.createMessage(g, timestamp, body);
|
||||
|
||||
BdfDictionary meta = BdfDictionary.of(
|
||||
new BdfEntry(MSG_KEY_LOCAL, true),
|
||||
new BdfEntry(MSG_KEY_TIMESTAMP, timestamp)
|
||||
);
|
||||
clientHelper.addLocalMessage(txn, m, meta, true, false);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException();
|
||||
}
|
||||
|
||||
// messageTracker.trackOutgoingMessage(txn, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removingContact(Transaction txn, Contact c) throws DbException {
|
||||
db.removeGroup(txn, getContactGroup(c));
|
||||
}
|
||||
|
||||
private void setContactId(Transaction txn, GroupId g, ContactId c)
|
||||
throws DbException {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put(GROUP_KEY_CONTACT_ID, c.getInt());
|
||||
try {
|
||||
clientHelper.mergeGroupMetadata(txn, g, d);
|
||||
} catch (FormatException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClientVisibilityChanging(Transaction txn, Contact c,
|
||||
Group.Visibility v) throws DbException {
|
||||
// Apply the client's visibility to the contact group
|
||||
Group g = getContactGroup(c);
|
||||
db.setGroupVisibility(txn, c.getId(), g.getId(), v);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.briarproject.briar.handshakekeyexchange;
|
||||
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.sync.validation.ValidationManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||
import org.briarproject.briar.api.conversation.ConversationManager;
|
||||
import org.briarproject.briar.api.handshakekeyexchange.HandshakeKeyExchangeManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class HandshakeKeyExchangeModule {
|
||||
public static class EagerSingletons {
|
||||
@Inject
|
||||
HandshakeKeyExchangeManager handshakeKeyExchangeManager;
|
||||
|
||||
@Inject
|
||||
HandshakeKeyExchangeValidator handshakeKeyExchangeValidator;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
HandshakeKeyExchangeManager handshakeKeyExchangeManager(
|
||||
LifecycleManager lifecycleManager,
|
||||
ValidationManager validationManager,
|
||||
ContactManager contactManager,
|
||||
ClientVersioningManager clientVersioningManager,
|
||||
ConversationManager conversationManager,
|
||||
HandshakeKeyExchangeManagerImpl handshakeKeyExchangeManager) {
|
||||
|
||||
lifecycleManager.registerOpenDatabaseHook(handshakeKeyExchangeManager);
|
||||
validationManager
|
||||
.registerIncomingMessageHook(HandshakeKeyExchangeManager.CLIENT_ID,
|
||||
HandshakeKeyExchangeManager.MAJOR_VERSION, handshakeKeyExchangeManager);
|
||||
|
||||
contactManager.registerContactHook(handshakeKeyExchangeManager);
|
||||
clientVersioningManager.registerClient(HandshakeKeyExchangeManager.CLIENT_ID, HandshakeKeyExchangeManager.MAJOR_VERSION,
|
||||
HandshakeKeyExchangeManager.MINOR_VERSION, handshakeKeyExchangeManager);
|
||||
conversationManager.registerConversationClient(handshakeKeyExchangeManager);
|
||||
return handshakeKeyExchangeManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
HandshakeKeyExchangeValidator handshakeKeyExchangeValidator(
|
||||
ValidationManager validationManager,
|
||||
ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder,
|
||||
Clock clock) {
|
||||
HandshakeKeyExchangeValidator validator =
|
||||
new HandshakeKeyExchangeValidator(clientHelper, metadataEncoder, clock);
|
||||
validationManager.registerMessageValidator(HandshakeKeyExchangeManager.CLIENT_ID, HandshakeKeyExchangeManager.MAJOR_VERSION,
|
||||
validator);
|
||||
return validator;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.briarproject.briar.handshakekeyexchange;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.client.BdfMessageContext;
|
||||
import org.briarproject.bramble.api.client.BdfMessageValidator;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
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.MetadataEncoder;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.api.sync.InvalidMessageException;
|
||||
import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import static org.briarproject.briar.handshakekeyexchange.HandshakeKeyExchangeConstants.MSG_KEY_LOCAL;
|
||||
|
||||
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class HandshakeKeyExchangeValidator extends BdfMessageValidator {
|
||||
|
||||
@Inject
|
||||
protected HandshakeKeyExchangeValidator(
|
||||
ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder,
|
||||
Clock clock) {
|
||||
super(clientHelper, metadataEncoder, clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BdfMessageContext validateMessage(Message m, Group g,
|
||||
BdfList body) throws InvalidMessageException, FormatException {
|
||||
checkSize(body,1);
|
||||
BdfDictionary meta = BdfDictionary.of(
|
||||
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||
);
|
||||
return new BdfMessageContext(meta);
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,11 @@ import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.bramble.api.identity.Identity;
|
||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.briar.api.socialbackup.BackupPayload;
|
||||
import org.briarproject.briar.api.socialbackup.ContactData;
|
||||
import org.briarproject.briar.api.socialbackup.MessageParser;
|
||||
import org.briarproject.briar.api.socialbackup.Shard;
|
||||
import org.briarproject.briar.api.socialbackup.SocialBackup;
|
||||
@@ -55,7 +57,7 @@ public class BackupPayloadDecoderImpl implements BackupPayloadDecoder {
|
||||
this.messageParser = messageParser;
|
||||
}
|
||||
|
||||
public org.briarproject.briar.api.socialbackup.SocialBackup decodeBackupPayload(
|
||||
public SocialBackup decodeBackupPayload(
|
||||
SecretKey secret,
|
||||
BackupPayload backupPayload)
|
||||
throws FormatException, GeneralSecurityException {
|
||||
@@ -98,13 +100,17 @@ public class BackupPayloadDecoderImpl implements BackupPayloadDecoder {
|
||||
PrivateKey handShakePrivateKey =
|
||||
new AgreementPrivateKey(bdfIdentity.getRaw(3));
|
||||
|
||||
Map<TransportId, TransportProperties> localProperties = clientHelper
|
||||
.parseAndValidateTransportPropertiesMap(
|
||||
bdfIdentity.getDictionary(4));
|
||||
LOG.info("Local transport properties parsed");
|
||||
|
||||
Long created = System.currentTimeMillis();
|
||||
|
||||
Identity identity = new Identity(localAuthor, handshakePublicKey,
|
||||
handShakePrivateKey, created);
|
||||
LOG.info("New identity created");
|
||||
|
||||
List<org.briarproject.briar.api.socialbackup.ContactData> contactDataList = new ArrayList();
|
||||
List<ContactData> contactDataList = new ArrayList();
|
||||
|
||||
for (int i = 0; i < bdfContactData.size(); i++) {
|
||||
BdfList bdfData = bdfContactData.getList(i);
|
||||
@@ -143,9 +149,9 @@ public class BackupPayloadDecoderImpl implements BackupPayloadDecoder {
|
||||
org.briarproject.briar.api.socialbackup.ContactData contactData =
|
||||
new org.briarproject.briar.api.socialbackup.ContactData(contact, properties, shard);
|
||||
contactDataList.add(contactData);
|
||||
LOG.info("Contact added");
|
||||
LOG.info("Contact fully parsed");
|
||||
}
|
||||
LOG.info("All contacts added");
|
||||
return new SocialBackup(identity, contactDataList, version);
|
||||
LOG.info("All contacts fully parsed");
|
||||
return new SocialBackup(identity, contactDataList, localProperties, version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,17 @@ package org.briarproject.briar.socialbackup;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.identity.Identity;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.briar.api.socialbackup.BackupPayload;
|
||||
import org.briarproject.briar.api.socialbackup.ContactData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@NotNullByDefault
|
||||
interface BackupPayloadEncoder {
|
||||
|
||||
BackupPayload encodeBackupPayload(SecretKey secret, Identity identity,
|
||||
List<ContactData> contactData, int version);
|
||||
List<ContactData> contactData, int version, Map<TransportId, TransportProperties> localTransportProperties);
|
||||
}
|
||||
|
||||
@@ -10,11 +10,15 @@ import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.identity.Identity;
|
||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.briar.api.socialbackup.BackupPayload;
|
||||
import org.briarproject.briar.api.socialbackup.Shard;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
@@ -45,15 +49,26 @@ class BackupPayloadEncoderImpl implements BackupPayloadEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.briarproject.briar.api.socialbackup.BackupPayload encodeBackupPayload(SecretKey secret,
|
||||
Identity identity, List<org.briarproject.briar.api.socialbackup.ContactData> contactData, int version) {
|
||||
public BackupPayload encodeBackupPayload(SecretKey secret,
|
||||
Identity identity,
|
||||
List<org.briarproject.briar.api.socialbackup.ContactData> contactData,
|
||||
int version,
|
||||
Map<TransportId, TransportProperties> localTransportProperties) {
|
||||
// Encode the local identity
|
||||
BdfList bdfIdentity = new BdfList();
|
||||
LocalAuthor localAuthor = identity.getLocalAuthor();
|
||||
bdfIdentity.add(clientHelper.toList(localAuthor));
|
||||
bdfIdentity.add(localAuthor.getPrivateKey().getEncoded());
|
||||
|
||||
// Add handshake keypair
|
||||
assert identity.getHandshakePublicKey() != null;
|
||||
bdfIdentity.add(identity.getHandshakePublicKey().getEncoded());
|
||||
assert identity.getHandshakePrivateKey() != null;
|
||||
bdfIdentity.add(identity.getHandshakePrivateKey().getEncoded());
|
||||
|
||||
// Add local transport properties
|
||||
bdfIdentity.add(clientHelper.toDictionary(localTransportProperties));
|
||||
|
||||
// Encode the contact data
|
||||
BdfList bdfContactData = new BdfList();
|
||||
for (org.briarproject.briar.api.socialbackup.ContactData cd : contactData) {
|
||||
@@ -84,10 +99,13 @@ class BackupPayloadEncoderImpl implements BackupPayloadEncoder {
|
||||
int encrypted = cipher.process(plaintext, 0, plaintext.length,
|
||||
ciphertext, 0);
|
||||
if (encrypted != ciphertext.length) throw new AssertionError();
|
||||
byte[] ciphertextWithNonce = new byte[ciphertext.length + nonce.length];
|
||||
byte[] ciphertextWithNonce =
|
||||
new byte[ciphertext.length + nonce.length];
|
||||
System.arraycopy(nonce, 0, ciphertextWithNonce, 0, nonce.length);
|
||||
System.arraycopy(ciphertext, 0, ciphertextWithNonce, nonce.length, ciphertext.length);
|
||||
return new org.briarproject.briar.api.socialbackup.BackupPayload(ciphertextWithNonce);
|
||||
System.arraycopy(ciphertext, 0, ciphertextWithNonce, nonce.length,
|
||||
ciphertext.length);
|
||||
return new org.briarproject.briar.api.socialbackup.BackupPayload(
|
||||
ciphertextWithNonce);
|
||||
} catch (FormatException | GeneralSecurityException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ import org.briarproject.briar.client.ConversationClientImpl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
@@ -66,7 +67,6 @@ import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
|
||||
import static org.briarproject.briar.socialbackup.MessageType.BACKUP;
|
||||
@@ -440,14 +440,25 @@ class SocialBackupManagerImpl extends ConversationClientImpl
|
||||
int version)
|
||||
throws DbException {
|
||||
Identity identity = identityManager.getIdentity(txn);
|
||||
|
||||
// Add local transport properties
|
||||
Map<TransportId, TransportProperties> localProps =
|
||||
transportPropertyManager.getLocalProperties(txn);
|
||||
Map<TransportId, TransportProperties> filteredLocalProps =
|
||||
new HashMap<>();
|
||||
if (localProps.get(TorConstants.ID) != null) {
|
||||
filteredLocalProps
|
||||
.put(TorConstants.ID, localProps.get(TorConstants.ID));
|
||||
}
|
||||
|
||||
return backupPayloadEncoder.encodeBackupPayload(secret, identity,
|
||||
contactData, version);
|
||||
contactData, version, filteredLocalProps);
|
||||
}
|
||||
|
||||
private List<ContactData> loadContactData(Transaction txn)
|
||||
throws DbException {
|
||||
Collection<Contact> contacts = contactManager.getContacts(txn);
|
||||
List<org.briarproject.briar.api.socialbackup.ContactData> contactData =
|
||||
List<ContactData> contactData =
|
||||
new ArrayList<>();
|
||||
for (Contact c : contacts) {
|
||||
// Skip contacts that are in the process of being removed
|
||||
@@ -456,6 +467,10 @@ class SocialBackupManagerImpl extends ConversationClientImpl
|
||||
Map<TransportId, TransportProperties> props =
|
||||
getTransportProperties(txn, c.getId());
|
||||
Shard shard = getRemoteShard(txn, contactGroup.getId());
|
||||
if (c.getHandshakePublicKey() == null) {
|
||||
System.out.println(
|
||||
"Warning - adding contact with no handshake public key");
|
||||
}
|
||||
contactData
|
||||
.add(new org.briarproject.briar.api.socialbackup.ContactData(
|
||||
c, props, shard));
|
||||
@@ -466,9 +481,14 @@ class SocialBackupManagerImpl extends ConversationClientImpl
|
||||
private Map<TransportId, TransportProperties> getTransportProperties(
|
||||
Transaction txn, ContactId c) throws DbException {
|
||||
// TODO: Include filtered properties for other transports
|
||||
TransportProperties p = transportPropertyManager
|
||||
.getRemoteProperties(txn, c, TorConstants.ID);
|
||||
return singletonMap(TorConstants.ID, p);
|
||||
TransportId ids[] = {TorConstants.ID};
|
||||
// {TorConstants.ID, LanTcpConstants.ID, BluetoothConstants.ID};
|
||||
Map<TransportId, TransportProperties> props = new HashMap();
|
||||
for (TransportId id : ids) {
|
||||
props.put(id, transportPropertyManager
|
||||
.getRemoteProperties(txn, c, id));
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
private void sendShardMessage(Transaction txn, Contact custodian,
|
||||
@@ -562,7 +582,7 @@ class SocialBackupManagerImpl extends ConversationClientImpl
|
||||
db.deleteMessageMetadata(txn, prevId);
|
||||
}
|
||||
sendBackupMessage(txn, custodian, newVersion, payload);
|
||||
} catch (NoSuchContactException|NoSuchGroupException e){
|
||||
} catch (NoSuchContactException | NoSuchGroupException e) {
|
||||
// The custodian is no longer a contact - continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.briarproject.briar.socialbackup.recovery;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
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.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
@@ -9,9 +11,15 @@ import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.identity.AuthorId;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.api.socialbackup.BackupPayload;
|
||||
import org.briarproject.briar.api.socialbackup.ContactData;
|
||||
import org.briarproject.briar.api.socialbackup.DarkCrystal;
|
||||
import org.briarproject.briar.api.socialbackup.MessageEncoder;
|
||||
import org.briarproject.briar.api.socialbackup.MessageParser;
|
||||
import org.briarproject.briar.api.socialbackup.ReturnShardPayload;
|
||||
import org.briarproject.briar.api.socialbackup.Shard;
|
||||
import org.briarproject.briar.api.socialbackup.SocialBackup;
|
||||
@@ -21,6 +29,9 @@ import org.briarproject.briar.socialbackup.BackupPayloadDecoder;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -29,14 +40,18 @@ import javax.inject.Inject;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
public class RestoreAccountImpl implements RestoreAccount {
|
||||
private final ArrayList<ReturnShardPayload> recoveredShards = new ArrayList<>();
|
||||
private final Set<ReturnShardPayload> recoveredShards = new HashSet<>();
|
||||
private final DarkCrystal darkCrystal;
|
||||
private final Executor ioExecutor;
|
||||
private final DatabaseComponent db;
|
||||
private final ContactManager contactManager;
|
||||
private final MessageEncoder messageEncoder;
|
||||
private final MessageParser messageParser;
|
||||
private final TransportPropertyManager transportPropertyManager;
|
||||
private final LifecycleManager lifecycleManager;
|
||||
private SecretKey secretKey;
|
||||
private final BackupPayloadDecoder backupPayloadDecoder;
|
||||
private final ClientHelper clientHelper;
|
||||
private SocialBackup socialBackup;
|
||||
private byte[] secretId;
|
||||
|
||||
@@ -48,20 +63,29 @@ public class RestoreAccountImpl implements RestoreAccount {
|
||||
BackupPayloadDecoder backupPayloadDecoder, DatabaseComponent db,
|
||||
@IoExecutor Executor ioExecutor,
|
||||
ContactManager contactManager,
|
||||
LifecycleManager lifecycleManager) {
|
||||
LifecycleManager lifecycleManager,
|
||||
TransportPropertyManager transportPropertyManager,
|
||||
MessageEncoder messageEncoder,
|
||||
MessageParser messageParser,
|
||||
ClientHelper clientHelper) {
|
||||
this.darkCrystal = darkCrystal;
|
||||
this.backupPayloadDecoder = backupPayloadDecoder;
|
||||
this.db = db;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.lifecycleManager = lifecycleManager;
|
||||
this.contactManager = contactManager;
|
||||
this.transportPropertyManager = transportPropertyManager;
|
||||
this.messageEncoder = messageEncoder;
|
||||
this.messageParser = messageParser;
|
||||
this.clientHelper = clientHelper;
|
||||
}
|
||||
|
||||
public int getNumberOfShards() {
|
||||
return recoveredShards.size();
|
||||
}
|
||||
|
||||
public AddReturnShardPayloadResult addReturnShardPayload(ReturnShardPayload toAdd) {
|
||||
public AddReturnShardPayloadResult addReturnShardPayload(
|
||||
ReturnShardPayload toAdd) {
|
||||
AddReturnShardPayloadResult result = AddReturnShardPayloadResult.OK;
|
||||
// TODO figure out how to actually use a hash set for these objects
|
||||
for (ReturnShardPayload returnShardPayload : recoveredShards) {
|
||||
@@ -75,7 +99,8 @@ public class RestoreAccountImpl implements RestoreAccount {
|
||||
return AddReturnShardPayloadResult.MISMATCH;
|
||||
}
|
||||
recoveredShards.add(toAdd);
|
||||
return AddReturnShardPayloadResult.OK;
|
||||
return canRecover() ? AddReturnShardPayloadResult.RECOVERED :
|
||||
AddReturnShardPayloadResult.OK;
|
||||
}
|
||||
|
||||
public boolean canRecover() {
|
||||
@@ -115,10 +140,7 @@ public class RestoreAccountImpl implements RestoreAccount {
|
||||
return socialBackup;
|
||||
}
|
||||
|
||||
public void addContactsToDb() throws DbException {
|
||||
if (socialBackup == null) throw new DbException();
|
||||
AuthorId localAuthorId = socialBackup.getIdentity().getId();
|
||||
|
||||
public void restoreAccountWhenDatabaseReady() throws DbException {
|
||||
ioExecutor.execute(() -> {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
@@ -126,19 +148,80 @@ public class RestoreAccountImpl implements RestoreAccount {
|
||||
LOG.warning("Interrupted when waiting for database");
|
||||
}
|
||||
try {
|
||||
db.transaction(false, txn -> {
|
||||
for (ContactData contactData : socialBackup.getContacts()) {
|
||||
Contact c = contactData.getContact();
|
||||
LOG.info("Adding contact " + c.getAuthor().getName() + " " + c.getAlias());
|
||||
contactManager.addContact(txn, c.getAuthor(), localAuthorId,
|
||||
c.getHandshakePublicKey(), c.isVerified());
|
||||
}
|
||||
});
|
||||
addLocalTransportProperties();
|
||||
addContactsToDb();
|
||||
} catch (DbException e) {
|
||||
LOG.warning("Error adding contacts to database");
|
||||
LOG.warning(e.getMessage());
|
||||
LOG.warning("Error when processing backup");
|
||||
e.printStackTrace();
|
||||
}
|
||||
LOG.info("Added all contacts");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void addContactsToDb() throws DbException {
|
||||
if (socialBackup == null) throw new DbException();
|
||||
AuthorId localAuthorId = socialBackup.getIdentity().getId();
|
||||
|
||||
try {
|
||||
db.transaction(false, txn -> {
|
||||
for (ContactData contactData : socialBackup.getContacts()) {
|
||||
Contact c = contactData.getContact();
|
||||
LOG.info("Adding contact " + c.getAuthor().getName() +
|
||||
" " + c.getAlias());
|
||||
if (c.getHandshakePublicKey() == null) {
|
||||
LOG.warning(
|
||||
"Warning: contact has no handshake public key");
|
||||
}
|
||||
ContactId contactId = contactManager
|
||||
.addContact(txn, c.getAuthor(), localAuthorId,
|
||||
c.getHandshakePublicKey(),
|
||||
c.isVerified());
|
||||
transportPropertyManager
|
||||
.addRemoteProperties(txn, contactId,
|
||||
contactData.getProperties());
|
||||
}
|
||||
});
|
||||
} catch (DbException e) {
|
||||
LOG.warning("Error adding contacts to database");
|
||||
LOG.warning(e.getMessage());
|
||||
} catch (GeneralSecurityException e) {
|
||||
LOG.warning("Error adding handshake key");
|
||||
LOG.warning(e.getMessage());
|
||||
}
|
||||
LOG.info("Added all contacts");
|
||||
}
|
||||
|
||||
private void addLocalTransportProperties()
|
||||
throws DbException {
|
||||
LOG.info("Adding local transport properties");
|
||||
for (Map.Entry<TransportId, TransportProperties> propertiesEntry : socialBackup
|
||||
.getLocalTransportProperties().entrySet()) {
|
||||
LOG.info("Adding transport property " +
|
||||
propertiesEntry.getKey().getString());
|
||||
transportPropertyManager
|
||||
.mergeLocalProperties(propertiesEntry.getKey(),
|
||||
propertiesEntry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getEncodedShards() {
|
||||
Set<String> s = new HashSet();
|
||||
for (ReturnShardPayload r : recoveredShards) {
|
||||
s.add(StringUtils
|
||||
.toHexString(messageEncoder.encodeReturnShardPayload(r)));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public void restoreFromPrevious(Set<String> previousShards) {
|
||||
for (String s : previousShards) {
|
||||
try {
|
||||
addReturnShardPayload(messageParser.parseReturnShardPayload(
|
||||
clientHelper.toList(StringUtils.fromHexString(s))));
|
||||
} catch (FormatException e) {
|
||||
LOG.warning("Error parsing shard from previous session");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user