Resolve merge conflict with social-backup-poc

This commit is contained in:
ameba23
2021-10-06 11:32:11 +02:00
33 changed files with 768 additions and 83 deletions

View File

@@ -50,7 +50,8 @@ public interface ContactManager {
boolean active) throws DbException;
ContactId addContact(Transaction txn, Author remote, AuthorId local,
PublicKey handshake, boolean verified) throws DbException;
PublicKey handshake, boolean verified)
throws DbException, GeneralSecurityException;
/**
* Stores a contact associated with the given local and remote pseudonyms,
@@ -209,6 +210,19 @@ public interface ContactManager {
void setContactAlias(ContactId c, @Nullable String alias)
throws DbException;
/**
* Sets the contact's handshake public key
*/
void setHandshakePublicKey(Transaction txn, ContactId c,
PublicKey handshakePublicKey) throws DbException,
GeneralSecurityException;
/**
* Sets the contact's handshake public key
*/
void setHandshakePublicKey(ContactId c, PublicKey handshakePublicKey)
throws DbException, GeneralSecurityException;
/**
* Returns true if a contact with this {@code remoteAuthorId} belongs to
* the local pseudonym with this {@code localAuthorId}.

View File

@@ -546,6 +546,11 @@ public interface DatabaseComponent extends TransactionManager {
void setContactAlias(Transaction txn, ContactId c, @Nullable String alias)
throws DbException;
/**
* Sets the remote handshake public key for a given contact
*/
void setHandshakePublicKey(Transaction txn, ContactId c, PublicKey handshakePublicKey) throws DbException;
/**
* Sets the given group's visibility to the given contact.
*/

View File

@@ -33,11 +33,11 @@ dependencies {
}
animalsniffer {
// Allow requireNonNull: Android desugaring rewrites it (so it's safe for us to use),
// and it gets used when passing method references instead of lambdas with Java 11.
// Note that this line allows *all* methods from java.util.Objects.
// That's the best that we can do with the configuration options that Animal Sniffer offers.
ignore 'java.util.Objects'
// Allow requireNonNull: Android desugaring rewrites it (so it's safe for us to use),
// and it gets used when passing method references instead of lambdas with Java 11.
// Note that this line allows *all* methods from java.util.Objects.
// That's the best that we can do with the configuration options that Animal Sniffer offers.
ignore 'java.util.Objects'
}
// needed to make test output available to bramble-java

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
@@ -50,7 +49,8 @@ class IncomingDuplexSyncConnection extends DuplexSyncConnection
@Override
public void run() {
LOG.info("Running IncomingDuplexSyncConnection");
LOG.info("Running IncomingDuplexSyncConnection on transport " +
transportId.getString());
// Read and recognise the tag
StreamContext ctx = recogniseTag(reader, transportId);
if (ctx == null) {
@@ -65,10 +65,10 @@ class IncomingDuplexSyncConnection extends DuplexSyncConnection
return;
}
if (ctx.isHandshakeMode()) {
if (!performHandshake(ctx, contactId)) {
LOG.warning("Handshake failed");
return;
}
if (!performHandshake(ctx, contactId)) {
LOG.warning("Handshake failed");
return;
}
// Allocate a rotation mode stream context
ctx = allocateStreamContext(contactId, transportId);
if (ctx == null) {

View File

@@ -61,7 +61,8 @@ class OutgoingDuplexSyncConnection extends DuplexSyncConnection
@Override
public void run() {
LOG.info("Running OutgoingDuplexSyncConnection");
LOG.info("Running OutgoingDuplexSyncConnection on transport " +
transportId.getString());
// Allocate a stream context
StreamContext ctx = allocateStreamContext(contactId, transportId);
if (ctx == null) {

View File

@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
import org.briarproject.bramble.api.crypto.CryptoConstants;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
@@ -121,9 +122,12 @@ class ContactManagerImpl implements ContactManager, EventListener {
@Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
PublicKey handshake, boolean verified) throws DbException {
PublicKey handshake, boolean verified)
throws DbException, GeneralSecurityException {
ContactId c = db.addContact(txn, remote, local, handshake, verified);
Contact contact = db.getContact(txn, c);
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
keyManager.addContact(txn, c, handshake, ourKeyPair);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@@ -243,6 +247,25 @@ class ContactManagerImpl implements ContactManager, EventListener {
db.transaction(false, txn -> setContactAlias(txn, c, alias));
}
@Override
public void setHandshakePublicKey(Transaction txn, ContactId c,
PublicKey handshakePublicKey) throws DbException, GeneralSecurityException {
if (handshakePublicKey.getKeyType() !=
CryptoConstants.KEY_TYPE_AGREEMENT) {
throw new IllegalArgumentException();
}
db.setHandshakePublicKey(txn, c, handshakePublicKey);
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
keyManager.addContact(txn, c, handshakePublicKey, ourKeyPair);
}
@Override
public void setHandshakePublicKey(ContactId c, PublicKey handshakePublicKey)
throws DbException, GeneralSecurityException {
db.transaction(false,
txn -> setHandshakePublicKey(txn, c, handshakePublicKey));
}
@Override
public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException {

View File

@@ -32,6 +32,7 @@ import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeys;
import java.sql.Connection;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -695,6 +696,11 @@ interface Database<T> {
void setHandshakeKeyPair(T txn, AuthorId local, PublicKey publicKey,
PrivateKey privateKey) throws DbException;
/**
* Sets the handshake public key for a given contact
*/
void setHandshakePublicKey(T txn, ContactId c, PublicKey handshakePublicKey) throws DbException;
/**
* Marks the given message as permanent, i.e. not temporary.
*/

View File

@@ -1040,6 +1040,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
}
@Override
public void setHandshakePublicKey(Transaction transaction, ContactId c, PublicKey handshakePublicKey) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
db.setHandshakePublicKey(txn, c, handshakePublicKey);
}
@Override
public void setHandshakeKeyPair(Transaction transaction, AuthorId local,
PublicKey publicKey, PrivateKey privateKey) throws DbException {

View File

@@ -3058,6 +3058,23 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
@Override
public void setHandshakePublicKey(Connection txn, ContactId c, PublicKey handshakePublicKey) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE contacts SET handshakePublicKey = ? WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, handshakePublicKey.getEncoded());
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public void setGroupVisibility(Connection txn, ContactId c, GroupId g,
boolean shared) throws DbException {

View File

@@ -387,6 +387,13 @@ public class ConversationActivity extends BriarActivity
}
});
// enable help recover account action if available
observeOnce(viewModel.amCustodian(), this, enable -> {
if (enable) {
menu.findItem(R.id.action_help_recover_account).setEnabled(true);
}
});
return super.onCreateOptionsMenu(menu);
}

View File

@@ -41,6 +41,7 @@ import org.briarproject.briar.api.messaging.PrivateMessageFactory;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent;
import org.briarproject.briar.api.remotewipe.RemoteWipeManager;
import org.briarproject.briar.api.socialbackup.SocialBackupManager;
import java.util.Collection;
import java.util.List;
@@ -86,6 +87,7 @@ public class ConversationViewModel extends DbViewModel
private final AttachmentRetriever attachmentRetriever;
private final AttachmentCreator attachmentCreator;
private final RemoteWipeManager remoteWipeManager;
private final SocialBackupManager socialBackupManager;
@Nullable
private ContactId contactId = null;
@@ -108,6 +110,7 @@ public class ConversationViewModel extends DbViewModel
new MutableLiveEvent<>();
private final MutableLiveData<Boolean> amRemoteWiper = new MutableLiveData<>();
private final MutableLiveData<Boolean> isRemoteWiper = new MutableLiveData<>();
private final MutableLiveData<Boolean> amCustodian = new MutableLiveData<>();
@Inject
ConversationViewModel(Application application,
@@ -123,7 +126,8 @@ public class ConversationViewModel extends DbViewModel
PrivateMessageFactory privateMessageFactory,
AttachmentRetriever attachmentRetriever,
RemoteWipeManager remoteWipeManager,
AttachmentCreator attachmentCreator) {
AttachmentCreator attachmentCreator,
SocialBackupManager socialBackupManager) {
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
this.db = db;
this.eventBus = eventBus;
@@ -135,6 +139,7 @@ public class ConversationViewModel extends DbViewModel
this.attachmentRetriever = attachmentRetriever;
this.attachmentCreator = attachmentCreator;
this.remoteWipeManager = remoteWipeManager;
this.socialBackupManager = socialBackupManager;
messagingGroupId = map(contactItem, c ->
messagingManager.getContactGroup(c.getContact()).getId());
contactDeleted.setValue(false);
@@ -311,6 +316,10 @@ public class ConversationViewModel extends DbViewModel
boolean isWiper = db.transactionWithResult(true,
txn -> remoteWipeManager.isWiper(txn, c));
isRemoteWiper.postValue(isWiper);
// Check if we are a social backup custodian for this contact
boolean amCustodianBool = db.transactionWithResult(true,
txn -> socialBackupManager.amCustodian(txn, c));
amCustodian.postValue(amCustodianBool);
}
@DatabaseExecutor
@@ -404,6 +413,10 @@ public class ConversationViewModel extends DbViewModel
return isRemoteWiper;
}
LiveData<Boolean> amCustodian() {
return amCustodian;
}
@UiThread
void recheckFeaturesAndOnboarding(ContactId contactId) {
runOnDbThread(() -> {

View File

@@ -4,6 +4,7 @@ import android.os.Bundle;
import android.widget.Toast;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.briar.R;
@@ -29,6 +30,9 @@ public class DistributedBackupActivity extends BriarActivity implements
@Inject
public SocialBackupManager socialBackupManager;
@Inject
public ContactManager contactManager;
@Inject
public DatabaseComponent db;
@@ -52,6 +56,20 @@ public class DistributedBackupActivity extends BriarActivity implements
showInitialFragment(fragment);
});
} catch (DbException e) {
// Check the number of contacts in the contacts list > 1
try {
if (contactManager.getContacts().size() < 2) {
Toast.makeText(this,
R.string.social_backup_not_enough_contacts,
Toast.LENGTH_LONG).show();
finish();
}
} catch (DbException dbException) {
Toast.makeText(this,
R.string.reading_contacts_error,
Toast.LENGTH_LONG).show();
finish();
}
CustodianSelectorFragment fragment =
CustodianSelectorFragment.newInstance();
showInitialFragment(fragment);

View File

@@ -64,17 +64,13 @@ public class ThresholdSelectorFragment extends BaseFragment {
message = view.findViewById(R.id.textViewMessage);
mOfn = view.findViewById(R.id.textViewmOfn);
if (numberOfCustodians == 2) {
message.setText(R.string.threshold_too_few_custodians);
}
if (numberOfCustodians > 3) {
seekBar.setMax(numberOfCustodians -3);
seekBar.setProgress(threshold - 2);
seekBar.setOnSeekBarChangeListener(new SeekBarListener());
recommendedThreshold =
SecretSharingWrapper.defaultThreshold(numberOfCustodians);
threshold = recommendedThreshold;
seekBar.setProgress(threshold - 2);
} else {
seekBar.setEnabled(false);
threshold = 2;

View File

@@ -1,6 +1,8 @@
package org.briarproject.briar.android.socialbackup.recover;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
@@ -14,6 +16,7 @@ import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.android.contact.add.nearby.QrCodeUtils;
import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import org.briarproject.briar.api.socialbackup.MessageEncoder;
import org.briarproject.briar.api.socialbackup.ReturnShardPayload;
import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount;
import org.briarproject.briar.api.socialbackup.recovery.SecretOwnerTask;
@@ -22,6 +25,7 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.util.HashSet;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -50,6 +54,7 @@ class OwnerReturnShardViewModel extends AndroidViewModel
private final Executor ioExecutor;
private final SecretOwnerTask task;
private final RestoreAccount restoreAccount;
private final SharedPreferences prefs;
private final MutableLiveEvent<Boolean> errorTryAgain =
new MutableLiveEvent<>();
@@ -65,18 +70,26 @@ class OwnerReturnShardViewModel extends AndroidViewModel
private Bitmap qrCodeBitmap;
private WifiManager wifiManager;
private SecretKey secretKey;
private final MessageEncoder messageEncoder;
@Inject
OwnerReturnShardViewModel(Application app,
AndroidExecutor androidExecutor,
SecretOwnerTask task,
RestoreAccount restoreAccount,
@IoExecutor Executor ioExecutor) {
@IoExecutor Executor ioExecutor,
MessageEncoder messageEncoder) {
super(app);
this.androidExecutor = androidExecutor;
this.ioExecutor = ioExecutor;
this.restoreAccount = restoreAccount;
this.messageEncoder = messageEncoder;
this.task = task;
this.prefs = app.getSharedPreferences("account-recovery",
Context.MODE_PRIVATE);
restoreAccount.restoreFromPrevious(prefs.getStringSet("recovered", new HashSet<>()));
wifiManager = (WifiManager) app.getSystemService(WIFI_SERVICE);
// IntentFilter filter = new IntentFilter(ACTION_SCAN_MODE_CHANGED);
@@ -144,13 +157,6 @@ class OwnerReturnShardViewModel extends AndroidViewModel
ioExecutor.execute(() -> {
task.start(this, getWifiIpv4Address());
});
// KeyAgreementTask oldTask = task;
// KeyAgreementTask newTask = keyAgreementTaskProvider.get();
// task = newTask;
// ioExecutor.execute(() -> {
// if (oldTask != null) oldTask.stopListening();
// newTask.listen();
// });
}
@UiThread
@@ -218,16 +224,18 @@ class OwnerReturnShardViewModel extends AndroidViewModel
this.state.postValue(state);
});
} else if (state instanceof SecretOwnerTask.State.Success) {
// startClicked.setEvent(true);
this.state.postValue(state);
// TODO do same for failure
} else {
this.state.postValue(state);
}
}
public RestoreAccount.AddReturnShardPayloadResult addToShardSet(ReturnShardPayload toAdd) {
return restoreAccount.addReturnShardPayload(toAdd);
RestoreAccount.AddReturnShardPayloadResult result = restoreAccount.addReturnShardPayload(toAdd);
if (result == RestoreAccount.AddReturnShardPayloadResult.OK) {
prefs.edit().putStringSet("recovered", restoreAccount.getEncodedShards()).apply();
}
return result;
}
public boolean canRecover() {

View File

@@ -52,9 +52,11 @@ public class RestoreAccountActivity extends BaseActivity
showInitialFragment(RestoreAccountSetPasswordFragment.newInstance());
} else if (state == State.DOZE) {
showDozeFragment();
} else if (state == State.CREATED || state == State.FAILED) {
// TODO: Show an error if failed
} else if (state == State.CREATED) {
showApp();
} else { // FAILED
// TODO: Show an error if failed
finish();
}
}

