Implement publishing ikey elements

This commit is contained in:
Paul Schaub 2020-11-22 17:34:21 +01:00
parent b8185b30ae
commit 3cbe527959
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
6 changed files with 127 additions and 15 deletions

View File

@ -57,6 +57,9 @@ public class AccountDetailsFragment extends Fragment {
@BindView(R.id.btn_backup_ikey) @BindView(R.id.btn_backup_ikey)
Button ikeyCreateBackupButton; Button ikeyCreateBackupButton;
@BindView(R.id.btn_send_ikey_element)
Button ikeySendDecisions;
@BindView(R.id.ikey_fingerprint) @BindView(R.id.ikey_fingerprint)
TextView ikeyFingerprint; TextView ikeyFingerprint;
@ -81,6 +84,19 @@ public class AccountDetailsFragment extends Fragment {
this.accountId = accountId; this.accountId = accountId;
} }
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_account_details, container, false);
ButterKnife.bind(this, view);
setHasOptionsMenu(true);
externalFingerprintRecyclerView.setAdapter(otherFingerprintsAdapter);
observeViewModel();
return view;
}
@Override @Override
public void onAttach(@NonNull Context context) { public void onAttach(@NonNull Context context) {
super.onAttach(context); super.onAttach(context);
@ -89,8 +105,7 @@ public class AccountDetailsFragment extends Fragment {
viewModel = new ViewModelProvider(this, factory) viewModel = new ViewModelProvider(this, factory)
.get(AndroidAccountDetailsViewModel.class); .get(AndroidAccountDetailsViewModel.class);
this.otherFingerprintsAdapter = new ToggleableFingerprintsAdapter( this.otherFingerprintsAdapter = new ToggleableFingerprintsAdapter(this::markFingerprintTrusted);
(fingerprint, checked) -> viewModel.markFingerprintTrusted(fingerprint, checked));
this.otherFingerprintsAdapter.setItemLongClickListener(fingerprint -> viewModel.unpublishPublicKey(fingerprint)); this.otherFingerprintsAdapter.setItemLongClickListener(fingerprint -> viewModel.unpublishPublicKey(fingerprint));
} }
@ -115,21 +130,22 @@ public class AccountDetailsFragment extends Fragment {
case R.id.action_restore_ikey_backup: case R.id.action_restore_ikey_backup:
viewModel.onRestoreIkeyBackup(); viewModel.onRestoreIkeyBackup();
return true; return true;
// case R.id.action_setup_ikey:
// getParentFragmentManager().beginTransaction()
// .replace(R.id.fragment, IkeySetupFragment.newInstance(accountId))
// .commit();
// return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Nullable private void markFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, boolean trusted) {
@Override viewModel.markFingerprintTrusted(fingerprint, trusted);
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { }
View view = inflater.inflate(R.layout.fragment_account_details, container, false);
ButterKnife.bind(this, view);
setHasOptionsMenu(true);
externalFingerprintRecyclerView.setAdapter(otherFingerprintsAdapter); private void sendIkeyElement() {
observeViewModel(); viewModel.sendIkeyElement(accountId);
return view;
} }
private void observeViewModel() { private void observeViewModel() {
@ -165,6 +181,8 @@ public class AccountDetailsFragment extends Fragment {
ikeyCreateBackupButton.setEnabled(authenticated); ikeyCreateBackupButton.setEnabled(authenticated);
localKeyCreateBackupButton.setEnabled(authenticated); localKeyCreateBackupButton.setEnabled(authenticated);
}); });
ikeySendDecisions.setOnClickListener(v -> sendIkeyElement());
} }
private void startShareFingerprintIntent(OpenPgpV4Fingerprint fingerprint) { private void startShareFingerprintIntent(OpenPgpV4Fingerprint fingerprint) {

View File

@ -105,6 +105,10 @@ public class AndroidAccountDetailsViewModel extends AndroidViewModel implements
getCommonViewModel().markFingerprintTrusted(accountId, fingerprint, trusted); getCommonViewModel().markFingerprintTrusted(accountId, fingerprint, trusted);
} }
public void sendIkeyElement(UUID accountId) {
getCommonViewModel().sendIkeyElement(accountId);
}
public LiveData<Optional<OpenPgpV4Fingerprint>> getIkeyFingerprint() { public LiveData<Optional<OpenPgpV4Fingerprint>> getIkeyFingerprint() {
return ikeyFingerprint; return ikeyFingerprint;
} }

View File

@ -16,6 +16,7 @@
android:orientation="vertical"> android:orientation="vertical">
<de.hdodenhof.circleimageview.CircleImageView <de.hdodenhof.circleimageview.CircleImageView
android:visibility="gone"
android:id="@+id/avatar" android:id="@+id/avatar"
android:layout_width="196dp" android:layout_width="196dp"
android:layout_height="196dp" android:layout_height="196dp"
@ -77,7 +78,7 @@
android:id="@+id/title_ikey" android:id="@+id/title_ikey"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Account Identity Key" android:text="Identity Key"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@ -134,7 +135,7 @@
android:id="@+id/title" android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Local Fingerprint" android:text="Device Key"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@ -153,6 +154,7 @@
<Button <Button
android:id="@+id/btn_backup" android:id="@+id/btn_backup"
android:visibility="gone"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/btn_server_backup" android:text="@string/btn_server_backup"
@ -180,6 +182,14 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"/> android:layout_marginBottom="8dp"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn_send_ikey_element"
android:layout_gravity="end"
android:text="Publish Decisions" />
</LinearLayout> </LinearLayout>

View File

@ -21,7 +21,7 @@
android:id="@+id/title" android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Other Fingerprints" android:text="Other Devices"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />

View File

@ -23,4 +23,8 @@
android:title="Restore Identity Key Backup" android:title="Restore Identity Key Backup"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/action_setup_ikey"
android:title="Ikey Setup" />
</menu> </menu>

View File

@ -1,7 +1,14 @@
package org.mercury_im.messenger.core.viewmodel.account.detail; package org.mercury_im.messenger.core.viewmodel.account.detail;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smackx.ikey.IkeyManager; import org.jivesoftware.smackx.ikey.IkeyManager;
import org.jivesoftware.smackx.ikey.element.IkeyElement;
import org.jivesoftware.smackx.ikey.element.SubordinateElement;
import org.jivesoftware.smackx.ikey.mechanism.IkeyType;
import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureCreationMechanism;
import org.jivesoftware.smackx.ox.element.PublicKeysListElement; import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
import org.jivesoftware.smackx.ox.element.SecretkeyElement; import org.jivesoftware.smackx.ox.element.SecretkeyElement;
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore; import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
@ -10,6 +17,8 @@ import org.jivesoftware.smackx.pep.PepManager;
import org.jivesoftware.smackx.pubsub.LeafNode; import org.jivesoftware.smackx.pubsub.LeafNode;
import org.jivesoftware.smackx.pubsub.PayloadItem; import org.jivesoftware.smackx.pubsub.PayloadItem;
import org.jivesoftware.smackx.pubsub.PubSubManager; import org.jivesoftware.smackx.pubsub.PubSubManager;
import org.jivesoftware.smackx.pubsub.PubSubUri;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.core.SchedulersFacade; import org.mercury_im.messenger.core.SchedulersFacade;
import org.mercury_im.messenger.core.connection.MercuryConnection; import org.mercury_im.messenger.core.connection.MercuryConnection;
@ -25,14 +34,24 @@ import org.mercury_im.messenger.core.viewmodel.MercuryViewModel;
import org.mercury_im.messenger.core.viewmodel.openpgp.FingerprintViewItem; import org.mercury_im.messenger.core.viewmodel.openpgp.FingerprintViewItem;
import org.mercury_im.messenger.entity.Account; import org.mercury_im.messenger.entity.Account;
import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.protection.UnprotectedKeysProtector;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import io.reactivex.Completable; import io.reactivex.Completable;
import io.reactivex.Maybe;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.Single; import io.reactivex.Single;
@ -40,6 +59,8 @@ import static org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil.PEP_NODE_PUBLIC_
public class AccountDetailsViewModel implements MercuryViewModel { public class AccountDetailsViewModel implements MercuryViewModel {
private static final Logger LOGGER = Logger.getLogger(AccountDetailsViewModel.class.getName());
private MercuryConnectionManager connectionManager; private MercuryConnectionManager connectionManager;
private final OpenPgpRepository openPgpRepository; private final OpenPgpRepository openPgpRepository;
private final IkeyRepository ikeyRepository; private final IkeyRepository ikeyRepository;
@ -68,6 +89,61 @@ public class AccountDetailsViewModel implements MercuryViewModel {
.subscribe()); .subscribe());
} }
public void sendIkeyElement(UUID accountId) {
addDisposable(Completable
.fromAction(() -> {
IkeyElement ikeyElement = syncCreateIkeyElement(accountId);
IkeyManager ikeyManager = IkeyManager.getInstanceFor(connectionManager.getConnection(accountId).getConnection());
ikeyManager.publishIkeyElement(ikeyElement);
})
.compose(schedulers.executeUiSafeCompletable())
.subscribe(
() -> LOGGER.log(Level.INFO, "Successfully published ikey element."),
e -> LOGGER.log(Level.SEVERE, "Error publishing ikey element:", e)));
}
private IkeyElement syncCreateIkeyElement(UUID accountId) throws URISyntaxException, PGPException, IOException {
MercuryConnection connection = connectionManager.getConnection(accountId);
IkeyManager ikeyManager = ikeyInitializer.initFor(connection);
Account account = accountRepository.getAccount(accountId).blockingGet();
OpenPgpV4Fingerprint localFp = openPgpRepository.observeLocalFingerprintOf(accountId).blockingFirst().getItem();
// Why does openPgpRepository.loadAnnouncedFingerprints() never return?
List<OpenPgpV4Fingerprint> fingerprintList =
openPgpRepository.observeFingerprints(accountId, account.getJid()).first(new ArrayList<>())
.map(l -> {
List<OpenPgpV4Fingerprint> fps = new ArrayList<>();
for (FingerprintViewItem vi : l) {
fps.add(vi.getFingerprint());
}
return fps;
}).blockingGet();
List<SubordinateElement> subordinateElements = new ArrayList<>();
URI uri = new URI(getOxKeyNodeUri(connection, localFp).toString());
subordinateElements.add(new SubordinateElement("urn:xmpp:openpgp:0", uri, localFp.toString()));
for (OpenPgpV4Fingerprint fp : fingerprintList) {
URI u = new URI(getOxKeyNodeUri(connection, fp).toString());
subordinateElements.add(new SubordinateElement("urn:xmpp:openpgp:0", u, fp.toString()));
}
PGPSecretKeyRing secretKeys = openPgpRepository.loadSecretKeysOf(accountId, account.getJid())
.blockingGet().getSecretKeyRing(localFp.getKeyId());
IkeyElement ikeyElement = ikeyManager.createOxIkeyElement(secretKeys,
new UnprotectedKeysProtector(), subordinateElements.toArray(new SubordinateElement[]{}));
return ikeyElement;
}
private PubSubUri getOxKeyNodeUri(MercuryConnection connection, OpenPgpV4Fingerprint fingerprint) {
PubSubManager pubSubManager = PubSubManager.getInstanceFor(connection.getConnection());
BareJid serviceJid = pubSubManager.getServiceJid();
return new PubSubUri(serviceJid, OpenPgpPubSubUtil.PEP_NODE_PUBLIC_KEY(fingerprint), null, null);
}
public Single<EntityBareJid> getJid(UUID accountId) { public Single<EntityBareJid> getJid(UUID accountId) {
return accountRepository.getAccount(accountId).toSingle() return accountRepository.getAccount(accountId).toSingle()
.compose(schedulers.executeUiSafeSingle()) .compose(schedulers.executeUiSafeSingle())