From 2a365d986fdce628f9df57f4174fc2c117c3e4a2 Mon Sep 17 00:00:00 2001 From: ameba23 Date: Wed, 21 Apr 2021 15:36:44 +0200 Subject: [PATCH 01/25] Restore account method for account manager --- .../bramble/api/account/AccountManager.java | 12 ++++++++++++ .../bramble/account/AccountManagerImpl.java | 12 ++++++++++++ .../socialbackup/recover/RestoreAccountActivity.java | 4 ++++ .../recover/RestoreAccountViewModel.java | 4 ++++ 4 files changed, 32 insertions(+) create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java index f33b44594..4acbee525 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java @@ -2,6 +2,7 @@ package org.briarproject.bramble.api.account; import org.briarproject.bramble.api.crypto.DecryptionException; import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; @@ -43,6 +44,17 @@ public interface AccountManager { */ boolean createAccount(String name, String password); + /** + * Restores a given identity by registering it with the + * {@link IdentityManager}. Creates a database key, encrypts it with the + * given password and stores it on disk. {@link #accountExists()} will + * return true after this method returns true. + * @param identity + * @param password + * @return + */ + boolean restoreAccount(Identity identity, String password); + /** * Deletes all account state from disk. {@link #accountExists()} will * return false after this method returns. diff --git a/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java index 4636dfad3..1e1fa71c7 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java @@ -176,6 +176,18 @@ class AccountManagerImpl implements AccountManager { } } + public boolean restoreAccount(Identity identity, String password) { + synchronized (stateChangeLock) { + if (hasDatabaseKey()) + throw new AssertionError("Already have a database key"); + identityManager.registerIdentity(identity); + SecretKey key = crypto.generateSecretKey(); + if (!encryptAndStoreDatabaseKey(key, password)) return false; + databaseKey = key; + return true; + } + } + @GuardedBy("stateChangeLock") private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) { byte[] plaintext = key.getBytes(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java new file mode 100644 index 000000000..cd3aceb7f --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java @@ -0,0 +1,4 @@ +package org.briarproject.briar.android.socialbackup.recover; + +public class RestoreAccountActivity { +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java new file mode 100644 index 000000000..3f9f020af --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java @@ -0,0 +1,4 @@ +package org.briarproject.briar.android.socialbackup.recover; + +public class RestoreAccountViewModel { +} From afc0bc3f3c555f46a8d5ba01863999371bd62655 Mon Sep 17 00:00:00 2001 From: ameba23 Date: Wed, 21 Apr 2021 16:34:28 +0200 Subject: [PATCH 02/25] Refactor and create a RestoreAccount class which does the combining --- .../briar/api}/socialbackup/ContactData.java | 0 .../briar/api}/socialbackup/SocialBackup.java | 6 +- .../socialbackup/recovery/RestoreAccount.java | 4 + .../socialbackup/BackupPayloadDecoder.java | 1 + .../BackupPayloadDecoderImpl.java | 9 ++- .../socialbackup/BackupPayloadEncoder.java | 2 +- .../BackupPayloadEncoderImpl.java | 4 +- .../socialbackup/SocialBackupManagerImpl.java | 20 ++--- .../socialbackup/SocialBackupModule.java | 7 ++ .../recovery/RestoreAccountImpl.java | 73 +++++++++++++++++++ 10 files changed, 106 insertions(+), 20 deletions(-) rename {briar-core/src/main/java/org/briarproject/briar => briar-api/src/main/java/org/briarproject/briar/api}/socialbackup/ContactData.java (100%) rename {briar-core/src/main/java/org/briarproject/briar => briar-api/src/main/java/org/briarproject/briar/api}/socialbackup/SocialBackup.java (60%) create mode 100644 briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/ContactData.java b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/ContactData.java similarity index 100% rename from briar-core/src/main/java/org/briarproject/briar/socialbackup/ContactData.java rename to briar-api/src/main/java/org/briarproject/briar/api/socialbackup/ContactData.java diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackup.java b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/SocialBackup.java similarity index 60% rename from briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackup.java rename to briar-api/src/main/java/org/briarproject/briar/api/socialbackup/SocialBackup.java index 341d04816..a2173f8ef 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackup.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/SocialBackup.java @@ -6,10 +6,10 @@ import java.util.List; public class SocialBackup { private Identity identity; - private List contacts; + private List contacts; private int version; - SocialBackup (Identity identity, List contacts, int version) { + SocialBackup (Identity identity, List contacts, int version) { this.identity = identity; this.contacts = contacts; this.version = version; @@ -19,7 +19,7 @@ public class SocialBackup { return identity; } - public List getContacts() { + public List getContacts() { return contacts; } diff --git a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java new file mode 100644 index 000000000..32a25936a --- /dev/null +++ b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java @@ -0,0 +1,4 @@ +package org.briarproject.briar.api.socialbackup.recovery; + +public interface RestoreAccount { +} diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadDecoder.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadDecoder.java index 835869dac..640aac4a9 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadDecoder.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadDecoder.java @@ -4,6 +4,7 @@ import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.briar.api.socialbackup.BackupPayload; +import org.briarproject.briar.api.socialbackup.SocialBackup; import java.security.GeneralSecurityException; diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadDecoderImpl.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadDecoderImpl.java index a2672016b..4171d8154 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadDecoderImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadDecoderImpl.java @@ -20,6 +20,7 @@ import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.briar.api.socialbackup.BackupPayload; import org.briarproject.briar.api.socialbackup.MessageParser; import org.briarproject.briar.api.socialbackup.Shard; +import org.briarproject.briar.api.socialbackup.SocialBackup; import java.security.GeneralSecurityException; import java.security.SecureRandom; @@ -54,7 +55,7 @@ public class BackupPayloadDecoderImpl implements BackupPayloadDecoder { this.messageParser = messageParser; } - public SocialBackup decodeBackupPayload( + public org.briarproject.briar.api.socialbackup.SocialBackup decodeBackupPayload( SecretKey secret, BackupPayload backupPayload) throws FormatException, GeneralSecurityException { @@ -103,7 +104,7 @@ public class BackupPayloadDecoderImpl implements BackupPayloadDecoder { handShakePrivateKey, created); LOG.info("New identity created"); - List contactDataList = new ArrayList(); + List contactDataList = new ArrayList(); for (int i = 0; i < bdfContactData.size(); i++) { BdfList bdfData = bdfContactData.getList(i); @@ -139,8 +140,8 @@ public class BackupPayloadDecoderImpl implements BackupPayloadDecoder { Contact contact = new Contact(contactId, author, author.getId(), alias, contactHandshakePublicKey, false); - ContactData contactData = - new ContactData(contact, properties, shard); + org.briarproject.briar.api.socialbackup.ContactData contactData = + new org.briarproject.briar.api.socialbackup.ContactData(contact, properties, shard); contactDataList.add(contactData); LOG.info("Contact added"); } diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadEncoder.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadEncoder.java index b65f027da..7019661c9 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadEncoder.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadEncoder.java @@ -10,5 +10,5 @@ import java.util.List; interface BackupPayloadEncoder { org.briarproject.briar.api.socialbackup.BackupPayload encodeBackupPayload(SecretKey secret, Identity identity, - List contactData, int version); + List contactData, int version); } diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadEncoderImpl.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadEncoderImpl.java index f813c4efd..7e867f697 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadEncoderImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadEncoderImpl.java @@ -46,7 +46,7 @@ class BackupPayloadEncoderImpl implements BackupPayloadEncoder { @Override public org.briarproject.briar.api.socialbackup.BackupPayload encodeBackupPayload(SecretKey secret, - Identity identity, List contactData, int version) { + Identity identity, List contactData, int version) { // Encode the local identity BdfList bdfIdentity = new BdfList(); LocalAuthor localAuthor = identity.getLocalAuthor(); @@ -56,7 +56,7 @@ class BackupPayloadEncoderImpl implements BackupPayloadEncoder { bdfIdentity.add(identity.getHandshakePrivateKey().getEncoded()); // Encode the contact data BdfList bdfContactData = new BdfList(); - for (ContactData cd : contactData) { + for (org.briarproject.briar.api.socialbackup.ContactData cd : contactData) { BdfList bdfData = new BdfList(); Contact contact = cd.getContact(); bdfData.add(clientHelper.toList(contact.getAuthor())); diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupManagerImpl.java index b7a58e7d5..3ebc187f9 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupManagerImpl.java @@ -193,12 +193,12 @@ class SocialBackupManagerImpl extends ConversationClientImpl // Add the shard to our backup, if any if (localBackupExists(txn)) { Shard shard = messageParser.parseShardMessage(body); - List contactData = loadContactData(txn); - ListIterator it = contactData.listIterator(); + List contactData = loadContactData(txn); + ListIterator it = contactData.listIterator(); while (it.hasNext()) { - ContactData cd = it.next(); + org.briarproject.briar.api.socialbackup.ContactData cd = it.next(); if (cd.getContact().getId().equals(contactId)) { - it.set(new ContactData(cd.getContact(), + it.set(new org.briarproject.briar.api.socialbackup.ContactData(cd.getContact(), cd.getProperties(), shard)); updateBackup(txn, contactData); break; @@ -278,7 +278,7 @@ class SocialBackupManagerImpl extends ConversationClientImpl } // Create the encrypted backup payload SecretKey secret = crypto.generateSecretKey(); - List contactData = loadContactData(txn); + List contactData = loadContactData(txn); BackupPayload payload = createBackupPayload(txn, secret, contactData, 0); // Create the shards @@ -415,17 +415,17 @@ class SocialBackupManagerImpl extends ConversationClientImpl } private BackupPayload createBackupPayload(Transaction txn, - SecretKey secret, List contactData, int version) + SecretKey secret, List contactData, int version) throws DbException { Identity identity = identityManager.getIdentity(txn); return backupPayloadEncoder.encodeBackupPayload(secret, identity, contactData, version); } - private List loadContactData(Transaction txn) + private List loadContactData(Transaction txn) throws DbException { Collection contacts = contactManager.getContacts(txn); - List contactData = new ArrayList<>(); + List contactData = new ArrayList<>(); for (Contact c : contacts) { // Skip contacts that are in the process of being removed Group contactGroup = getContactGroup(c); @@ -433,7 +433,7 @@ class SocialBackupManagerImpl extends ConversationClientImpl Map props = getTransportProperties(txn, c.getId()); Shard shard = getRemoteShard(txn, contactGroup.getId()); - contactData.add(new ContactData(c, props, shard)); + contactData.add(new org.briarproject.briar.api.socialbackup.ContactData(c, props, shard)); } return contactData; } @@ -513,7 +513,7 @@ class SocialBackupManagerImpl extends ConversationClientImpl throw new DbException(e); } } - private void updateBackup(Transaction txn, List contactData) + private void updateBackup(Transaction txn, List contactData) throws DbException { BackupMetadata backupMetadata = requireNonNull(getBackupMetadata(txn)); int newVersion = backupMetadata.getVersion() + 1; diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupModule.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupModule.java index 0a5530e8f..9a9c3adf8 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupModule.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupModule.java @@ -11,8 +11,10 @@ import org.briarproject.briar.api.conversation.ConversationManager; import org.briarproject.briar.api.socialbackup.SocialBackupExchangeManager; import org.briarproject.briar.api.socialbackup.SocialBackupManager; import org.briarproject.briar.api.socialbackup.recovery.CustodianTask; +import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; import org.briarproject.briar.api.socialbackup.recovery.SecretOwnerTask; import org.briarproject.briar.socialbackup.recovery.CustodianTaskImpl; +import org.briarproject.briar.socialbackup.recovery.RestoreAccountImpl; import org.briarproject.briar.socialbackup.recovery.SecretOwnerTaskImpl; import javax.inject.Inject; @@ -119,4 +121,9 @@ public class SocialBackupModule { CustodianTask custodianTask(CustodianTaskImpl custodianTask) { return custodianTask; } + + @Provides + RestoreAccount restoreAccount(RestoreAccountImpl restoreAccount) { + return restoreAccount; + } } diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java new file mode 100644 index 000000000..b6a918649 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java @@ -0,0 +1,73 @@ +package org.briarproject.briar.socialbackup.recovery; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.briar.api.socialbackup.BackupPayload; +import org.briarproject.briar.api.socialbackup.DarkCrystal; +import org.briarproject.briar.api.socialbackup.ReturnShardPayload; +import org.briarproject.briar.api.socialbackup.Shard; +import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; +import org.briarproject.briar.socialbackup.BackupPayloadDecoder; +import org.briarproject.briar.api.socialbackup.SocialBackup; + +import java.security.GeneralSecurityException; +import java.util.ArrayList; + +import javax.inject.Inject; + +public class RestoreAccountImpl implements RestoreAccount { + private ArrayList recoveredShards = new ArrayList<>(); + private final DarkCrystal darkCrystal; + private SecretKey secretKey; + private final BackupPayloadDecoder backupPayloadDecoder; + private SocialBackup socialBackup; + + @Inject + RestoreAccountImpl(DarkCrystal darkCrystal, BackupPayloadDecoder backupPayloadDecoder) { + this.darkCrystal = darkCrystal; + this.backupPayloadDecoder = backupPayloadDecoder; + } + + public int getNumberOfShards() { return recoveredShards.size(); } + + // TODO figure out how to actually use a hash set for these objects + public boolean addReturnShardPayload(ReturnShardPayload toAdd) { + boolean found = false; + for (ReturnShardPayload returnShardPayload : recoveredShards) { + if (toAdd.equals(returnShardPayload)) { + found = true; + break; + } + } + if (!found) recoveredShards.add(toAdd); + return !found; + } + + public boolean canRecover() { + ArrayList shards = new ArrayList(); + for (ReturnShardPayload returnShardPayload : recoveredShards) { + // TODO check shards all have same secret id + shards.add(returnShardPayload.getShard()); + } + try { + secretKey = darkCrystal.combineShards(shards); + } catch (GeneralSecurityException e) { + // TODO handle error message + return false; + } + return true; + } + + public int recover() throws FormatException, GeneralSecurityException { + if (secretKey == null) throw new GeneralSecurityException(); + // TODO find backup with highest version number + BackupPayload backupPayload = recoveredShards.get(0).getBackupPayload(); + socialBackup = backupPayloadDecoder.decodeBackupPayload(secretKey, backupPayload); + int version = socialBackup.getVersion(); + return version; + } + + public SocialBackup getSocialBackup() { + return socialBackup; + } +} From 5c22d233ef6a2d7154be267537f41d3ddf816a5b Mon Sep 17 00:00:00 2001 From: ameba23 Date: Wed, 21 Apr 2021 16:34:56 +0200 Subject: [PATCH 03/25] Restore account activity/view model --- .../android/activity/ActivityComponent.java | 4 + .../recover/OwnerReturnShardViewModel.java | 46 ++----- .../recover/RestoreAccountActivity.java | 81 +++++++++++- .../recover/RestoreAccountViewModel.java | 120 +++++++++++++++++- 4 files changed, 212 insertions(+), 39 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index c7aeb94c5..b97ee15ff 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -94,8 +94,10 @@ import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardFragm import org.briarproject.briar.android.socialbackup.ShardsSentFragment; import org.briarproject.briar.android.socialbackup.ThresholdSelectorFragment; import org.briarproject.briar.android.socialbackup.creation.CreateBackupModule; +import org.briarproject.briar.android.socialbackup.recover.RestoreAccountActivity; import org.briarproject.briar.android.splash.SplashScreenActivity; import org.briarproject.briar.android.test.TestDataActivity; +import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; import dagger.Component; @@ -206,6 +208,8 @@ public interface ActivityComponent { void inject(OwnerRecoveryModeMainFragment ownerRecoveryModeMainFragment); + void inject(RestoreAccountActivity restoreAccountActivity); + // Fragments void inject(AuthorNameFragment fragment); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardViewModel.java index 91a4cc7af..e5b02dfc6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardViewModel.java @@ -15,12 +15,12 @@ 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.BackupPayload; -import org.briarproject.briar.api.socialbackup.DarkCrystal; import org.briarproject.briar.api.socialbackup.ReturnShardPayload; import org.briarproject.briar.api.socialbackup.Shard; +import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; import org.briarproject.briar.api.socialbackup.recovery.SecretOwnerTask; import org.briarproject.briar.socialbackup.BackupPayloadDecoder; -import org.briarproject.briar.socialbackup.SocialBackup; +import org.briarproject.briar.api.socialbackup.SocialBackup; import java.net.InetAddress; import java.net.UnknownHostException; @@ -56,8 +56,7 @@ class OwnerReturnShardViewModel extends AndroidViewModel private final AndroidExecutor androidExecutor; private final Executor ioExecutor; private final SecretOwnerTask task; - private final DarkCrystal darkCrystal; - private final BackupPayloadDecoder backupPayloadDecoder; + private final RestoreAccount restoreAccount; private final MutableLiveEvent showQrCodeFragment = new MutableLiveEvent<>(); @@ -67,7 +66,6 @@ class OwnerReturnShardViewModel extends AndroidViewModel new MutableLiveEvent<>(); private boolean wasContinueClicked = false; private boolean isActivityResumed = false; - private ArrayList recoveredShards = new ArrayList<>(); private Bitmap qrCodeBitmap; private WifiManager wifiManager; private SecretKey secretKey; @@ -76,14 +74,12 @@ class OwnerReturnShardViewModel extends AndroidViewModel OwnerReturnShardViewModel(Application app, AndroidExecutor androidExecutor, SecretOwnerTask task, - DarkCrystal darkCrystal, - BackupPayloadDecoder backupPayloadDecoder, + RestoreAccount restoreAccount, @IoExecutor Executor ioExecutor) { super(app); this.androidExecutor = androidExecutor; this.ioExecutor = ioExecutor; - this.backupPayloadDecoder = backupPayloadDecoder; - this.darkCrystal = darkCrystal; + this.restoreAccount = restoreAccount; this.task = task; wifiManager = (WifiManager) app.getSystemService(WIFI_SERVICE); @@ -196,7 +192,7 @@ class OwnerReturnShardViewModel extends AndroidViewModel } public int getNumberOfShards() { - return recoveredShards.size(); + return restoreAccount.getNumberOfShards(); } @Override @@ -227,38 +223,14 @@ class OwnerReturnShardViewModel extends AndroidViewModel // TODO figure out how to actually use a hash set for these objects public boolean addToShardSet(ReturnShardPayload toAdd) { - boolean found = false; - for (ReturnShardPayload returnShardPayload : recoveredShards) { - if (toAdd.equals(returnShardPayload)) { - found = true; - break; - } - } - if (!found) recoveredShards.add(toAdd); - return !found; + return restoreAccount.addReturnShardPayload(toAdd); } public boolean canRecover() { - ArrayList shards = new ArrayList(); - for (ReturnShardPayload returnShardPayload : recoveredShards) { - // TODO check shards all have same secret id - shards.add(returnShardPayload.getShard()); - } - try { - secretKey = darkCrystal.combineShards(shards); - } catch (GeneralSecurityException e) { - // TODO handle error message - return false; - } - return true; + return restoreAccount.canRecover(); } public int recover() throws FormatException, GeneralSecurityException { - if (secretKey == null) throw new GeneralSecurityException(); - // TODO find backup with highest version number - BackupPayload backupPayload = recoveredShards.get(0).getBackupPayload(); - SocialBackup decodedBackup = backupPayloadDecoder.decodeBackupPayload(secretKey, backupPayload); - int version = decodedBackup.getVersion(); - return version; + return restoreAccount.recover(); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java index cd3aceb7f..5031e0855 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java @@ -1,4 +1,83 @@ package org.briarproject.briar.android.socialbackup.recover; -public class RestoreAccountActivity { +import android.annotation.TargetApi; +import android.content.Intent; +import android.os.Bundle; + +import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.briar.R; +import org.briarproject.briar.android.account.DozeFragment; +import org.briarproject.briar.android.account.SetPasswordFragment; +import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.activity.BaseActivity; +import org.briarproject.briar.android.fragment.BaseFragment; + +import javax.annotation.Nullable; +import javax.inject.Inject; + +import androidx.lifecycle.ViewModelProvider; + +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; +import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY; +import static org.briarproject.briar.android.socialbackup.recover.RestoreAccountViewModel.State; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +public class RestoreAccountActivity extends BaseActivity + implements BaseFragment.BaseFragmentListener { + + @Inject + ViewModelProvider.Factory viewModelFactory; + RestoreAccountViewModel viewModel; + + @Override + public void injectActivity(ActivityComponent component) { + component.inject(this); + + viewModel = new ViewModelProvider(this, viewModelFactory) + .get(RestoreAccountViewModel.class); + viewModel.getState().observeEvent(this, this::onStateChanged); + } + + @Override + public void onCreate(@Nullable Bundle state) { + super.onCreate(state); + setContentView(R.layout.activity_fragment_container); + } + + private void onStateChanged(RestoreAccountViewModel.State state) { + if (state == RestoreAccountViewModel.State.SET_PASSWORD) { + showInitialFragment(SetPasswordFragment.newInstance()); + } else if (state == State.DOZE) { + showDozeFragment(); + } else if (state == State.CREATED || state == State.FAILED) { + // TODO: Show an error if failed + showApp(); + } + } + + @TargetApi(23) + void showDozeFragment() { + showNextFragment(DozeFragment.newInstance()); + } + + void showApp() { + Intent i = new Intent(this, ENTRY_ACTIVITY); + i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME | + FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_CLEAR_TOP); + startActivity(i); + supportFinishAfterTransition(); + overridePendingTransition(R.anim.screen_new_in, R.anim.screen_old_out); + } + + @Override + @Deprecated + public void runOnDbThread(Runnable runnable) { + throw new RuntimeException("Don't use this deprecated method here."); + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java index 3f9f020af..521a97552 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java @@ -1,4 +1,122 @@ package org.briarproject.briar.android.socialbackup.recover; -public class RestoreAccountViewModel { +import android.app.Application; + +import org.briarproject.bramble.api.account.AccountManager; +import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator; +import org.briarproject.bramble.api.identity.Identity; +import org.briarproject.bramble.api.lifecycle.IoExecutor; +import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.briar.R; +import org.briarproject.briar.android.account.DozeHelper; +import org.briarproject.briar.android.viewmodel.LiveEvent; +import org.briarproject.briar.android.viewmodel.MutableLiveEvent; +import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; + +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import androidx.annotation.Nullable; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import static java.util.logging.Logger.getLogger; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +class RestoreAccountViewModel extends AndroidViewModel { + enum State {SET_PASSWORD, DOZE, CREATED, FAILED} + + private static final Logger LOG = + getLogger(RestoreAccountViewModel.class.getName()); + + @Nullable + private String password; + private final MutableLiveEvent + state = new MutableLiveEvent<>(); + private final MutableLiveData isCreatingAccount = + new MutableLiveData<>(false); + + private final AccountManager accountManager; + private final Executor ioExecutor; + private final PasswordStrengthEstimator strengthEstimator; + private final DozeHelper dozeHelper; + private final RestoreAccount restoreAccount; + + @Inject + RestoreAccountViewModel(Application app, + AccountManager accountManager, + RestoreAccount restoreAccount, + @IoExecutor Executor ioExecutor, + PasswordStrengthEstimator strengthEstimator, + DozeHelper dozeHelper) { + super(app); + this.accountManager = accountManager; + this.ioExecutor = ioExecutor; + this.strengthEstimator = strengthEstimator; + this.dozeHelper = dozeHelper; + this.restoreAccount = restoreAccount; + + ioExecutor.execute(() -> { + if (accountManager.accountExists()) { + throw new AssertionError(); + } else { + state.postEvent(State.SET_PASSWORD); + } + }); + } + + LiveEvent getState() { + return state; + } + + LiveData getIsCreatingAccount() { + return isCreatingAccount; + } + +// void setAuthorName(String authorName) { +// this.authorName = authorName; +// state.setEvent(SET_PASSWORD); +// } + + void setPassword(String password) { + this.password = password; + if (needToShowDozeFragment()) { + state.setEvent(State.DOZE); + } else { + createAccount(); + } + } + + float estimatePasswordStrength(String password) { + return strengthEstimator.estimateStrength(password); + } + + boolean needToShowDozeFragment() { + return dozeHelper.needToShowDozeFragment(getApplication()); + } + + void dozeExceptionConfirmed() { + createAccount(); + } + + private void createAccount() { +// if (authorName == null) throw new IllegalStateException(); + if (password == null) throw new IllegalStateException(); + isCreatingAccount.setValue(true); + Identity identity = restoreAccount.getSocialBackup().getIdentity(); + ioExecutor.execute(() -> { + if (accountManager.restoreAccount(identity, password)) { + LOG.info("Restore account"); + state.postEvent(State.CREATED); + } else { + LOG.warning("Failed to create account"); + state.postEvent(State.FAILED); + } + }); + } } From 34f15b6bdcca3395ea5c5d067c79fc6ad94ec602 Mon Sep 17 00:00:00 2001 From: ameba23 Date: Wed, 21 Apr 2021 16:35:27 +0200 Subject: [PATCH 04/25] ContactData and SocialBackup now live in briar-api --- .../briar/api/socialbackup/ContactData.java | 3 +-- .../briar/api/socialbackup/SocialBackup.java | 8 ++++---- .../api/socialbackup/recovery/RestoreAccount.java | 11 +++++++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/ContactData.java b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/ContactData.java index 10778858a..7d83a6eaa 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/ContactData.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/ContactData.java @@ -1,10 +1,9 @@ -package org.briarproject.briar.socialbackup; +package org.briarproject.briar.api.socialbackup; import org.briarproject.bramble.api.contact.Contact; 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.Shard; import java.util.Map; diff --git a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/SocialBackup.java b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/SocialBackup.java index a2173f8ef..76cfa6f05 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/SocialBackup.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/SocialBackup.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.socialbackup; +package org.briarproject.briar.api.socialbackup; import org.briarproject.bramble.api.identity.Identity; @@ -6,10 +6,10 @@ import java.util.List; public class SocialBackup { private Identity identity; - private List contacts; + private List contacts; private int version; - SocialBackup (Identity identity, List contacts, int version) { + SocialBackup (Identity identity, List contacts, int version) { this.identity = identity; this.contacts = contacts; this.version = version; @@ -19,7 +19,7 @@ public class SocialBackup { return identity; } - public List getContacts() { + public List getContacts() { return contacts; } diff --git a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java index 32a25936a..9cfbc85ae 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java @@ -1,4 +1,15 @@ package org.briarproject.briar.api.socialbackup.recovery; +import org.briarproject.bramble.api.FormatException; +import org.briarproject.briar.api.socialbackup.ReturnShardPayload; +import org.briarproject.briar.api.socialbackup.SocialBackup; + +import java.security.GeneralSecurityException; + public interface RestoreAccount { + int getNumberOfShards(); + boolean addReturnShardPayload(ReturnShardPayload toadd); + boolean canRecover(); + int recover() throws FormatException, GeneralSecurityException; + SocialBackup getSocialBackup(); } From 327cbe23ee3f1f13d02ff6d9b07f0c6d07e7a7e2 Mon Sep 17 00:00:00 2001 From: ameba23 Date: Wed, 21 Apr 2021 21:52:48 +0200 Subject: [PATCH 05/25] Use DozeHelper --- .../java/org/briarproject/briar/android/account/DozeHelper.java | 2 +- .../android/socialbackup/recover/RestoreAccountViewModel.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/account/DozeHelper.java b/briar-android/src/main/java/org/briarproject/briar/android/account/DozeHelper.java index f56431488..13add7b55 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/account/DozeHelper.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/DozeHelper.java @@ -2,7 +2,7 @@ package org.briarproject.briar.android.account; import android.content.Context; -interface DozeHelper { +public interface DozeHelper { boolean needToShowDozeFragment(Context context); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java index 521a97552..b4cb07902 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java @@ -8,7 +8,6 @@ import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; -import org.briarproject.briar.R; import org.briarproject.briar.android.account.DozeHelper; import org.briarproject.briar.android.viewmodel.LiveEvent; import org.briarproject.briar.android.viewmodel.MutableLiveEvent; From 4b77a9ab60e7979f04bfded4c1725dec3c56ef3d Mon Sep 17 00:00:00 2001 From: ameba23 Date: Wed, 21 Apr 2021 21:53:06 +0200 Subject: [PATCH 06/25] Use backup with highest version number --- .../recovery/RestoreAccountImpl.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java index b6a918649..9c6db68de 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java @@ -60,11 +60,17 @@ public class RestoreAccountImpl implements RestoreAccount { public int recover() throws FormatException, GeneralSecurityException { if (secretKey == null) throw new GeneralSecurityException(); - // TODO find backup with highest version number - BackupPayload backupPayload = recoveredShards.get(0).getBackupPayload(); - socialBackup = backupPayloadDecoder.decodeBackupPayload(secretKey, backupPayload); - int version = socialBackup.getVersion(); - return version; + // Find backup with highest version number + int highestVersion = 0; + for (ReturnShardPayload returnShardPayload : recoveredShards) { + BackupPayload backupPayload = returnShardPayload.getBackupPayload(); + SocialBackup s = backupPayloadDecoder.decodeBackupPayload(secretKey, backupPayload); + if (s.getVersion() > highestVersion) { + socialBackup = s; + highestVersion = s.getVersion(); + } + } + return highestVersion; } public SocialBackup getSocialBackup() { From 2e03967519c437c4aa23707c960b59f4947cc1e6 Mon Sep 17 00:00:00 2001 From: ameba23 Date: Thu, 22 Apr 2021 09:01:44 +0200 Subject: [PATCH 07/25] Fix dependency problems after refactor --- briar-android/src/main/AndroidManifest.xml | 8 ++++++++ .../socialbackup/recover/OwnerReturnShardActivity.java | 10 ++++++++++ briar-android/src/main/res/values/strings.xml | 2 +- .../briar/api/socialbackup/ContactData.java | 4 ++-- .../briar/api/socialbackup/SocialBackup.java | 2 +- .../briar/socialbackup/BackupPayloadEncoder.java | 6 ++++-- .../briar/socialbackup/SocialBackupManagerImpl.java | 9 +++++---- .../socialbackup/recovery/RestoreAccountImpl.java | 2 +- 8 files changed, 32 insertions(+), 11 deletions(-) diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index 7596c8532..5f0972bdc 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -174,6 +174,14 @@ android:value="org.briarproject.briar.android.account.NewOrRecoverActivity" /> + + + Social Backup - Old Social Backup + Restore Account You have received a social backup shard. diff --git a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/ContactData.java b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/ContactData.java index 7d83a6eaa..d1c846390 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/ContactData.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/ContactData.java @@ -12,14 +12,14 @@ import javax.annotation.concurrent.Immutable; @Immutable @NotNullByDefault -class ContactData { +public class ContactData { private final Contact contact; private final Map properties; @Nullable private final Shard shard; - ContactData(Contact contact, + public ContactData(Contact contact, Map properties, @Nullable Shard shard) { this.contact = contact; diff --git a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/SocialBackup.java b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/SocialBackup.java index 76cfa6f05..1be5a0822 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/SocialBackup.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/SocialBackup.java @@ -9,7 +9,7 @@ public class SocialBackup { private List contacts; private int version; - SocialBackup (Identity identity, List contacts, int version) { + public SocialBackup (Identity identity, List contacts, int version) { this.identity = identity; this.contacts = contacts; this.version = version; diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadEncoder.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadEncoder.java index 7019661c9..ed8e852b2 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadEncoder.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/BackupPayloadEncoder.java @@ -3,12 +3,14 @@ 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.briar.api.socialbackup.BackupPayload; +import org.briarproject.briar.api.socialbackup.ContactData; import java.util.List; @NotNullByDefault interface BackupPayloadEncoder { - org.briarproject.briar.api.socialbackup.BackupPayload encodeBackupPayload(SecretKey secret, Identity identity, - List contactData, int version); + BackupPayload encodeBackupPayload(SecretKey secret, Identity identity, + List contactData, int version); } diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupManagerImpl.java index 3ebc187f9..cadf7b29e 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupManagerImpl.java @@ -52,6 +52,7 @@ import org.briarproject.briar.api.socialbackup.ShardMessageHeader; import org.briarproject.briar.api.socialbackup.ShardReceivedEvent; import org.briarproject.briar.api.socialbackup.SocialBackupManager; import org.briarproject.briar.client.ConversationClientImpl; +import org.briarproject.briar.api.socialbackup.ContactData; import java.util.ArrayList; import java.util.Collection; @@ -193,12 +194,12 @@ class SocialBackupManagerImpl extends ConversationClientImpl // Add the shard to our backup, if any if (localBackupExists(txn)) { Shard shard = messageParser.parseShardMessage(body); - List contactData = loadContactData(txn); - ListIterator it = contactData.listIterator(); + List contactData = loadContactData(txn); + ListIterator it = contactData.listIterator(); while (it.hasNext()) { - org.briarproject.briar.api.socialbackup.ContactData cd = it.next(); + ContactData cd = it.next(); if (cd.getContact().getId().equals(contactId)) { - it.set(new org.briarproject.briar.api.socialbackup.ContactData(cd.getContact(), + it.set(new ContactData(cd.getContact(), cd.getProperties(), shard)); updateBackup(txn, contactData); break; diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java index 9c6db68de..fb39f7f42 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java @@ -61,7 +61,7 @@ public class RestoreAccountImpl implements RestoreAccount { public int recover() throws FormatException, GeneralSecurityException { if (secretKey == null) throw new GeneralSecurityException(); // Find backup with highest version number - int highestVersion = 0; + int highestVersion = -1; for (ReturnShardPayload returnShardPayload : recoveredShards) { BackupPayload backupPayload = returnShardPayload.getBackupPayload(); SocialBackup s = backupPayloadDecoder.decodeBackupPayload(secretKey, backupPayload); From 41c13f80f40b963c9d3c6eb7ecd5b63a06c54d4a Mon Sep 17 00:00:00 2001 From: ameba23 Date: Thu, 22 Apr 2021 10:56:20 +0200 Subject: [PATCH 08/25] Copy code from SetPassword fragment to avoid it using the wrong view model --- .../briar/android/account/SetupViewModel.java | 1 + .../android/activity/ActivityComponent.java | 3 + .../recover/OwnerReturnShardModule.java | 6 + .../recover/RestoreAccountActivity.java | 7 +- .../recover/RestoreAccountFragment.java | 91 ++++++++++++ .../RestoreAccountSetPasswordFragment.java | 131 ++++++++++++++++++ 6 files changed, 235 insertions(+), 4 deletions(-) create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountFragment.java create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountSetPasswordFragment.java diff --git a/briar-android/src/main/java/org/briarproject/briar/android/account/SetupViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/account/SetupViewModel.java index 322d4fd24..845075e8d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/account/SetupViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/SetupViewModel.java @@ -29,6 +29,7 @@ import static org.briarproject.briar.android.account.SetupViewModel.State.SET_PA @MethodsNotNullByDefault @ParametersNotNullByDefault +public class SetupViewModel extends AndroidViewModel { enum State {AUTHOR_NAME, SET_PASSWORD, DOZE, CREATED, FAILED} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index b97ee15ff..a3b587a0d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -95,6 +95,7 @@ import org.briarproject.briar.android.socialbackup.ShardsSentFragment; import org.briarproject.briar.android.socialbackup.ThresholdSelectorFragment; import org.briarproject.briar.android.socialbackup.creation.CreateBackupModule; import org.briarproject.briar.android.socialbackup.recover.RestoreAccountActivity; +import org.briarproject.briar.android.socialbackup.recover.RestoreAccountSetPasswordFragment; import org.briarproject.briar.android.splash.SplashScreenActivity; import org.briarproject.briar.android.test.TestDataActivity; import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; @@ -291,4 +292,6 @@ public interface ActivityComponent { void inject(OwnerReturnShardFragment ownerReturnShardFragment); void inject(CustodianReturnShardSuccessFragment custodianReturnShardSuccessFragment); + + void inject(RestoreAccountSetPasswordFragment restoreAccountSetPasswordFragment); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardModule.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardModule.java index b2f2da6e4..a68e587a6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardModule.java @@ -1,6 +1,7 @@ package org.briarproject.briar.android.socialbackup.recover; import org.briarproject.briar.android.viewmodel.ViewModelKey; +import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; import androidx.lifecycle.ViewModel; import dagger.Binds; @@ -17,4 +18,9 @@ public abstract class OwnerReturnShardModule { abstract ViewModel bindOwnerReturnShardViewModel( OwnerReturnShardViewModel ownerReturnShardViewModel); + @Binds + @IntoMap + @ViewModelKey(RestoreAccountViewModel.class) + abstract ViewModel bindRestoreAccountViewModel( + RestoreAccountViewModel restoreAccountViewModel); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java index 5031e0855..e63ed26ae 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java @@ -8,7 +8,6 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; import org.briarproject.briar.android.account.DozeFragment; -import org.briarproject.briar.android.account.SetPasswordFragment; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BaseActivity; import org.briarproject.briar.android.fragment.BaseFragment; @@ -50,10 +49,10 @@ public class RestoreAccountActivity extends BaseActivity } private void onStateChanged(RestoreAccountViewModel.State state) { - if (state == RestoreAccountViewModel.State.SET_PASSWORD) { - showInitialFragment(SetPasswordFragment.newInstance()); + if (state == State.SET_PASSWORD) { + showInitialFragment(RestoreAccountSetPasswordFragment.newInstance()); } else if (state == State.DOZE) { - showDozeFragment(); +// showDozeFragment(); } else if (state == State.CREATED || state == State.FAILED) { // TODO: Show an error if failed showApp(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountFragment.java new file mode 100644 index 000000000..329df7ee2 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountFragment.java @@ -0,0 +1,91 @@ +package org.briarproject.briar.android.socialbackup.recover; + +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; + +import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.briar.R; +import org.briarproject.briar.android.fragment.BaseFragment; + +import javax.inject.Inject; + +import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; + +import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE; +import static android.view.inputmethod.EditorInfo.IME_ACTION_NEXT; +import static org.briarproject.briar.android.util.UiUtils.enterPressed; +import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +abstract class RestoreAccountFragment extends BaseFragment implements TextWatcher, + TextView.OnEditorActionListener, View.OnClickListener { + + private final static String STATE_KEY_CLICKED = "setupFragmentClicked"; + + @Inject + ViewModelProvider.Factory viewModelFactory; + RestoreAccountViewModel viewModel; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + viewModel = new ViewModelProvider(requireActivity()) + .get(RestoreAccountViewModel.class); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.help_action, menu); + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.action_help) { + showOnboardingDialog(getContext(), getHelpText()); + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + protected abstract String getHelpText(); + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, + int after) { + // noop + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, + int count) { + // noop + } + + @Override + public boolean onEditorAction(TextView textView, int actionId, + @Nullable KeyEvent keyEvent) { + if (actionId == IME_ACTION_NEXT || actionId == IME_ACTION_DONE || + enterPressed(actionId, keyEvent)) { + onClick(textView); + return true; + } + return false; + } + + @Override + public void afterTextChanged(Editable editable) { + // noop + } +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountSetPasswordFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountSetPasswordFragment.java new file mode 100644 index 000000000..c75b3bbed --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountSetPasswordFragment.java @@ -0,0 +1,131 @@ +package org.briarproject.briar.android.socialbackup.recover; + +import android.os.Bundle; +import android.os.IBinder; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.ProgressBar; + +import com.google.android.material.textfield.TextInputEditText; +import com.google.android.material.textfield.TextInputLayout; + +import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.briar.R; +import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.login.StrengthMeter; + +import javax.annotation.Nullable; + +import static android.content.Context.INPUT_METHOD_SERVICE; +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; +import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE; +import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK; +import static org.briarproject.briar.android.util.UiUtils.setError; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +public class RestoreAccountSetPasswordFragment extends RestoreAccountFragment { + private final static String TAG = RestoreAccountSetPasswordFragment.class.getName(); + + private TextInputLayout passwordEntryWrapper; + private TextInputLayout passwordConfirmationWrapper; + private TextInputEditText passwordEntry; + private TextInputEditText passwordConfirmation; + private StrengthMeter strengthMeter; + private Button nextButton; + + public static RestoreAccountSetPasswordFragment newInstance() { + return new RestoreAccountSetPasswordFragment(); + } + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + } + + @Override + public View onCreateView(LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + requireActivity().setTitle(getString(R.string.setup_password_intro)); + View v = inflater.inflate(R.layout.fragment_setup_password, container, + false); + + strengthMeter = v.findViewById(R.id.strength_meter); + passwordEntryWrapper = v.findViewById(R.id.password_entry_wrapper); + passwordEntry = v.findViewById(R.id.password_entry); + passwordConfirmationWrapper = + v.findViewById(R.id.password_confirm_wrapper); + passwordConfirmation = v.findViewById(R.id.password_confirm); + nextButton = v.findViewById(R.id.next); + ProgressBar progressBar = v.findViewById(R.id.progress); + + passwordEntry.addTextChangedListener(this); + passwordConfirmation.addTextChangedListener(this); + nextButton.setOnClickListener(this); + + if (!viewModel.needToShowDozeFragment()) { + nextButton.setText(R.string.create_account_button); + passwordConfirmation.setImeOptions(IME_ACTION_DONE); + } + + viewModel.getIsCreatingAccount() + .observe(getViewLifecycleOwner(), isCreatingAccount -> { + if (isCreatingAccount) { + nextButton.setVisibility(INVISIBLE); + progressBar.setVisibility(VISIBLE); + // this also avoids the keyboard popping up + passwordEntry.setFocusable(false); + passwordConfirmation.setFocusable(false); + } + }); + + return v; + } + + @Override + public String getUniqueTag() { + return TAG; + } + + @Override + protected String getHelpText() { + return getString(R.string.setup_password_explanation); + } + + @Override + public void onTextChanged(CharSequence authorName, int i, int i1, int i2) { + String password1 = passwordEntry.getText().toString(); + String password2 = passwordConfirmation.getText().toString(); + boolean passwordsMatch = password1.equals(password2); + + strengthMeter + .setVisibility(password1.length() > 0 ? VISIBLE : INVISIBLE); + float strength = viewModel.estimatePasswordStrength(password1); + strengthMeter.setStrength(strength); + boolean strongEnough = strength >= QUITE_WEAK; + + setError(passwordEntryWrapper, getString(R.string.password_too_weak), + password1.length() > 0 && !strongEnough); + setError(passwordConfirmationWrapper, + getString(R.string.passwords_do_not_match), + password2.length() > 0 && !passwordsMatch); + + boolean enabled = passwordsMatch && strongEnough; + nextButton.setEnabled(enabled); + passwordConfirmation.setOnEditorActionListener(enabled ? this : null); + } + + @Override + public void onClick(View view) { + IBinder token = passwordEntry.getWindowToken(); + Object o = getContext().getSystemService(INPUT_METHOD_SERVICE); + ((InputMethodManager) o).hideSoftInputFromWindow(token, 0); + viewModel.setPassword(passwordEntry.getText().toString()); + } +} From 3ac7d71f8a7bef133e1428496abbaf9facbef2aa Mon Sep 17 00:00:00 2001 From: ameba23 Date: Thu, 22 Apr 2021 11:04:24 +0200 Subject: [PATCH 09/25] Change to fail state if no backup found --- .../socialbackup/recover/RestoreAccountViewModel.java | 8 +++++++- .../briar/api/socialbackup/recovery/RestoreAccount.java | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java index b4cb07902..d7708b225 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java @@ -11,6 +11,7 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.android.account.DozeHelper; import org.briarproject.briar.android.viewmodel.LiveEvent; import org.briarproject.briar.android.viewmodel.MutableLiveEvent; +import org.briarproject.briar.api.socialbackup.SocialBackup; import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; import java.util.concurrent.Executor; @@ -107,7 +108,12 @@ class RestoreAccountViewModel extends AndroidViewModel { // if (authorName == null) throw new IllegalStateException(); if (password == null) throw new IllegalStateException(); isCreatingAccount.setValue(true); - Identity identity = restoreAccount.getSocialBackup().getIdentity(); + SocialBackup socialBackup = restoreAccount.getSocialBackup(); + if (socialBackup == null) { + LOG.warning("Cannot retrieve social backup"); + state.postEvent(State.FAILED); + } + Identity identity = socialBackup.getIdentity(); ioExecutor.execute(() -> { if (accountManager.restoreAccount(identity, password)) { LOG.info("Restore account"); diff --git a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java index 9cfbc85ae..32b5e5030 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java @@ -7,9 +7,14 @@ import org.briarproject.briar.api.socialbackup.SocialBackup; import java.security.GeneralSecurityException; public interface RestoreAccount { + int getNumberOfShards(); - boolean addReturnShardPayload(ReturnShardPayload toadd); + + boolean addReturnShardPayload(ReturnShardPayload toAdd); + boolean canRecover(); + int recover() throws FormatException, GeneralSecurityException; + SocialBackup getSocialBackup(); } From 60c6c6a7aea202ba8c51cca6ba6c37060ca68f58 Mon Sep 17 00:00:00 2001 From: ameba23 Date: Thu, 22 Apr 2021 11:55:20 +0200 Subject: [PATCH 10/25] RestoreAccount should be injected as singleton --- .../org/briarproject/briar/socialbackup/SocialBackupModule.java | 1 + 1 file changed, 1 insertion(+) diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupModule.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupModule.java index 9a9c3adf8..086f9322e 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupModule.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/SocialBackupModule.java @@ -123,6 +123,7 @@ public class SocialBackupModule { } @Provides + @Singleton RestoreAccount restoreAccount(RestoreAccountImpl restoreAccount) { return restoreAccount; } From b1c6c602a6b7e212dd53313c0631cfb5661df070 Mon Sep 17 00:00:00 2001 From: ameba23 Date: Thu, 22 Apr 2021 17:41:58 +0200 Subject: [PATCH 11/25] Add DozeView --- .../briar/android/account/DozeView.java | 2 +- .../briar/android/account/HuaweiView.java | 2 +- .../briar/android/account/PowerView.java | 4 +- .../android/activity/ActivityComponent.java | 3 + .../recover/RestoreAccountActivity.java | 2 +- .../recover/RestoreAccountDozeFragment.java | 118 ++++++++++++++++++ .../recover/RestoreAccountViewModel.java | 26 ++-- 7 files changed, 145 insertions(+), 12 deletions(-) create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountDozeFragment.java diff --git a/briar-android/src/main/java/org/briarproject/briar/android/account/DozeView.java b/briar-android/src/main/java/org/briarproject/briar/android/account/DozeView.java index c036f657b..17d2ba78f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/account/DozeView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/DozeView.java @@ -14,7 +14,7 @@ import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting; @UiThread @NotNullByDefault -class DozeView extends PowerView { +public class DozeView extends PowerView { @Nullable private Runnable onButtonClickListener; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/account/HuaweiView.java b/briar-android/src/main/java/org/briarproject/briar/android/account/HuaweiView.java index 54b4f4831..c120a305d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/account/HuaweiView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/HuaweiView.java @@ -21,7 +21,7 @@ import static android.os.Build.VERSION.SDK_INT; @UiThread @NotNullByDefault -class HuaweiView extends PowerView { +public class HuaweiView extends PowerView { private final static String PACKAGE_NAME = "com.huawei.systemmanager"; private final static String CLASS_NAME = diff --git a/briar-android/src/main/java/org/briarproject/briar/android/account/PowerView.java b/briar-android/src/main/java/org/briarproject/briar/android/account/PowerView.java index 4fd4d167c..37d34d0f7 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/account/PowerView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/PowerView.java @@ -24,7 +24,7 @@ import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog; @UiThread @NotNullByDefault -abstract class PowerView extends ConstraintLayout { +public abstract class PowerView extends ConstraintLayout { private final TextView textView; private final ImageView checkImage; @@ -156,7 +156,7 @@ abstract class PowerView extends ConstraintLayout { }; } - interface OnCheckedChangedListener { + public interface OnCheckedChangedListener { void onCheckedChanged(); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index a3b587a0d..f441e651b 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -95,6 +95,7 @@ import org.briarproject.briar.android.socialbackup.ShardsSentFragment; import org.briarproject.briar.android.socialbackup.ThresholdSelectorFragment; import org.briarproject.briar.android.socialbackup.creation.CreateBackupModule; import org.briarproject.briar.android.socialbackup.recover.RestoreAccountActivity; +import org.briarproject.briar.android.socialbackup.recover.RestoreAccountDozeFragment; import org.briarproject.briar.android.socialbackup.recover.RestoreAccountSetPasswordFragment; import org.briarproject.briar.android.splash.SplashScreenActivity; import org.briarproject.briar.android.test.TestDataActivity; @@ -294,4 +295,6 @@ public interface ActivityComponent { void inject(CustodianReturnShardSuccessFragment custodianReturnShardSuccessFragment); void inject(RestoreAccountSetPasswordFragment restoreAccountSetPasswordFragment); + + void inject(RestoreAccountDozeFragment restoreAccountDozeFragment); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java index e63ed26ae..afdaccb7d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java @@ -52,7 +52,7 @@ public class RestoreAccountActivity extends BaseActivity if (state == State.SET_PASSWORD) { showInitialFragment(RestoreAccountSetPasswordFragment.newInstance()); } else if (state == State.DOZE) { -// showDozeFragment(); + showDozeFragment(); } else if (state == State.CREATED || state == State.FAILED) { // TODO: Show an error if failed showApp(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountDozeFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountDozeFragment.java new file mode 100644 index 000000000..719a56470 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountDozeFragment.java @@ -0,0 +1,118 @@ +package org.briarproject.briar.android.socialbackup.recover; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ProgressBar; + +import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.briar.R; +import org.briarproject.briar.android.account.DozeView; +import org.briarproject.briar.android.account.HuaweiView; +import org.briarproject.briar.android.account.PowerView; +import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.util.UiUtils; + +import androidx.annotation.Nullable; + +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; +import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING; +import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +public class RestoreAccountDozeFragment extends RestoreAccountFragment + implements PowerView.OnCheckedChangedListener { + + private final static String TAG = org.briarproject.briar.android.account.DozeFragment.class.getName(); + + private DozeView dozeView; + private HuaweiView huaweiView; + private Button next; + private boolean secondAttempt = false; + + public static RestoreAccountDozeFragment newInstance() { + return new RestoreAccountDozeFragment(); + } + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + } + + @Override + public View onCreateView(LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + requireActivity().setTitle(getString(R.string.setup_doze_title)); + setHasOptionsMenu(false); + View v = inflater.inflate(R.layout.fragment_setup_doze, container, + false); + dozeView = v.findViewById(R.id.dozeView); + dozeView.setOnCheckedChangedListener(this); + huaweiView = v.findViewById(R.id.huaweiView); + huaweiView.setOnCheckedChangedListener(this); + next = v.findViewById(R.id.next); + ProgressBar progressBar = v.findViewById(R.id.progress); + + dozeView.setOnButtonClickListener(this::askForDozeWhitelisting); + next.setOnClickListener(this); + + viewModel.getIsCreatingAccount() + .observe(getViewLifecycleOwner(), isCreatingAccount -> { + if (isCreatingAccount) { + next.setVisibility(INVISIBLE); + progressBar.setVisibility(VISIBLE); + } + }); + + return v; + } + + @Override + public String getUniqueTag() { + return TAG; + } + + @Override + protected String getHelpText() { + return getString(R.string.setup_doze_explanation); + } + + @Override + public void onActivityResult(int request, int result, + @Nullable Intent data) { + super.onActivityResult(request, result, data); + if (request == REQUEST_DOZE_WHITELISTING) { + if (!dozeView.needsToBeShown() || secondAttempt) { + dozeView.setChecked(true); + } else if (getContext() != null) { + secondAttempt = true; + showOnboardingDialog(getContext(), getHelpText()); + } + } + } + + @Override + public void onCheckedChanged() { + next.setEnabled(dozeView.isChecked() && huaweiView.isChecked()); + } + + @SuppressLint("BatteryLife") + private void askForDozeWhitelisting() { + if (getContext() == null) return; + Intent i = UiUtils.getDozeWhitelistingIntent(getContext()); + startActivityForResult(i, REQUEST_DOZE_WHITELISTING); + } + + @Override + public void onClick(View view) { + viewModel.dozeExceptionConfirmed(); + } +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java index d7708b225..7f2b3c2f5 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java @@ -3,7 +3,9 @@ package org.briarproject.briar.android.socialbackup.recover; import android.app.Application; import org.briarproject.bramble.api.account.AccountManager; +import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator; +import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; @@ -42,6 +44,7 @@ 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; @@ -50,12 +53,14 @@ class RestoreAccountViewModel extends AndroidViewModel { @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; @@ -78,11 +83,6 @@ class RestoreAccountViewModel extends AndroidViewModel { return isCreatingAccount; } -// void setAuthorName(String authorName) { -// this.authorName = authorName; -// state.setEvent(SET_PASSWORD); -// } - void setPassword(String password) { this.password = password; if (needToShowDozeFragment()) { @@ -105,7 +105,6 @@ class RestoreAccountViewModel extends AndroidViewModel { } private void createAccount() { -// if (authorName == null) throw new IllegalStateException(); if (password == null) throw new IllegalStateException(); isCreatingAccount.setValue(true); SocialBackup socialBackup = restoreAccount.getSocialBackup(); @@ -116,7 +115,20 @@ class RestoreAccountViewModel extends AndroidViewModel { Identity identity = socialBackup.getIdentity(); ioExecutor.execute(() -> { if (accountManager.restoreAccount(identity, password)) { - LOG.info("Restore account"); + LOG.info("Restored account"); + + try { + restoreAccount.addContactsToDb(); + } catch (InterruptedException e) { + LOG.warning("Process interrupted when waiting for db to open"); + state.postEvent(State.FAILED); + return; + } catch (DbException e) { + LOG.warning("DbException when adding contacts"); + state.postEvent(State.FAILED); + return; + } + LOG.info("Added recovered contacts to database"); state.postEvent(State.CREATED); } else { LOG.warning("Failed to create account"); From 07141b688ace4e01a0193e6923029ae3e6d5e10d Mon Sep 17 00:00:00 2001 From: ameba23 Date: Thu, 22 Apr 2021 17:42:33 +0200 Subject: [PATCH 12/25] Add a method to add recovered contacts to db --- .../socialbackup/recovery/RestoreAccount.java | 3 ++ .../recovery/RestoreAccountImpl.java | 35 ++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java index 32b5e5030..45e2e983f 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java @@ -1,6 +1,7 @@ package org.briarproject.briar.api.socialbackup.recovery; import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.db.DbException; import org.briarproject.briar.api.socialbackup.ReturnShardPayload; import org.briarproject.briar.api.socialbackup.SocialBackup; @@ -17,4 +18,6 @@ public interface RestoreAccount { int recover() throws FormatException, GeneralSecurityException; SocialBackup getSocialBackup(); + + void addContactsToDb() throws InterruptedException, DbException; } diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java index fb39f7f42..25f0d351f 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java @@ -1,14 +1,19 @@ package org.briarproject.briar.socialbackup.recovery; import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.db.DatabaseComponent; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.lifecycle.LifecycleManager; 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.ReturnShardPayload; import org.briarproject.briar.api.socialbackup.Shard; +import org.briarproject.briar.api.socialbackup.SocialBackup; import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; import org.briarproject.briar.socialbackup.BackupPayloadDecoder; -import org.briarproject.briar.api.socialbackup.SocialBackup; import java.security.GeneralSecurityException; import java.util.ArrayList; @@ -18,17 +23,25 @@ import javax.inject.Inject; public class RestoreAccountImpl implements RestoreAccount { private ArrayList recoveredShards = new ArrayList<>(); private final DarkCrystal darkCrystal; + private final DatabaseComponent db; + private final LifecycleManager lifecycleManager; private SecretKey secretKey; private final BackupPayloadDecoder backupPayloadDecoder; private SocialBackup socialBackup; @Inject - RestoreAccountImpl(DarkCrystal darkCrystal, BackupPayloadDecoder backupPayloadDecoder) { + RestoreAccountImpl(DarkCrystal darkCrystal, + BackupPayloadDecoder backupPayloadDecoder, DatabaseComponent db, + LifecycleManager lifecycleManager) { this.darkCrystal = darkCrystal; this.backupPayloadDecoder = backupPayloadDecoder; + this.db = db; + this.lifecycleManager = lifecycleManager; } - public int getNumberOfShards() { return recoveredShards.size(); } + public int getNumberOfShards() { + return recoveredShards.size(); + } // TODO figure out how to actually use a hash set for these objects public boolean addReturnShardPayload(ReturnShardPayload toAdd) { @@ -64,7 +77,8 @@ public class RestoreAccountImpl implements RestoreAccount { int highestVersion = -1; for (ReturnShardPayload returnShardPayload : recoveredShards) { BackupPayload backupPayload = returnShardPayload.getBackupPayload(); - SocialBackup s = backupPayloadDecoder.decodeBackupPayload(secretKey, backupPayload); + SocialBackup s = backupPayloadDecoder + .decodeBackupPayload(secretKey, backupPayload); if (s.getVersion() > highestVersion) { socialBackup = s; highestVersion = s.getVersion(); @@ -76,4 +90,17 @@ public class RestoreAccountImpl implements RestoreAccount { public SocialBackup getSocialBackup() { return socialBackup; } + + public void addContactsToDb() throws InterruptedException, DbException { + if (socialBackup == null) throw new DbException(); + // TODO maybe waitForDatabase should be in another thread + lifecycleManager.waitForDatabase(); + db.transaction(false, txn -> { + for (ContactData contactData : socialBackup.getContacts()) { + Contact c = contactData.getContact(); + db.addContact(txn, c.getAuthor(), c.getLocalAuthorId(), + c.getHandshakePublicKey(), c.isVerified()); + } + }); + } } From f5455d320b61027b10a72f1e425cbcd4cf437a34 Mon Sep 17 00:00:00 2001 From: ameba23 Date: Fri, 23 Apr 2021 12:22:18 +0200 Subject: [PATCH 13/25] bramble-core --- .../briarproject/bramble/api/contact/ContactManager.java | 4 ++++ .../recover/CustodianReturnShardErrorFragment.java | 6 ++++++ .../recover/OwnerReturnShardSuccessFragment.java | 6 ++++++ 3 files changed, 16 insertions(+) create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardErrorFragment.java create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardSuccessFragment.java diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java index 0330d443c..d1eb4e913 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java @@ -3,6 +3,7 @@ package org.briarproject.bramble.api.contact; import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.UnsupportedVersionException; +import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.DbException; @@ -48,6 +49,9 @@ public interface ContactManager { SecretKey rootKey, long timestamp, boolean alice, boolean verified, boolean active) throws DbException; + ContactId addContact(Transaction txn, Author remote, AuthorId local, + PublicKey handshake, boolean verified) throws DbException; + /** * Stores a contact associated with the given local and remote pseudonyms, * replacing the given pending contact, derives and stores handshake mode diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardErrorFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardErrorFragment.java new file mode 100644 index 000000000..c9a5f18a7 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardErrorFragment.java @@ -0,0 +1,6 @@ +package org.briarproject.briar.android.socialbackup.recover; + +import org.briarproject.briar.android.fragment.BaseFragment; + +public class CustodianReturnShardErrorFragment extends BaseFragment { +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardSuccessFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardSuccessFragment.java new file mode 100644 index 000000000..e3ccef3a7 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardSuccessFragment.java @@ -0,0 +1,6 @@ +package org.briarproject.briar.android.socialbackup.recover; + +import org.briarproject.briar.android.fragment.BaseFragment; + +public class OwnerReturnShardSuccessFragment extends BaseFragment { +} From acb9b18507b3f6be2d3879b6a8386871cf0a0bc9 Mon Sep 17 00:00:00 2001 From: ameba23 Date: Fri, 23 Apr 2021 12:23:13 +0200 Subject: [PATCH 14/25] addContact method which also takes a handshakePublicKey --- .../briarproject/bramble/contact/ContactManagerImpl.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java index 3c11e80f6..f7992aa66 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java @@ -119,6 +119,15 @@ class ContactManagerImpl implements ContactManager, EventListener { verified, active)); } + @Override + public ContactId addContact(Transaction txn, Author remote, AuthorId local, + PublicKey handshake, boolean verified) throws DbException { + ContactId c = db.addContact(txn, remote, local, handshake, verified); + Contact contact = db.getContact(txn, c); + for (ContactHook hook : hooks) hook.addingContact(txn, contact); + return c; + } + @Override public String getHandshakeLink() throws DbException { KeyPair keyPair = db.transactionWithResult(true, From e29a5f487e9465e9e6fdef40ae92d4247387522e Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 23 Apr 2021 12:17:56 +0100 Subject: [PATCH 15/25] WIP: Add handshake support for outgoing connections. --- .../bramble/api/contact/HandshakeManager.java | 13 +++ .../bramble/api/transport/KeyManager.java | 14 +++ .../connection/ConnectionManagerImpl.java | 2 +- .../OutgoingDuplexSyncConnection.java | 90 +++++++++++++++++-- .../bramble/contact/HandshakeManagerImpl.java | 23 +++++ .../bramble/transport/KeyManagerImpl.java | 16 +++- 6 files changed, 146 insertions(+), 12 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/HandshakeManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/HandshakeManager.java index 1f586ada5..538d775c9 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/HandshakeManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/HandshakeManager.java @@ -24,6 +24,19 @@ public interface HandshakeManager { HandshakeResult handshake(PendingContactId p, InputStream in, StreamWriter out) throws DbException, IOException; + /** + * Handshakes with the given contact. Returns an ephemeral master key + * authenticated with both parties' handshake key pairs and a flag + * indicating whether the local peer is Alice or Bob. + * + * @param in An incoming stream for the handshake, which must be secured in + * handshake mode + * @param out An outgoing stream for the handshake, which must be secured + * in handshake mode + */ + HandshakeResult handshake(ContactId c, InputStream in, StreamWriter out) + throws DbException, IOException; + class HandshakeResult { private final SecretKey masterKey; diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java index 50f7d8aa0..b23bbb96a 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java @@ -35,6 +35,20 @@ public interface KeyManager { ContactId c, SecretKey rootKey, long timestamp, boolean alice, boolean active) throws DbException; + /** + * Derives and stores a set of rotation mode transport keys for + * communicating with the given contact over each transport and returns the + * key set IDs. + *

+ * {@link StreamContext StreamContexts} for the contact can be created + * after this method has returned. + * + * @param alice True if the local party is Alice + * @param active Whether the derived keys can be used for outgoing streams + */ + Map addRotationKeys(ContactId c, SecretKey rootKey, + long timestamp, boolean alice, boolean active) throws DbException; + /** * Informs the key manager that a new contact has been added. Derives and * stores a set of handshake mode transport keys for communicating with the diff --git a/bramble-core/src/main/java/org/briarproject/bramble/connection/ConnectionManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/connection/ConnectionManagerImpl.java index 2a50033b1..08542cac7 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/connection/ConnectionManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/connection/ConnectionManagerImpl.java @@ -101,7 +101,7 @@ class ConnectionManagerImpl implements ConnectionManager { ioExecutor.execute(new OutgoingDuplexSyncConnection(keyManager, connectionRegistry, streamReaderFactory, streamWriterFactory, syncSessionFactory, transportPropertyManager, ioExecutor, - secureRandom, c, t, d)); + secureRandom, handshakeManager, c, t, d)); } @Override diff --git a/bramble-core/src/main/java/org/briarproject/bramble/connection/OutgoingDuplexSyncConnection.java b/bramble-core/src/main/java/org/briarproject/bramble/connection/OutgoingDuplexSyncConnection.java index d7f777b7b..9ffb974c6 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/connection/OutgoingDuplexSyncConnection.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/connection/OutgoingDuplexSyncConnection.java @@ -2,6 +2,7 @@ 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.db.DbException; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; @@ -14,9 +15,11 @@ import org.briarproject.bramble.api.sync.SyncSessionFactory; import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamReaderFactory; +import org.briarproject.bramble.api.transport.StreamWriter; import org.briarproject.bramble.api.transport.StreamWriterFactory; import java.io.IOException; +import java.io.InputStream; import java.security.SecureRandom; import java.util.concurrent.Executor; @@ -28,21 +31,31 @@ import static org.briarproject.bramble.util.LogUtils.logException; class OutgoingDuplexSyncConnection extends DuplexSyncConnection implements Runnable { + // FIXME: Exchange timestamp as part of handshake protocol? + private static final long TIMESTAMP = 1617235200; // 1 April 2021 00:00 UTC + private final SecureRandom secureRandom; + private final HandshakeManager handshakeManager; private final ContactId contactId; - OutgoingDuplexSyncConnection(KeyManager keyManager, + OutgoingDuplexSyncConnection( + KeyManager keyManager, ConnectionRegistry connectionRegistry, StreamReaderFactory streamReaderFactory, StreamWriterFactory streamWriterFactory, SyncSessionFactory syncSessionFactory, TransportPropertyManager transportPropertyManager, - Executor ioExecutor, SecureRandom secureRandom, ContactId contactId, - TransportId transportId, DuplexTransportConnection connection) { + Executor ioExecutor, + SecureRandom secureRandom, + HandshakeManager handshakeManager, + ContactId contactId, + TransportId transportId, + DuplexTransportConnection connection) { super(keyManager, connectionRegistry, streamReaderFactory, streamWriterFactory, syncSessionFactory, transportPropertyManager, ioExecutor, transportId, connection); this.secureRandom = secureRandom; + this.handshakeManager = handshakeManager; this.contactId = contactId; } @@ -56,10 +69,22 @@ class OutgoingDuplexSyncConnection extends DuplexSyncConnection return; } if (ctx.isHandshakeMode()) { - // TODO: Support handshake mode for contacts - LOG.warning("Cannot use handshake mode stream context"); - onWriteError(); - return; + if (!performHandshake(ctx)) { + LOG.warning("Handshake failed"); + return; + } + // Allocate a rotation mode stream context + ctx = allocateStreamContext(contactId, transportId); + if (ctx == null) { + LOG.warning("Could not allocate stream context"); + onWriteError(); + return; + } + if (ctx.isHandshakeMode()) { + LOG.warning("Got handshake mode context after handshaking"); + onWriteError(); + return; + } } // Start the incoming session on another thread Priority priority = generatePriority(); @@ -127,6 +152,57 @@ class OutgoingDuplexSyncConnection extends DuplexSyncConnection } } + private boolean performHandshake(StreamContext ctxOut) { + // Flush the output stream to send the outgoing stream header + StreamWriter out; + try { + out = streamWriterFactory.createStreamWriter( + writer.getOutputStream(), ctxOut); + out.getOutputStream().flush(); + } catch (IOException e) { + logException(LOG, WARNING, e); + onWriteError(); + return false; + } + // Read and recognise the tag + StreamContext ctxIn = recogniseTag(reader, transportId); + // Unrecognised tags are suspicious in this case + if (ctxIn == null) { + LOG.warning("Unrecognised tag for returning stream"); + onReadError(); + return false; + } + // Check that the stream comes from the expected contact + ContactId inContactId = ctxIn.getContactId(); + if (contactId == null) { + LOG.warning("Expected contact tag, got rendezvous tag"); + onReadError(); + return false; + } + if (!inContactId.equals(contactId)) { + LOG.warning("Wrong contact ID for returning stream"); + onReadError(); + return false; + } + // TODO: Register the connection, close it if it's redundant + // Handshake and exchange contacts + try { + InputStream in = streamReaderFactory.createStreamReader( + reader.getInputStream(), ctxIn); + HandshakeManager.HandshakeResult result = + handshakeManager.handshake(contactId, in, out); + keyManager.addRotationKeys(contactId, result.getMasterKey(), + TIMESTAMP, result.isAlice(), true); + return true; + } catch (IOException | DbException e) { + logException(LOG, WARNING, e); + onWriteError(); + return false; + } finally { + // TODO: Unregister the connection + } + } + private void onReadError() { // 'Recognised' is always true for outgoing connections onReadError(true); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/HandshakeManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/HandshakeManagerImpl.java index 7edeaf786..068685ff7 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/contact/HandshakeManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/HandshakeManagerImpl.java @@ -3,6 +3,8 @@ package org.briarproject.bramble.contact; import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Predicate; +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.contact.HandshakeManager; import org.briarproject.bramble.api.contact.PendingContact; @@ -88,6 +90,27 @@ class HandshakeManagerImpl implements HandshakeManager { }); PublicKey theirStaticPublicKey = keys.getFirst(); KeyPair ourStaticKeyPair = keys.getSecond(); + return handshake(theirStaticPublicKey, ourStaticKeyPair, in, out); + } + + @Override + public HandshakeResult handshake(ContactId c, InputStream in, + StreamWriter out) throws DbException, IOException { + Pair keys = db.transactionWithResult(true, txn -> { + Contact contact = contactManager.getContact(txn, c); + PublicKey handshakePublicKey = contact.getHandshakePublicKey(); + if (handshakePublicKey == null) throw new DbException(); + KeyPair keyPair = identityManager.getHandshakeKeys(txn); + return new Pair<>(handshakePublicKey, keyPair); + }); + PublicKey theirStaticPublicKey = keys.getFirst(); + KeyPair ourStaticKeyPair = keys.getSecond(); + return handshake(theirStaticPublicKey, ourStaticKeyPair, in, out); + } + + private HandshakeResult handshake(PublicKey theirStaticPublicKey, + KeyPair ourStaticKeyPair, InputStream in, StreamWriter out) + throws IOException { boolean alice = transportCrypto.isAlice(theirStaticPublicKey, ourStaticKeyPair); RecordReader recordReader = recordReaderFactory.createRecordReader(in); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java index 58aeb7cea..d240d644f 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java @@ -101,9 +101,9 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { } @Override - public Map addRotationKeys( - Transaction txn, ContactId c, SecretKey rootKey, long timestamp, - boolean alice, boolean active) throws DbException { + public Map addRotationKeys(Transaction txn, + ContactId c, SecretKey rootKey, long timestamp, boolean alice, + boolean active) throws DbException { Map ids = new HashMap<>(); for (Entry e : managers.entrySet()) { TransportId t = e.getKey(); @@ -114,6 +114,14 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { return ids; } + @Override + public Map addRotationKeys(ContactId c, + SecretKey rootKey, long timestamp, boolean alice, boolean active) + throws DbException { + return db.transactionWithResult(false, txn -> + addRotationKeys(txn, c, rootKey, timestamp, alice, active)); + } + @Override public Map addContact(Transaction txn, ContactId c, PublicKey theirPublicKey, KeyPair ourKeyPair) @@ -137,7 +145,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener { PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair) throws DbException, GeneralSecurityException { SecretKey staticMasterKey = transportCrypto - .deriveStaticMasterKey(theirPublicKey, ourKeyPair); + .deriveStaticMasterKey(theirPublicKey, ourKeyPair); SecretKey rootKey = transportCrypto.deriveHandshakeRootKey(staticMasterKey, true); boolean alice = transportCrypto.isAlice(theirPublicKey, ourKeyPair); From 3d34a36908f9993788e41e77948ddad17d429823 Mon Sep 17 00:00:00 2001 From: ameba23 Date: Fri, 23 Apr 2021 13:18:47 +0200 Subject: [PATCH 16/25] Show success fragment on recovering account --- .../android/activity/ActivityComponent.java | 6 +++ .../recover/CustodianReturnShardActivity.java | 2 + .../CustodianReturnShardErrorFragment.java | 52 +++++++++++++++++++ .../CustodianReturnShardViewModel.java | 15 ++++++ .../recover/OwnerReturnShardActivity.java | 23 ++++---- .../OwnerReturnShardSuccessFragment.java | 48 +++++++++++++++++ .../recover/OwnerReturnShardViewModel.java | 17 +++--- .../recover/RestoreAccountActivity.java | 3 +- .../recover/RestoreAccountDozeFragment.java | 2 +- .../recover/RestoreAccountViewModel.java | 24 +++++---- 10 files changed, 161 insertions(+), 31 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index f441e651b..d0b7234b9 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -85,6 +85,7 @@ import org.briarproject.briar.android.socialbackup.CustodianSelectorFragment; import org.briarproject.briar.android.socialbackup.DistributedBackupActivity; import org.briarproject.briar.android.socialbackup.ExistingBackupFragment; import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardActivity; +import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardErrorFragment; import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardFragment; import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardSuccessFragment; import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeExplainerFragment; @@ -94,6 +95,7 @@ import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardFragm import org.briarproject.briar.android.socialbackup.ShardsSentFragment; import org.briarproject.briar.android.socialbackup.ThresholdSelectorFragment; import org.briarproject.briar.android.socialbackup.creation.CreateBackupModule; +import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardSuccessFragment; import org.briarproject.briar.android.socialbackup.recover.RestoreAccountActivity; import org.briarproject.briar.android.socialbackup.recover.RestoreAccountDozeFragment; import org.briarproject.briar.android.socialbackup.recover.RestoreAccountSetPasswordFragment; @@ -297,4 +299,8 @@ public interface ActivityComponent { void inject(RestoreAccountSetPasswordFragment restoreAccountSetPasswordFragment); void inject(RestoreAccountDozeFragment restoreAccountDozeFragment); + + void inject(OwnerReturnShardSuccessFragment ownerReturnShardSuccessFragment); + + void inject(CustodianReturnShardErrorFragment custodianReturnShardErrorFragment); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardActivity.java index 3346e1ce0..33e82536d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardActivity.java @@ -59,6 +59,8 @@ public class CustodianReturnShardActivity extends BriarActivity Toast.makeText(this, "It looks like you are not connected to a Wifi network", Toast.LENGTH_SHORT).show(); + showNextFragment(new CustodianReturnShardErrorFragment()); + return; } catch (DbException e) { Toast.makeText(this, "You do not hold a backup piece for this contact", diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardErrorFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardErrorFragment.java index c9a5f18a7..d0ae28640 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardErrorFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardErrorFragment.java @@ -1,6 +1,58 @@ package org.briarproject.briar.android.socialbackup.recover; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; + +import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.briar.R; +import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.fragment.BaseFragment; +import javax.inject.Inject; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault public class CustodianReturnShardErrorFragment extends BaseFragment { + public static final String TAG = + CustodianReturnShardErrorFragment.class.getName(); + + @Inject + ViewModelProvider.Factory viewModelFactory; + + private CustodianReturnShardViewModel viewModel; + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) + .get(CustodianReturnShardViewModel.class); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + // TODO change fragment to error + View view = inflater.inflate(R.layout.fragment_recovery_custodian_done, + container, false); + + Button button = view.findViewById(R.id.button); + button.setOnClickListener(e -> viewModel.onErrorCancelled()); + // TODO try again button + return view; + } + + @Override + public String getUniqueTag() { + return TAG; + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardViewModel.java index d3970c2ed..9c31b68d3 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/CustodianReturnShardViewModel.java @@ -58,6 +58,7 @@ public class CustodianReturnShardViewModel extends AndroidViewModel new MutableLiveEvent<>(); private final MutableLiveEvent successDismissed = new MutableLiveEvent<>(); + private final MutableLiveEvent errorTryAgain = new MutableLiveEvent<>(); private final MutableLiveData state = new MutableLiveData<>(); private final CustodianTask task; @@ -156,6 +157,16 @@ public class CustodianReturnShardViewModel extends AndroidViewModel showCameraFragment.setEvent(true); } + @UiThread + public void onErrorCancelled() { + errorTryAgain.postEvent(false); + } + + @UiThread + public void onErrorTryAgain() { + errorTryAgain.postEvent(true); + } + @UiThread public void onSuccessDismissed() { successDismissed.setEvent(true); @@ -186,4 +197,8 @@ public class CustodianReturnShardViewModel extends AndroidViewModel qrCodeRead = true; } } + + public MutableLiveEvent getErrorTryAgain() { + return errorTryAgain; + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardActivity.java index cc6792b5a..4ac3ad86a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardActivity.java @@ -77,6 +77,9 @@ public class OwnerReturnShardActivity extends BaseActivity showNextFragment(new OwnerRecoveryModeMainFragment()); } }); + viewModel.getSuccessDismissed().observeEvent(this, success -> { + if (success) onSuccessDismissed(); + }); viewModel.getState() .observe(this, this::onReturnShardStateChanged); } @@ -125,6 +128,13 @@ public class OwnerReturnShardActivity extends BaseActivity } } + private void onSuccessDismissed() { + finish(); + Intent i = new Intent(this, RestoreAccountActivity.class); + i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP | + FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_TASK_ON_HOME); + startActivity(i); + } private void onReturnShardStateChanged(SecretOwnerTask.State state) { if (state instanceof SecretOwnerTask.State.Success) { @@ -136,9 +146,8 @@ public class OwnerReturnShardActivity extends BaseActivity Toast.LENGTH_SHORT).show(); if (added && viewModel.canRecover()) { LOG.info("Secret key recovered"); - int version = 0; try { - version = viewModel.recover(); + viewModel.recover(); } catch (GeneralSecurityException e) { LOG.warning("Unable to decrypt backup" + e.toString()); Toast.makeText(this, @@ -153,15 +162,7 @@ public class OwnerReturnShardActivity extends BaseActivity Toast.LENGTH_LONG).show(); return; } - Toast.makeText(this, - "Account recovered! " + version, - Toast.LENGTH_LONG).show(); - finish(); - // TODO Success fragment - Intent i = new Intent(this, RestoreAccountActivity.class); - i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP | - FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_TASK_ON_HOME); - startActivity(i); + showNextFragment(new OwnerReturnShardSuccessFragment()); return; } onBackPressed(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardSuccessFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardSuccessFragment.java index e3ccef3a7..92c2ba378 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardSuccessFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardSuccessFragment.java @@ -1,6 +1,54 @@ package org.briarproject.briar.android.socialbackup.recover; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; + +import org.briarproject.briar.R; +import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.fragment.BaseFragment; +import javax.inject.Inject; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; + public class OwnerReturnShardSuccessFragment extends BaseFragment { + + public static final String TAG = + OwnerReturnShardSuccessFragment.class.getName(); + + @Inject + ViewModelProvider.Factory viewModelFactory; + + private OwnerReturnShardViewModel viewModel; + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) + .get(OwnerReturnShardViewModel.class); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_recovery_custodian_done, + container, false); + + Button button = view.findViewById(R.id.button); + button.setOnClickListener(e -> viewModel.onSuccessDismissed()); + + return view; + } + + @Override + public String getUniqueTag() { + return TAG; + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardViewModel.java index e5b02dfc6..1ccb763b7 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardViewModel.java @@ -14,19 +14,14 @@ 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.BackupPayload; import org.briarproject.briar.api.socialbackup.ReturnShardPayload; -import org.briarproject.briar.api.socialbackup.Shard; import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; import org.briarproject.briar.api.socialbackup.recovery.SecretOwnerTask; -import org.briarproject.briar.socialbackup.BackupPayloadDecoder; -import org.briarproject.briar.api.socialbackup.SocialBackup; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.charset.Charset; import java.security.GeneralSecurityException; -import java.util.ArrayList; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -51,8 +46,6 @@ class OwnerReturnShardViewModel extends AndroidViewModel @SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19 private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); -// private ReturnShardPayload returnShardPayload; - private final AndroidExecutor androidExecutor; private final Executor ioExecutor; private final SecretOwnerTask task; @@ -60,6 +53,7 @@ class OwnerReturnShardViewModel extends AndroidViewModel private final MutableLiveEvent showQrCodeFragment = new MutableLiveEvent<>(); + private final MutableLiveEvent successDismissed = new MutableLiveEvent<>(); private final MutableLiveData state = new MutableLiveData<>(); private final MutableLiveEvent startClicked = @@ -128,6 +122,11 @@ class OwnerReturnShardViewModel extends AndroidViewModel startShardReturn(); } + @UiThread + void onSuccessDismissed() { + successDismissed.setEvent(true); + } + @UiThread void startShardReturn() { // If we return to the intro fragment, the continue button needs to be @@ -233,4 +232,8 @@ class OwnerReturnShardViewModel extends AndroidViewModel public int recover() throws FormatException, GeneralSecurityException { return restoreAccount.recover(); } + + public MutableLiveEvent getSuccessDismissed() { + return successDismissed; + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java index afdaccb7d..2c9a3fefe 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java @@ -7,7 +7,6 @@ import android.os.Bundle; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; -import org.briarproject.briar.android.account.DozeFragment; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BaseActivity; import org.briarproject.briar.android.fragment.BaseFragment; @@ -61,7 +60,7 @@ public class RestoreAccountActivity extends BaseActivity @TargetApi(23) void showDozeFragment() { - showNextFragment(DozeFragment.newInstance()); + showNextFragment(RestoreAccountDozeFragment.newInstance()); } void showApp() { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountDozeFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountDozeFragment.java index 719a56470..6c079646d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountDozeFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountDozeFragment.java @@ -30,7 +30,7 @@ import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog; public class RestoreAccountDozeFragment extends RestoreAccountFragment implements PowerView.OnCheckedChangedListener { - private final static String TAG = org.briarproject.briar.android.account.DozeFragment.class.getName(); + private final static String TAG = RestoreAccountDozeFragment.class.getName(); private DozeView dozeView; private HuaweiView huaweiView; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java index 7f2b3c2f5..fe610ed91 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java @@ -30,7 +30,8 @@ import static java.util.logging.Logger.getLogger; @MethodsNotNullByDefault @ParametersNotNullByDefault -class RestoreAccountViewModel extends AndroidViewModel { +class RestoreAccountViewModel extends AndroidViewModel implements RestoreAccount.Observer { + enum State {SET_PASSWORD, DOZE, CREATED, FAILED} private static final Logger LOG = @@ -116,19 +117,12 @@ class RestoreAccountViewModel extends AndroidViewModel { ioExecutor.execute(() -> { if (accountManager.restoreAccount(identity, password)) { LOG.info("Restored account"); - try { - restoreAccount.addContactsToDb(); - } catch (InterruptedException e) { - LOG.warning("Process interrupted when waiting for db to open"); - state.postEvent(State.FAILED); - return; + restoreAccount.addContactsToDb(this); } catch (DbException e) { - LOG.warning("DbException when adding contacts"); + LOG.warning("Cannot retrieve social backup"); state.postEvent(State.FAILED); - return; } - LOG.info("Added recovered contacts to database"); state.postEvent(State.CREATED); } else { LOG.warning("Failed to create account"); @@ -136,4 +130,14 @@ class RestoreAccountViewModel extends AndroidViewModel { } }); } + + @Override + public void onAddingContactsFinished(boolean success) { + if (success) { + LOG.info("Added recovered contacts to database"); + } else { + LOG.warning("Error when adding contacts to database"); +// state.postEvent(State.FAILED); + } + } } From a5f05161353674d381b5192ba04b4f0cbd6d8a65 Mon Sep 17 00:00:00 2001 From: ameba23 Date: Fri, 23 Apr 2021 13:22:10 +0200 Subject: [PATCH 17/25] Tidy RestoreAccount --- .../socialbackup/recovery/RestoreAccount.java | 3 +- .../recovery/RestoreAccountImpl.java | 50 +++++++++++++++---- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java index 45e2e983f..db56ad81d 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/socialbackup/recovery/RestoreAccount.java @@ -9,6 +9,7 @@ import java.security.GeneralSecurityException; public interface RestoreAccount { + int getNumberOfShards(); boolean addReturnShardPayload(ReturnShardPayload toAdd); @@ -19,5 +20,5 @@ public interface RestoreAccount { SocialBackup getSocialBackup(); - void addContactsToDb() throws InterruptedException, DbException; + void addContactsToDb() throws DbException; } diff --git a/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java b/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java index 25f0d351f..5b05e5d1e 100644 --- a/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/socialbackup/recovery/RestoreAccountImpl.java @@ -2,9 +2,12 @@ package org.briarproject.briar.socialbackup.recovery; import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.contact.Contact; +import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.db.DatabaseComponent; 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.briar.api.socialbackup.BackupPayload; import org.briarproject.briar.api.socialbackup.ContactData; @@ -17,26 +20,39 @@ import org.briarproject.briar.socialbackup.BackupPayloadDecoder; import java.security.GeneralSecurityException; import java.util.ArrayList; +import java.util.concurrent.Executor; +import java.util.logging.Logger; import javax.inject.Inject; +import static java.util.logging.Logger.getLogger; + public class RestoreAccountImpl implements RestoreAccount { - private ArrayList recoveredShards = new ArrayList<>(); + private final ArrayList recoveredShards = new ArrayList<>(); private final DarkCrystal darkCrystal; + private final Executor ioExecutor; private final DatabaseComponent db; + private final ContactManager contactManager; private final LifecycleManager lifecycleManager; private SecretKey secretKey; private final BackupPayloadDecoder backupPayloadDecoder; private SocialBackup socialBackup; + private static final Logger LOG = + getLogger(RestoreAccountImpl.class.getName()); + @Inject RestoreAccountImpl(DarkCrystal darkCrystal, BackupPayloadDecoder backupPayloadDecoder, DatabaseComponent db, + @IoExecutor Executor ioExecutor, + ContactManager contactManager, LifecycleManager lifecycleManager) { this.darkCrystal = darkCrystal; this.backupPayloadDecoder = backupPayloadDecoder; this.db = db; + this.ioExecutor = ioExecutor; this.lifecycleManager = lifecycleManager; + this.contactManager = contactManager; } public int getNumberOfShards() { @@ -57,7 +73,7 @@ public class RestoreAccountImpl implements RestoreAccount { } public boolean canRecover() { - ArrayList shards = new ArrayList(); + ArrayList shards = new ArrayList<>(); for (ReturnShardPayload returnShardPayload : recoveredShards) { // TODO check shards all have same secret id shards.add(returnShardPayload.getShard()); @@ -91,16 +107,30 @@ public class RestoreAccountImpl implements RestoreAccount { return socialBackup; } - public void addContactsToDb() throws InterruptedException, DbException { + public void addContactsToDb() throws DbException { if (socialBackup == null) throw new DbException(); - // TODO maybe waitForDatabase should be in another thread - lifecycleManager.waitForDatabase(); - db.transaction(false, txn -> { - for (ContactData contactData : socialBackup.getContacts()) { - Contact c = contactData.getContact(); - db.addContact(txn, c.getAuthor(), c.getLocalAuthorId(), - c.getHandshakePublicKey(), c.isVerified()); + AuthorId localAuthorId = socialBackup.getIdentity().getId(); + + ioExecutor.execute(() -> { + try { + lifecycleManager.waitForDatabase(); + } catch (InterruptedException e) { + 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()); + } + }); + } catch (DbException e) { + LOG.warning("Error adding contacts to database"); + LOG.warning(e.getMessage()); + } + LOG.info("Added all contacts"); }); } } From a6f06e328be4358e55199fd8005b9b058f7ea4ba Mon Sep 17 00:00:00 2001 From: ameba23 Date: Sun, 25 Apr 2021 12:00:44 +0200 Subject: [PATCH 18/25] Dont use an observer when adding contacts --- .../recover/RestoreAccountViewModel.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java index fe610ed91..beeaf22f4 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java @@ -30,7 +30,7 @@ import static java.util.logging.Logger.getLogger; @MethodsNotNullByDefault @ParametersNotNullByDefault -class RestoreAccountViewModel extends AndroidViewModel implements RestoreAccount.Observer { +class RestoreAccountViewModel extends AndroidViewModel { enum State {SET_PASSWORD, DOZE, CREATED, FAILED} @@ -118,7 +118,7 @@ class RestoreAccountViewModel extends AndroidViewModel implements RestoreAccount if (accountManager.restoreAccount(identity, password)) { LOG.info("Restored account"); try { - restoreAccount.addContactsToDb(this); + restoreAccount.addContactsToDb(); } catch (DbException e) { LOG.warning("Cannot retrieve social backup"); state.postEvent(State.FAILED); @@ -130,14 +130,4 @@ class RestoreAccountViewModel extends AndroidViewModel implements RestoreAccount } }); } - - @Override - public void onAddingContactsFinished(boolean success) { - if (success) { - LOG.info("Added recovered contacts to database"); - } else { - LOG.warning("Error when adding contacts to database"); -// state.postEvent(State.FAILED); - } - } } From 6af739e58465ed866ee2737ac13fa60d1baa5515 Mon Sep 17 00:00:00 2001 From: ameba23 Date: Sun, 25 Apr 2021 12:06:49 +0200 Subject: [PATCH 19/25] Resolve conflict after merging with social-backup-outgoing-handshake --- .idea/codeStyles/Project.xml | 9 ---- .../connection/ConnectionManagerImpl.java | 2 +- .../connection/DuplexSyncConnection.java | 1 + .../IncomingDuplexSyncConnection.java | 46 +++++++++++++++++-- 4 files changed, 44 insertions(+), 14 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 7b9dcd72e..3d081536f 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -31,15 +31,6 @@ -