View File

@@ -1,6 +1,8 @@
package org.briarproject.briar.android.socialbackup.recover;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.contact.ContactManager;
@@ -45,27 +47,27 @@ class RestoreAccountViewModel extends AndroidViewModel {
new MutableLiveData<>(false);
private final AccountManager accountManager;
private final ContactManager contactManager;
private final Executor ioExecutor;
private final PasswordStrengthEstimator strengthEstimator;
private final DozeHelper dozeHelper;
private final RestoreAccount restoreAccount;
private final SharedPreferences prefs;
@Inject
RestoreAccountViewModel(Application app,
AccountManager accountManager,
ContactManager contactManager,
RestoreAccount restoreAccount,
@IoExecutor Executor ioExecutor,
PasswordStrengthEstimator strengthEstimator,
DozeHelper dozeHelper) {
super(app);
this.accountManager = accountManager;
this.contactManager = contactManager;
this.ioExecutor = ioExecutor;
this.strengthEstimator = strengthEstimator;
this.dozeHelper = dozeHelper;
this.restoreAccount = restoreAccount;
this.prefs = app.getSharedPreferences("account-recovery",
Context.MODE_PRIVATE);
ioExecutor.execute(() -> {
if (accountManager.accountExists()) {
@@ -112,17 +114,24 @@ class RestoreAccountViewModel extends AndroidViewModel {
if (socialBackup == null) {
LOG.warning("Cannot retrieve social backup");
state.postEvent(State.FAILED);
return;
}
Identity identity = socialBackup.getIdentity();
ioExecutor.execute(() -> {
if (accountManager.restoreAccount(identity, password)) {
LOG.info("Restored account");
try {
restoreAccount.addContactsToDb();
restoreAccount.restoreAccountWhenDatabaseReady();
} catch (DbException e) {
LOG.warning("Cannot retrieve social backup");
LOG.warning("Failure processing social backup");
e.printStackTrace();
state.postEvent(State.FAILED);
return;
}
// Remove partial recovery from shared preferences
prefs.edit().clear().apply();
state.postEvent(State.CREATED);
} else {
LOG.warning("Failed to create account");

View File

@@ -31,6 +31,7 @@
android:id="@+id/action_help_recover_account"
android:icon="@drawable/introduction_white"
android:title="@string/help_recover_account"
android:enabled="false"
app:showAsAction="never"/>
<item

View File

@@ -9,7 +9,7 @@
<string name="setup_name_explanation">Your nickname will be shown next to any content you post. You can\'t change it after creating your account.</string>
<string name="setup_next">Next</string>
<string name="setup_password_intro">Choose a Password</string>
<string name="setup_password_explanation">Your Briar account is stored encrypted on your device, not in the cloud. If you forget your password or uninstall Briar, there\'s no way to recover your account.\n\nChoose a long password that\'s hard to guess, such as four random words, or ten random letters, numbers and symbols.</string>
<string name="setup_password_explanation">Your Briar account is stored encrypted on your device, not in the cloud. If you forget your password or uninstall Briar, you can only recover your account if you have made a social backup.\n\nChoose a long password that\'s hard to guess, such as four random words, or ten random letters, numbers and symbols.</string>
<string name="setup_doze_title">Background Connections</string>
<string name="setup_doze_intro">To receive messages, Briar needs to stay connected in the background.</string>
<string name="setup_doze_explanation">To receive messages, Briar needs to stay connected in the background. Please disable battery optimizations so Briar can stay connected.</string>
@@ -37,7 +37,7 @@
<string name="sign_in_button">Sign In</string>
<string name="forgotten_password">I have forgotten my password</string>
<string name="dialog_title_lost_password">Lost Password</string>
<string name="dialog_message_lost_password">Your Briar account is stored encrypted on your device, not in the cloud, so we can\'t reset your password. Would you like to delete your account and start again?\n\nCaution: Your identities, contacts and messages will be permanently lost.</string>
<string name="dialog_message_lost_password">Your Briar account is stored encrypted on your device, not in the cloud, so we can\'t reset your password. If you have made a social backup, you can delete your account and set a new password when you restore it. Would you like to delete your account and start again?\n\nCaution: Your identities, contacts and messages will be permanently lost if you do not have a backup.</string>
<string name="startup_failed_notification_title">Briar could not start</string>
<string name="startup_failed_notification_text">Tap for more information.</string>
<string name="startup_failed_activity_title">Briar Startup Failure</string>
@@ -664,7 +664,6 @@
<string name="threshold_recommended">Secure - recommended threshold</string>
<string name="threshold_low_insecure">Insecure higher threshold recommended</string>
<string name="threshold_high_insecure">Danger of loss lower threshold recommended</string>
<string name="threshold_too_few_custodians">Danger of loss - more contacts recommended</string>
<string name="threshold_defined">Choose Threshold</string>
<string name="threshold_m_of_n">%d of %d contacts needed to recover your account</string>
@@ -730,6 +729,9 @@
<string name="activity_name_custodian_help_recovery">Help recover account</string>
<string name="existing_backup_explain">%d of the following contacts are needed to restore your account:</string>
<string name="social_backup_not_enough_contacts">To make a social backup, you need at least 2 contacts in your contacts list</string>
<string name="reading_contacts_error">There was an error reading your contacts list</string>
<!-- Remote Wipe -->
<!-- setup -->

View File

@@ -0,0 +1,22 @@
package org.briarproject.briar.api.handshakekeyexchange;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.briar.api.conversation.ConversationManager;
public interface HandshakeKeyExchangeManager extends ConversationManager.ConversationClient {
/**
* The unique ID of the client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.handshakekeyexchange");
/**
* The current major version of the handshake key exchange client.
*/
int MAJOR_VERSION = 0;
/**
* The current minor version of the handshake key exchange client.
*/
int MINOR_VERSION = 0;
}

View File

@@ -1,17 +1,22 @@
package org.briarproject.briar.api.socialbackup;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.util.List;
import java.util.Map;
public class SocialBackup {
private Identity identity;
private List<ContactData> contacts;
private Map<TransportId, TransportProperties> localTransportProperties;
private int version;
public SocialBackup (Identity identity, List<ContactData> contacts, int version) {
public SocialBackup (Identity identity, List<ContactData> contacts, Map<TransportId, TransportProperties> localTransportProperties, int version) {
this.identity = identity;
this.contacts = contacts;
this.localTransportProperties = localTransportProperties;
this.version = version;
}
@@ -23,6 +28,10 @@ public class SocialBackup {
return contacts;
}
public Map<TransportId, TransportProperties> getLocalTransportProperties() {
return localTransportProperties;
}
public int getVersion() {
return version;
}

View File

@@ -6,17 +6,21 @@ import org.briarproject.briar.api.socialbackup.ReturnShardPayload;
import org.briarproject.briar.api.socialbackup.SocialBackup;
import java.security.GeneralSecurityException;
import java.util.Set;
public interface RestoreAccount {
enum AddReturnShardPayloadResult {
DUPLICATE,
MISMATCH,
OK
OK,
RECOVERED
}
int getNumberOfShards();
Set<String> getEncodedShards();
AddReturnShardPayloadResult addReturnShardPayload(ReturnShardPayload toAdd);
boolean canRecover();
@@ -25,5 +29,11 @@ public interface RestoreAccount {
SocialBackup getSocialBackup();
void addContactsToDb() throws DbException;
// void addContactsToDb() throws DbException;
void restoreFromPrevious(Set<String> previousShards);
void restoreAccountWhenDatabaseReady() throws DbException;
// void addLocalTransportProperties() throws DbException;
}

View File

@@ -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());
}
}
}

View File

@@ -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 {

View File

@@ -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";
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -34,6 +34,7 @@ buildscript {
classpath files('libs/gradle-witness.jar')
}
// ext.dagger_version = "2.33"
ext.junit_version = "4.12"
ext.jmock_version = '2.12.0'
}