mirror of
https://codeberg.org/Mercury-IM/Mercury-IM
synced 2025-02-07 20:06:24 +01:00
Ikey backup restore works v1.0
This commit is contained in:
parent
067ea4b65b
commit
b8185b30ae
19 changed files with 444 additions and 190 deletions
|
@ -6,7 +6,11 @@ import androidx.lifecycle.LiveData;
|
|||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||
import org.mercury_im.messenger.android.MercuryImApplication;
|
||||
import org.mercury_im.messenger.android.ui.MercuryAndroidViewModel;
|
||||
import org.mercury_im.messenger.android.util.QrCodeGenerator;
|
||||
|
@ -15,6 +19,7 @@ import org.mercury_im.messenger.core.crypto.OpenPgpSecretKeyBackupPassphraseGene
|
|||
import org.mercury_im.messenger.core.util.Optional;
|
||||
import org.mercury_im.messenger.core.viewmodel.ikey.IkeySecretKeyBackupCreationViewModel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
@ -81,4 +86,11 @@ public class AndroidIkeyBackupCreationViewModel extends ViewModel implements Mer
|
|||
public IkeySecretKeyBackupCreationViewModel getCommonViewModel() {
|
||||
return commonViewModel;
|
||||
}
|
||||
|
||||
public void uploadBackup() {
|
||||
addDisposable(getCommonViewModel().createBackup()
|
||||
.compose(schedulers.executeUiSafeCompletable())
|
||||
.subscribe(() -> LOGGER.log(Level.INFO, "Successfully uploaded ikey backup."),
|
||||
e -> LOGGER.log(Level.INFO, "Error uploading ikey backup:", e)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,13 @@ import android.view.ViewGroup;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||
import org.mercury_im.messenger.R;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
@ -55,6 +60,8 @@ public class IkeyBackupCreationFragment extends Fragment {
|
|||
viewModel = new ViewModelProvider(this).get(AndroidIkeyBackupCreationViewModel.class);
|
||||
viewModel.initialize(accountId);
|
||||
|
||||
viewModel.uploadBackup();
|
||||
|
||||
viewModel.getPassphrase().observe(getViewLifecycleOwner(), passphrase -> backupCode.setText(passphrase));
|
||||
viewModel.getPassphraseAsQrCode().observe(getViewLifecycleOwner(), bitmap -> qrCode.setImageBitmap(bitmap));
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ import org.mercury_im.messenger.android.MercuryImApplication;
|
|||
import org.mercury_im.messenger.android.di.module.AndroidDatabaseModule;
|
||||
import org.mercury_im.messenger.android.di.module.AndroidSchedulersModule;
|
||||
import org.mercury_im.messenger.android.ui.account.detail.AndroidAccountDetailsViewModel;
|
||||
import org.mercury_im.messenger.android.ui.account.login.AddAccountActivity;
|
||||
import org.mercury_im.messenger.android.ui.account.login.AndroidIkeyInfoViewModel;
|
||||
import org.mercury_im.messenger.android.ui.account.login.EnterAccountDetailsFragment;
|
||||
import org.mercury_im.messenger.android.ui.account.login.IkeySetupViewModel;
|
||||
import org.mercury_im.messenger.android.ui.account.login.AndroidIkeySetupViewModel;
|
||||
import org.mercury_im.messenger.android.ui.contacts.AndroidContactListViewModel;
|
||||
import org.mercury_im.messenger.android.crypto.ikey.AndroidIkeyBackupCreationViewModel;
|
||||
import org.mercury_im.messenger.core.di.module.IkeyModule;
|
||||
|
@ -101,9 +101,11 @@ public interface AppComponent {
|
|||
|
||||
void inject(AndroidIkeyBackupCreationViewModel androidIkeyBackupCreationViewModel);
|
||||
|
||||
void inject(IkeySetupViewModel ikeySetupViewModel);
|
||||
void inject(AndroidIkeySetupViewModel androidIkeySetupViewModel);
|
||||
// void inject(AndroidOxSecretKeyBackupRestoreViewModel androidOxSecretKeyBackupRestoreViewModel);
|
||||
|
||||
void inject(AndroidIkeyInfoViewModel androidIkeyInfoViewModel);
|
||||
|
||||
|
||||
// Common VMs
|
||||
void inject(LoginViewModel loginViewModel);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.mercury_im.messenger.android.ui.account.detail;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
|
|
@ -34,7 +34,7 @@ public class AddAccountActivity extends AppCompatActivity {
|
|||
private SetupPagerAdapter pagerAdapter;
|
||||
|
||||
private AppComponent appComponent;
|
||||
private IkeySetupViewModel ikeyViewModel;
|
||||
private AndroidIkeySetupViewModel ikeyViewModel;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
|
@ -47,7 +47,7 @@ public class AddAccountActivity extends AppCompatActivity {
|
|||
viewPager.setAdapter(pagerAdapter);
|
||||
viewPager.setUserInputEnabled(false); // disable swiping
|
||||
|
||||
ikeyViewModel = new ViewModelProvider(this).get(IkeySetupViewModel.class); // shared between fragments
|
||||
ikeyViewModel = new ViewModelProvider(this).get(AndroidIkeySetupViewModel.class); // shared between fragments
|
||||
}
|
||||
|
||||
public void loginFinished(Optional<Account> optionalAccount) {
|
||||
|
@ -58,29 +58,32 @@ public class AddAccountActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
public void skipIkeySetup() {
|
||||
finish();
|
||||
}
|
||||
|
||||
public void setupIkey(UUID accountId) {
|
||||
int pos = pagerAdapter.getItemCount();
|
||||
pagerAdapter.getFragments().put(pos, IkeyBackupRestoreOrSkipFragment.newInstance(accountId));
|
||||
int nextPos = pagerAdapter.getItemCount();
|
||||
pagerAdapter.getFragments().put(nextPos, IkeyBackupRestoreOrSkipFragment.newInstance(accountId));
|
||||
pagerAdapter.notifyDataSetChanged();
|
||||
viewPager.setCurrentItem(pos);
|
||||
viewPager.setCurrentItem(nextPos);
|
||||
}
|
||||
|
||||
public void restoreIkeyBackup(UUID accountId) {
|
||||
int pos = pagerAdapter.getItemCount();
|
||||
pagerAdapter.getFragments().put(pos, IkeyBackupRestoreSuccessfulFragment.newInstance(accountId));
|
||||
public void restoreSuccessful(UUID accountId) {
|
||||
int nextPos = pagerAdapter.getItemCount();
|
||||
pagerAdapter.getFragments().put(nextPos, IkeyKeyInfoFragment.newInstance(accountId));
|
||||
pagerAdapter.notifyDataSetChanged();
|
||||
viewPager.setCurrentItem(pos);
|
||||
viewPager.setCurrentItem(nextPos);
|
||||
}
|
||||
|
||||
public void generateIkeyBackup(UUID accountId) {
|
||||
int pos = pagerAdapter.getItemCount();
|
||||
pagerAdapter.getFragments().put(pos, IkeyBackupCreationFragment.newInstance(accountId));
|
||||
int nextPos = pagerAdapter.getItemCount();
|
||||
pagerAdapter.getFragments().put(nextPos, IkeyBackupCreationFragment.newInstance(accountId));
|
||||
pagerAdapter.notifyDataSetChanged();
|
||||
viewPager.setCurrentItem(pos);
|
||||
viewPager.setCurrentItem(nextPos);
|
||||
}
|
||||
|
||||
public void displayInfo(UUID accountId) {
|
||||
int nextPos = pagerAdapter.getItemCount();
|
||||
pagerAdapter.getFragments().put(nextPos, IkeyKeyInfoFragment.newInstance(accountId));
|
||||
pagerAdapter.notifyDataSetChanged();
|
||||
viewPager.setCurrentItem(nextPos);
|
||||
}
|
||||
|
||||
private class SetupPagerAdapter extends FragmentStateAdapter {
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package org.mercury_im.messenger.android.ui.account.login;
|
||||
|
||||
import android.app.Application;
|
||||
import android.text.Spannable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import org.mercury_im.messenger.android.MercuryImApplication;
|
||||
import org.mercury_im.messenger.android.ui.MercuryAndroidViewModel;
|
||||
import org.mercury_im.messenger.android.ui.openpgp.OpenPgpV4FingerprintFormatter;
|
||||
import org.mercury_im.messenger.core.util.Optional;
|
||||
import org.mercury_im.messenger.core.viewmodel.ikey.IkeyInfoViewModel;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class AndroidIkeyInfoViewModel extends AndroidViewModel implements MercuryAndroidViewModel<IkeyInfoViewModel> {
|
||||
|
||||
private MutableLiveData<Spannable> fingerprint = new MutableLiveData<>();
|
||||
|
||||
@Inject
|
||||
IkeyInfoViewModel commonViewModel;
|
||||
|
||||
public AndroidIkeyInfoViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
((MercuryImApplication) application).getAppComponent().inject(this);
|
||||
}
|
||||
|
||||
public void init(UUID accountId) {
|
||||
getCommonViewModel().init(accountId);
|
||||
|
||||
addDisposable(getCommonViewModel().getFingerprint()
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::getItem)
|
||||
.map(OpenPgpV4FingerprintFormatter::formatOpenPgpV4Fingerprint)
|
||||
.subscribe(fingerprint::postValue));
|
||||
}
|
||||
|
||||
public LiveData<Spannable> getFingerprint() {
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IkeyInfoViewModel getCommonViewModel() {
|
||||
return commonViewModel;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package org.mercury_im.messenger.android.ui.account.login;
|
||||
|
||||
import android.app.Application;
|
||||
import android.graphics.Path;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
||||
import org.mercury_im.messenger.R;
|
||||
import org.mercury_im.messenger.android.MercuryImApplication;
|
||||
import org.mercury_im.messenger.android.ui.MercuryAndroidViewModel;
|
||||
import org.mercury_im.messenger.core.util.Optional;
|
||||
import org.mercury_im.messenger.core.viewmodel.ikey.IkeySetupViewModel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
public class AndroidIkeySetupViewModel extends AndroidViewModel implements MercuryAndroidViewModel<IkeySetupViewModel> {
|
||||
|
||||
@Inject
|
||||
IkeySetupViewModel commonViewModel;
|
||||
|
||||
private MutableLiveData<Optional<String>> passphraseError = new MutableLiveData<>();
|
||||
|
||||
public AndroidIkeySetupViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
MercuryImApplication.getApplication().getAppComponent().inject(this);
|
||||
}
|
||||
|
||||
public void init(UUID accountId) {
|
||||
getCommonViewModel().init(accountId);
|
||||
}
|
||||
|
||||
public Single<Optional<SecretkeyElement>> fetchBackupElement() {
|
||||
return getCommonViewModel().fetchBackupElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IkeySetupViewModel getCommonViewModel() {
|
||||
return commonViewModel;
|
||||
}
|
||||
|
||||
public void generateIdentityKey() throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
||||
getCommonViewModel().generateIdentityKey();
|
||||
}
|
||||
|
||||
public boolean restoreBackup(String backupCode) {
|
||||
OpenPgpSecretKeyBackupPassphrase passphrase;
|
||||
try {
|
||||
passphrase = new OpenPgpSecretKeyBackupPassphrase(backupCode);
|
||||
} catch (IllegalArgumentException e) {
|
||||
passphraseError.setValue(new Optional<>("Malformed Passphrase"));
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
getCommonViewModel().restoreBackup(passphrase);
|
||||
passphraseError.setValue(new Optional<>());
|
||||
return true;
|
||||
} catch (InvalidBackupCodeException e) {
|
||||
passphraseError.setValue(new Optional<>("Wrong Passphrase"));
|
||||
} catch (IOException | PGPException e) {
|
||||
passphraseError.setValue(new Optional<>("Error: " + e.getMessage()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public LiveData<Optional<String>> getPassphraseError() {
|
||||
return passphraseError;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package org.mercury_im.messenger.android.ui.account.login;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -8,6 +7,7 @@ import android.view.ViewGroup;
|
|||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -37,7 +37,7 @@ public class IkeyBackupRestoreOrSkipFragment extends Fragment {
|
|||
|
||||
private final UUID accountId;
|
||||
|
||||
private IkeySetupViewModel viewModel;
|
||||
private AndroidIkeySetupViewModel viewModel;
|
||||
|
||||
IkeyBackupRestoreOrSkipFragment(UUID accountId) {
|
||||
this.accountId = accountId;
|
||||
|
@ -58,11 +58,25 @@ public class IkeyBackupRestoreOrSkipFragment extends Fragment {
|
|||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
viewModel = new ViewModelProvider(requireActivity()).get(IkeySetupViewModel.class);
|
||||
viewModel = new ViewModelProvider(requireActivity()).get(AndroidIkeySetupViewModel.class);
|
||||
|
||||
restoreButton.setOnClickListener(v ->
|
||||
((AddAccountActivity) getActivity()).restoreIkeyBackup(accountId));
|
||||
restoreButton.setOnClickListener(v -> onRestore());
|
||||
regenerateButton.setOnClickListener(v ->
|
||||
((AddAccountActivity) getActivity()).generateIkeyBackup(accountId));
|
||||
|
||||
viewModel.getPassphraseError().observe(this, opt -> {
|
||||
if (opt.isPresent()) {
|
||||
Toast.makeText(getContext(), opt.getItem(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onRestore() {
|
||||
String backupCode = backupCodeEditText.getText().toString();
|
||||
|
||||
if (viewModel.restoreBackup(backupCode)) {
|
||||
((AddAccountActivity) getActivity()).restoreSuccessful(accountId);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.mercury_im.messenger.android.ui.account.login;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -19,29 +20,33 @@ import java.util.UUID;
|
|||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class IkeyBackupRestoreSuccessfulFragment extends Fragment {
|
||||
public class IkeyKeyInfoFragment extends Fragment {
|
||||
|
||||
@BindView(R.id.btn_done)
|
||||
Button doneButton;
|
||||
|
||||
@BindView(R.id.text)
|
||||
@BindView(R.id.fingerprint)
|
||||
TextView fingerprint;
|
||||
|
||||
private final UUID accountId;
|
||||
private IkeySetupViewModel viewModel;
|
||||
@BindView(R.id.btn_backup_ikey)
|
||||
Button buttonBackupIkey;
|
||||
|
||||
IkeyBackupRestoreSuccessfulFragment(UUID accountId) {
|
||||
@BindView(R.id.btn_done)
|
||||
Button buttonDone;
|
||||
|
||||
private final UUID accountId;
|
||||
|
||||
private AndroidIkeyInfoViewModel viewModel;
|
||||
|
||||
public IkeyKeyInfoFragment(UUID accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public static IkeyBackupRestoreSuccessfulFragment newInstance(UUID accountId) {
|
||||
return new IkeyBackupRestoreSuccessfulFragment(accountId);
|
||||
public static Fragment newInstance(UUID accountId) {
|
||||
return new IkeyKeyInfoFragment(accountId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_ikey_backup_restore_success, container);
|
||||
View view = inflater.inflate(R.layout.fragment_ikey_key_info, container, false);
|
||||
ButterKnife.bind(this, view);
|
||||
return view;
|
||||
}
|
||||
|
@ -49,8 +54,12 @@ public class IkeyBackupRestoreSuccessfulFragment extends Fragment {
|
|||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
viewModel = new ViewModelProvider(requireActivity()).get(IkeySetupViewModel.class);
|
||||
|
||||
doneButton.setOnClickListener(v -> getActivity().finish());
|
||||
this.viewModel = new ViewModelProvider(this).get(AndroidIkeyInfoViewModel.class);
|
||||
viewModel.init(accountId);
|
||||
viewModel.getFingerprint().observe(this, f -> fingerprint.setText(f));
|
||||
|
||||
buttonBackupIkey.setOnClickListener(v -> ((AddAccountActivity)getActivity()).generateIkeyBackup(accountId));
|
||||
buttonDone.setOnClickListener(v -> getActivity().finish());
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package org.mercury_im.messenger.android.ui.account.login;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -30,7 +29,7 @@ public class IkeySetupFragment extends Fragment {
|
|||
Button continueButton;
|
||||
|
||||
private final UUID accountId;
|
||||
private IkeySetupViewModel viewModel;
|
||||
private AndroidIkeySetupViewModel viewModel;
|
||||
|
||||
public IkeySetupFragment(UUID accountId) {
|
||||
this.accountId = accountId;
|
||||
|
@ -51,7 +50,7 @@ public class IkeySetupFragment extends Fragment {
|
|||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
viewModel = new ViewModelProvider(requireActivity()).get(IkeySetupViewModel.class);
|
||||
viewModel = new ViewModelProvider(requireActivity()).get(AndroidIkeySetupViewModel.class);
|
||||
viewModel.init(accountId);
|
||||
|
||||
skipButton.setOnClickListener(v ->
|
||||
|
@ -65,7 +64,8 @@ public class IkeySetupFragment extends Fragment {
|
|||
if (opt.isPresent()) {
|
||||
((AddAccountActivity) getActivity()).setupIkey(accountId);
|
||||
} else {
|
||||
((AddAccountActivity) getActivity()).generateIkeyBackup(accountId);
|
||||
viewModel.generateIdentityKey();
|
||||
((AddAccountActivity) getActivity()).displayInfo(accountId);
|
||||
}
|
||||
})
|
||||
.subscribe();
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
package org.mercury_im.messenger.android.ui.account.login;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smackx.ikey.IkeyManager;
|
||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||
import org.mercury_im.messenger.android.MercuryImApplication;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyInitializer;
|
||||
import org.mercury_im.messenger.core.util.Optional;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.MaybeObserver;
|
||||
import io.reactivex.Single;
|
||||
|
||||
public class IkeySetupViewModel extends AndroidViewModel {
|
||||
|
||||
@Inject
|
||||
MercuryConnectionManager connectionManager;
|
||||
|
||||
@Inject
|
||||
IkeyInitializer ikeyInitializer;
|
||||
|
||||
IkeyManager ikeyManager;
|
||||
private SecretkeyElement secretkeyElement;
|
||||
|
||||
public IkeySetupViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
MercuryImApplication.getApplication().getAppComponent().inject(this);
|
||||
}
|
||||
|
||||
public void init(UUID accountId) {
|
||||
MercuryConnection connection = connectionManager.getConnection(accountId);
|
||||
ikeyManager = ikeyInitializer.initFor(connection);
|
||||
}
|
||||
|
||||
public Single<Optional<SecretkeyElement>> fetchBackupElement() {
|
||||
return fetchMaybeBackupElement()
|
||||
.map(Optional::new)
|
||||
.toSingle(new Optional<>());
|
||||
}
|
||||
|
||||
private Maybe<SecretkeyElement> fetchMaybeBackupElement() {
|
||||
return new Maybe<SecretkeyElement>() {
|
||||
@Override
|
||||
protected void subscribeActual(MaybeObserver<? super SecretkeyElement> observer) {
|
||||
try {
|
||||
secretkeyElement = ikeyManager.fetchSecretIdentityKey();
|
||||
if (secretkeyElement != null) {
|
||||
observer.onSuccess(secretkeyElement);
|
||||
} else {
|
||||
observer.onComplete();
|
||||
}
|
||||
} catch (PubSubException.NotALeafNodeException e) {
|
||||
observer.onComplete();
|
||||
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
|
||||
observer.onError(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
40
app/src/main/res/layout/fragment_ikey_key_info.xml
Normal file
40
app/src/main/res/layout/fragment_ikey_key_info.xml
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="This is your personal Identity Key:"
|
||||
android:gravity="center"/>
|
||||
|
||||
<include android:id="@+id/fingerprint" layout="@layout/view_openpgp_4_fingerprint" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="This fingerprint is intended to be shared with your contacts."
|
||||
android:gravity="center"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="end">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_backup_ikey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Upload Backup" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_done"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Done" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -64,8 +64,6 @@ public class RxIkeyRepository implements IkeyRepository {
|
|||
m.setAccountId(accountId);
|
||||
m.setKey(secretKey);
|
||||
m.setFingerprint(new OpenPgpV4Fingerprint(secretKey.getPublicKey()));
|
||||
m.setBackupPassphrase(m.getBackupPassphrase());
|
||||
m.setTrusted(m.isTrusted());
|
||||
return m;
|
||||
})
|
||||
.flatMap(data::upsert)
|
||||
|
@ -86,8 +84,7 @@ public class RxIkeyRepository implements IkeyRepository {
|
|||
.where(IkeySecretKeyModel.ACCOUNT_ID.eq(accountID))
|
||||
.get()
|
||||
.observable()
|
||||
.map(IkeySecretKeyModel::getBackupPassphrase)
|
||||
.map(Optional::new)
|
||||
.map(m -> new Optional<>(m.getBackupPassphrase()))
|
||||
.single(new Optional<>());
|
||||
}
|
||||
|
||||
|
@ -99,7 +96,7 @@ public class RxIkeyRepository implements IkeyRepository {
|
|||
.single(new IkeySecretKeyModel())
|
||||
.map(m -> {
|
||||
m.setAccountId(accountId);
|
||||
m.setBackupPassphrase(m.getBackupPassphrase());
|
||||
m.setBackupPassphrase(passphrase);
|
||||
return m;
|
||||
})
|
||||
.flatMap(data::upsert)
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureVerificationMechanism;
|
|||
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
||||
import org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil;
|
||||
import org.jivesoftware.smackx.ox.util.SecretKeyBackupHelper;
|
||||
import org.jivesoftware.smackx.pep.PepEventListener;
|
||||
|
@ -39,8 +40,10 @@ import org.jxmpp.jid.EntityBareJid;
|
|||
import org.mercury_im.messenger.core.crypto.OpenPgpSecretKeyBackupPassphraseGenerator;
|
||||
import org.mercury_im.messenger.core.crypto.SecureRandomSecretKeyBackupPassphraseGenerator;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.key.collection.PGPKeyRing;
|
||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||
import org.pgpainless.util.BCUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
|
@ -280,4 +283,11 @@ public final class IkeyManager extends Manager {
|
|||
private OpenPgpSecretKeyBackupPassphrase generateBackupPassphrase() {
|
||||
return backupPassphraseGenerator.generateBackupPassphrase();
|
||||
}
|
||||
|
||||
public void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, OpenPgpSecretKeyBackupPassphrase passphrase)
|
||||
throws PGPException, IOException, InvalidBackupCodeException {
|
||||
PGPSecretKeyRing secretKeys = SecretKeyBackupHelper.restoreSecretKeyBackup(secretkeyElement, passphrase);
|
||||
store.storeSecretKey(secretKeys);
|
||||
store.storeBackupPassphrase(passphrase);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,9 +52,7 @@ public class IkeyStoreAdapter implements IkeyStore {
|
|||
|
||||
@Override
|
||||
public void storeSecretKey(PGPSecretKeyRing secretKey) {
|
||||
repository.storeSecretKey(accountId, secretKey)
|
||||
.compose(schedulers.executeUiSafeCompletable())
|
||||
.subscribe();
|
||||
repository.storeSecretKey(accountId, secretKey).blockingAwait();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -68,7 +66,6 @@ public class IkeyStoreAdapter implements IkeyStore {
|
|||
@Override
|
||||
public void storeBackupPassphrase(OpenPgpSecretKeyBackupPassphrase passphrase) {
|
||||
repository.storeBackupPassphrase(accountId, passphrase)
|
||||
.compose(schedulers.executeUiSafeCompletable())
|
||||
.subscribe();
|
||||
.blockingAwait();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package org.mercury_im.messenger.core.viewmodel.ikey;
|
||||
|
||||
import org.mercury_im.messenger.core.SchedulersFacade;
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
|
||||
import org.mercury_im.messenger.core.util.Optional;
|
||||
import org.mercury_im.messenger.core.viewmodel.MercuryViewModel;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
public class IkeyInfoViewModel implements MercuryViewModel {
|
||||
|
||||
private final SchedulersFacade schedulers;
|
||||
private final IkeyRepository ikeyRepository;
|
||||
|
||||
private final BehaviorSubject<Optional<OpenPgpV4Fingerprint>> fingerprint = BehaviorSubject.createDefault(new Optional<>());
|
||||
private UUID accountId;
|
||||
|
||||
@Inject
|
||||
public IkeyInfoViewModel(IkeyRepository ikeyRepository, SchedulersFacade schedulersFacade) {
|
||||
this.ikeyRepository = ikeyRepository;
|
||||
this.schedulers = schedulersFacade;
|
||||
}
|
||||
|
||||
public void init(UUID accountId) {
|
||||
if (this.accountId != null) {
|
||||
throw new IllegalStateException("Already initialized.");
|
||||
}
|
||||
|
||||
this.accountId = accountId;
|
||||
addDisposable(ikeyRepository.loadSecretKey(accountId)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::getItem)
|
||||
.firstOrError()
|
||||
.map(OpenPgpV4Fingerprint::new)
|
||||
.compose(schedulers.executeUiSafeSingle())
|
||||
.map(Optional::new)
|
||||
.subscribe(this.fingerprint::onNext));
|
||||
}
|
||||
|
||||
public Observable<Optional<OpenPgpV4Fingerprint>> getFingerprint() {
|
||||
return fingerprint;
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package org.mercury_im.messenger.core.viewmodel.ikey;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smackx.ikey.IkeyManager;
|
||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyInitializer;
|
||||
import org.mercury_im.messenger.core.viewmodel.MercuryViewModel;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
|
||||
public class IkeyInitializationViewModel implements MercuryViewModel {
|
||||
|
||||
private final MercuryConnectionManager connectionManager;
|
||||
private final IkeyInitializer ikeyInitializer;
|
||||
|
||||
public IkeyInitializationViewModel(MercuryConnectionManager connectionManager,
|
||||
IkeyInitializer ikeyInitializer) {
|
||||
this.connectionManager = connectionManager;
|
||||
this.ikeyInitializer = ikeyInitializer;
|
||||
}
|
||||
|
||||
public void initialize(Account account) throws PGPException, NoSuchAlgorithmException,
|
||||
NoSuchProviderException, InvalidAlgorithmParameterException, InterruptedException,
|
||||
SmackException.NoResponseException, SmackException.NotConnectedException,
|
||||
SmackException.FeatureNotSupportedException, XMPPException.XMPPErrorException,
|
||||
PubSubException.NotALeafNodeException, IOException {
|
||||
|
||||
MercuryConnection connection = connectionManager.getConnection(account);
|
||||
IkeyManager ikeyManager = ikeyInitializer.initFor(connection);
|
||||
|
||||
final SecretkeyElement backup = ikeyManager.fetchSecretIdentityKey();
|
||||
|
||||
if (!ikeyManager.hasLocalKey()) {
|
||||
if (backup == null) {
|
||||
ikeyManager.generateIdentityKey();
|
||||
ikeyManager.depositIdentityKeyBackup();
|
||||
} else {
|
||||
//ikeyManager.restore(backup);
|
||||
}
|
||||
} else {
|
||||
if (backup == null) {
|
||||
ikeyManager.depositIdentityKeyBackup();
|
||||
} else {
|
||||
//if (!ikeyManager.keyBackupIsSameAsLocal()) {
|
||||
// displayNewKeyNotification();
|
||||
//} else {
|
||||
// ikeyManager.depositIdentityKeyBackup();
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
package org.mercury_im.messenger.core.viewmodel.ikey;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smackx.ikey.IkeyManager;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyInitializer;
|
||||
|
@ -10,10 +14,12 @@ import org.mercury_im.messenger.core.util.Optional;
|
|||
import org.mercury_im.messenger.core.viewmodel.MercuryViewModel;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Observable;
|
||||
|
||||
public class IkeySecretKeyBackupCreationViewModel implements MercuryViewModel {
|
||||
|
@ -34,12 +40,21 @@ public class IkeySecretKeyBackupCreationViewModel implements MercuryViewModel {
|
|||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public void createIkeySecretKeyBackup(Account account) {
|
||||
MercuryConnection connection = connectionManager.getConnection(account);
|
||||
IkeyManager ikeyManager = ikeyInitializer.initFor(connection);
|
||||
}
|
||||
|
||||
public Observable<Optional<OpenPgpSecretKeyBackupPassphrase>> getPassphrase() {
|
||||
return ikeyRepository.loadBackupPassphrase(accountId).toObservable();
|
||||
}
|
||||
|
||||
public void doCreateBackup() throws XMPPException.XMPPErrorException, InterruptedException,
|
||||
SmackException.NoResponseException, SmackException.NotConnectedException,
|
||||
SmackException.FeatureNotSupportedException, PGPException,
|
||||
PubSubException.NotALeafNodeException, IOException {
|
||||
MercuryConnection connection = connectionManager.getConnection(accountId);
|
||||
IkeyManager ikeyManager = ikeyInitializer.initFor(connection);
|
||||
|
||||
ikeyManager.depositIdentityKeyBackup();
|
||||
}
|
||||
|
||||
public Completable createBackup() {
|
||||
return Completable.fromAction(this::doCreateBackup);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package org.mercury_im.messenger.core.viewmodel.ikey;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smackx.ikey.IkeyManager;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
|
||||
import org.mercury_im.messenger.core.crypto.OpenPgpSecretKeyBackupPassphraseGenerator;
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyInitializer;
|
||||
import org.mercury_im.messenger.core.util.Optional;
|
||||
import org.mercury_im.messenger.core.viewmodel.MercuryViewModel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.MaybeObserver;
|
||||
import io.reactivex.Single;
|
||||
import lombok.Getter;
|
||||
|
||||
public class IkeySetupViewModel implements MercuryViewModel {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(IkeySetupViewModel.class.getName());
|
||||
|
||||
private final MercuryConnectionManager connectionManager;
|
||||
private final IkeyInitializer ikeyInitializer;
|
||||
|
||||
private IkeyManager ikeyManager;
|
||||
private SecretkeyElement secretkeyElement;
|
||||
|
||||
@Getter
|
||||
private final OpenPgpSecretKeyBackupPassphrase backupCreationPassphrase;
|
||||
|
||||
@Inject
|
||||
public IkeySetupViewModel(MercuryConnectionManager connectionManager, IkeyInitializer ikeyInitializer, OpenPgpSecretKeyBackupPassphraseGenerator passphraseGenerator) {
|
||||
this.connectionManager = connectionManager;
|
||||
this.ikeyInitializer = ikeyInitializer;
|
||||
|
||||
this.backupCreationPassphrase = passphraseGenerator.generateBackupPassphrase();
|
||||
}
|
||||
|
||||
public void init(UUID accountId) {
|
||||
MercuryConnection connection = connectionManager.getConnection(accountId);
|
||||
ikeyManager = ikeyInitializer.initFor(connection);
|
||||
}
|
||||
|
||||
public Single<Optional<SecretkeyElement>> fetchBackupElement() {
|
||||
return fetchMaybeBackupElement()
|
||||
.map(Optional::new)
|
||||
.toSingle(new Optional<>());
|
||||
}
|
||||
|
||||
private Maybe<SecretkeyElement> fetchMaybeBackupElement() {
|
||||
return new Maybe<SecretkeyElement>() {
|
||||
@Override
|
||||
protected void subscribeActual(MaybeObserver<? super SecretkeyElement> observer) {
|
||||
try {
|
||||
secretkeyElement = ikeyManager.fetchSecretIdentityKey();
|
||||
if (secretkeyElement != null) {
|
||||
LOGGER.log(Level.INFO, "Found IKey backup element.");
|
||||
observer.onSuccess(secretkeyElement);
|
||||
} else {
|
||||
LOGGER.log(Level.INFO, "Ikey backup element is null.");
|
||||
observer.onComplete();
|
||||
}
|
||||
} catch (PubSubException.NotALeafNodeException e) {
|
||||
LOGGER.log(Level.INFO, "Ikey backup node is not a LeafNode.");
|
||||
observer.onComplete();
|
||||
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
|
||||
LOGGER.log(Level.INFO, "Error restoring Ikey backup", e);
|
||||
observer.onError(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void generateIdentityKey() throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
||||
ikeyManager.generateIdentityKey();
|
||||
}
|
||||
|
||||
public void restoreBackup(OpenPgpSecretKeyBackupPassphrase passphrase) throws IOException, PGPException, InvalidBackupCodeException {
|
||||
ikeyManager.restoreSecretKeyBackup(secretkeyElement, passphrase);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue