Compare commits
2 Commits
b67af2c702
...
2ea902a081
Author | SHA1 | Date |
---|---|---|
Paul Schaub | 2ea902a081 | |
Paul Schaub | 4b9e9f9ae9 |
|
@ -106,7 +106,7 @@ public class AccountDetailsFragment extends Fragment {
|
|||
viewModel = new ViewModelProvider(this, factory)
|
||||
.get(AndroidAccountDetailsViewModel.class);
|
||||
|
||||
this.otherFingerprintsAdapter = new ToggleableFingerprintsAdapter(this::markFingerprintTrusted);
|
||||
this.otherFingerprintsAdapter = new ToggleableFingerprintsAdapter(context, this::markFingerprintTrusted);
|
||||
this.otherFingerprintsAdapter.setItemLongClickListener(fingerprint -> viewModel.unpublishPublicKey(fingerprint));
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ public class AndroidAccountDetailsViewModel extends AndroidViewModel implements
|
|||
LOGGER.log(Level.INFO, "Creating AndroidAccountDetailsViewModel");
|
||||
((MercuryImApplication) application).getAppComponent().inject(this);
|
||||
|
||||
addDisposable(getCommonViewModel().observeLocalFingerprint(accountId)
|
||||
addDisposable(getCommonViewModel().observeLocalDeviceFingerprint(accountId)
|
||||
.compose(schedulers.executeUiSafeObservable())
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::getItem)
|
||||
|
@ -64,7 +64,7 @@ public class AndroidAccountDetailsViewModel extends AndroidViewModel implements
|
|||
.subscribe(ikeyFingerprint::postValue,
|
||||
e -> LOGGER.log(Level.SEVERE, "Error displaying ikey fingerprint", e)));
|
||||
|
||||
addDisposable(getCommonViewModel().observeRemoteFingerprints(accountId)
|
||||
addDisposable(getCommonViewModel().observeRemoteDeviceFingerprints(accountId)
|
||||
.compose(schedulers.executeUiSafeObservable())
|
||||
.subscribe(list -> {
|
||||
LOGGER.log(Level.INFO, "Set remote fingerprints to list: " + Arrays.toString(list.toArray()));
|
||||
|
|
|
@ -27,6 +27,7 @@ import javax.inject.Inject;
|
|||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Single;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
public class AndroidContactDetailViewModel extends ViewModel implements MercuryAndroidViewModel<ContactDetailViewModel> {
|
||||
|
||||
|
@ -122,6 +123,7 @@ public class AndroidContactDetailViewModel extends ViewModel implements MercuryA
|
|||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public void markDeviceFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, boolean checked) {
|
||||
commonViewModel.markDeviceFingerprintTrusted(fingerprint, checked);
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ public class ContactDetailFragment extends Fragment {
|
|||
contactName.setOnClickListener(v -> displayChangeContactNameDialog());
|
||||
|
||||
|
||||
fingerprintsAdapter = new ToggleableFingerprintsAdapter(
|
||||
fingerprintsAdapter = new ToggleableFingerprintsAdapter(getContext(),
|
||||
(fingerprint, checked) -> viewModel.markDeviceFingerprintTrusted(fingerprint, checked));
|
||||
fingerprintRecyclerView.setAdapter(fingerprintsAdapter);
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.mercury_im.messenger.android.ui.openpgp;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -9,6 +11,7 @@ import android.widget.TextView;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
import org.mercury_im.messenger.R;
|
||||
import org.mercury_im.messenger.core.viewmodel.openpgp.FingerprintViewItem;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
@ -25,8 +28,10 @@ public class ToggleableFingerprintsAdapter extends RecyclerView.Adapter<Toggleab
|
|||
private OnFingerprintItemLongClickListener longClickListener = null;
|
||||
|
||||
private static final DateFormat dateFormat = SimpleDateFormat.getDateInstance();
|
||||
private Context context;
|
||||
|
||||
public ToggleableFingerprintsAdapter(OnFingerprintItemToggleListener toggleListener) {
|
||||
public ToggleableFingerprintsAdapter(Context context, OnFingerprintItemToggleListener toggleListener) {
|
||||
this.context = context;
|
||||
this.toggleListener = toggleListener;
|
||||
}
|
||||
|
||||
|
@ -57,8 +62,13 @@ public class ToggleableFingerprintsAdapter extends RecyclerView.Adapter<Toggleab
|
|||
holder.fingerprintTimestamp.setText(dateFormat.format(f.getModificationDate()));
|
||||
|
||||
holder.trustSwitch.setChecked(f.isTrusted());
|
||||
holder.trustSwitch.setOnCheckedChangeListener(
|
||||
(buttonView, isChecked) -> toggleListener.onFingerprintToggled(fingerprint, isChecked));
|
||||
holder.trustSwitch.setOnClickListener(v -> {
|
||||
boolean checked = ((Switch) v).isChecked();
|
||||
toggleListener.onFingerprintToggled(fingerprint, checked);
|
||||
});
|
||||
if (f.getTrusted() == OpenPgpTrustStore.Trust.ikey_trusted && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
holder.trustSwitch.setThumbDrawable(context.getDrawable(R.drawable.ic_lock_black_24dp));
|
||||
}
|
||||
holder.divider.setVisibility(position == fingerprints.size() - 1 ? View.GONE : View.VISIBLE);
|
||||
|
||||
holder.itemView.setOnLongClickListener(v -> {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
|
||||
</vector>
|
|
@ -1,5 +1,6 @@
|
|||
package org.mercury_im.messenger.data.di;
|
||||
|
||||
import org.jivesoftware.smackx.ikey.util.IkeyOpenPgpTrustStore;
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.AccountRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.DirectChatRepository;
|
||||
|
@ -8,6 +9,7 @@ import org.mercury_im.messenger.core.data.repository.GroupChatRepository;
|
|||
import org.mercury_im.messenger.core.data.repository.IkeyRecordRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.MessageRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.PeerRepository;
|
||||
import org.mercury_im.messenger.data.mapping.AccountMapping;
|
||||
import org.mercury_im.messenger.data.mapping.DirectChatMapping;
|
||||
|
@ -20,6 +22,7 @@ import org.mercury_im.messenger.data.repository.RxDirectChatRepository;
|
|||
import org.mercury_im.messenger.data.repository.RxEntityCapsRepository;
|
||||
import org.mercury_im.messenger.data.repository.RxGroupChatRepository;
|
||||
import org.mercury_im.messenger.data.repository.RxIkeyRepository;
|
||||
import org.mercury_im.messenger.data.repository.RxIkeyTrustRepository;
|
||||
import org.mercury_im.messenger.data.repository.RxMessageRepository;
|
||||
import org.mercury_im.messenger.data.repository.RxOpenPgpRepository;
|
||||
import org.mercury_im.messenger.data.repository.RxPeerRepository;
|
||||
|
@ -100,4 +103,11 @@ public class RepositoryModule {
|
|||
static IkeyRepository provideIkeyRepository(ReactiveEntityStore<Persistable> data) {
|
||||
return new RxIkeyRepository(data);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static OpenPgpTrustRepository provideOpenPgpTrustRepository(IkeyRepository recordRepository, OpenPgpRepository openPgpRepository) {
|
||||
return (OpenPgpTrustRepository) openPgpRepository;
|
||||
// return new RxIkeyTrustRepository(recordRepository);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.mercury_im.messenger.data.model;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.data.converter.Base64PGPPublicKeyRingConverter;
|
||||
import org.mercury_im.messenger.data.converter.EntityBareJidConverter;
|
||||
|
@ -15,23 +16,28 @@ import io.requery.CascadeAction;
|
|||
import io.requery.Column;
|
||||
import io.requery.Convert;
|
||||
import io.requery.Entity;
|
||||
import io.requery.Index;
|
||||
import io.requery.Key;
|
||||
import io.requery.OneToMany;
|
||||
import io.requery.Table;
|
||||
import io.requery.converter.UUIDConverter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Table(name = "ikey_record")
|
||||
@Table(name = "ikey_record", uniqueIndexes = "unique_ikey_record")
|
||||
@Entity
|
||||
@ToString
|
||||
public class AbstractIkeyRecordModel {
|
||||
|
||||
@Key
|
||||
@Convert(UUIDConverter.class)
|
||||
UUID id;
|
||||
|
||||
@Column(name = "account")
|
||||
@Index("unique_ikey_record")
|
||||
@Column
|
||||
@Convert(UUIDConverter.class)
|
||||
UUID accountId;
|
||||
|
||||
@Index("unique_ikey_record")
|
||||
@Column(name = "jid")
|
||||
@Convert(EntityBareJidConverter.class)
|
||||
EntityBareJid jid;
|
||||
|
@ -50,7 +56,11 @@ public class AbstractIkeyRecordModel {
|
|||
@Convert(Base64PGPPublicKeyRingConverter.class)
|
||||
PGPPublicKeyRing superordinate;
|
||||
|
||||
@Index("unique_ikey_record")
|
||||
@Column(name = "fingerprint")
|
||||
@Convert(OpenPgpV4FingerprintConverter.class)
|
||||
OpenPgpV4Fingerprint fingerprint;
|
||||
|
||||
@Column
|
||||
OpenPgpTrustStore.Trust trust;
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
package org.mercury_im.messenger.data.model;
|
||||
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.data.converter.EntityBareJidConverter;
|
||||
import org.mercury_im.messenger.data.converter.OpenPgpTrustConverter;
|
||||
import org.mercury_im.messenger.data.converter.OpenPgpV4FingerprintConverter;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import io.requery.Column;
|
||||
import io.requery.Convert;
|
||||
import io.requery.Entity;
|
||||
import io.requery.ForeignKey;
|
||||
import io.requery.Key;
|
||||
import io.requery.ReferentialAction;
|
||||
import io.requery.Table;
|
||||
import io.requery.converter.UUIDConverter;
|
||||
|
||||
@Table(name = "ikey_trust")
|
||||
@Entity
|
||||
public class AbstractIkeyTrustModel {
|
||||
|
||||
@Key
|
||||
@ForeignKey(references = AbstractAccountModel.class, delete = ReferentialAction.CASCADE)
|
||||
@Convert(UUIDConverter.class)
|
||||
UUID accountId;
|
||||
|
||||
@Key
|
||||
@Convert(EntityBareJidConverter.class)
|
||||
EntityBareJid jid;
|
||||
|
||||
@Key
|
||||
@Convert(OpenPgpV4FingerprintConverter.class)
|
||||
OpenPgpV4Fingerprint fingerprint;
|
||||
|
||||
@Column(name = "trust")
|
||||
@Convert(OpenPgpTrustConverter.class)
|
||||
OpenPgpTrustStore.Trust trust;
|
||||
}
|
|
@ -4,16 +4,15 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|||
import org.jivesoftware.smackx.ikey.record.IkeyRecord;
|
||||
import org.jivesoftware.smackx.ikey.record.IkeySubordinateRecord;
|
||||
import org.jivesoftware.smackx.ikey.record.OxSubordinateRecord;
|
||||
import org.jivesoftware.smackx.ikey.util.IkeyTrust;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
|
||||
import org.mercury_im.messenger.core.util.Optional;
|
||||
import org.mercury_im.messenger.data.model.IkeyRecordModel;
|
||||
import org.mercury_im.messenger.data.model.IkeySecretKeyModel;
|
||||
import org.mercury_im.messenger.data.model.IkeySubordinateModel;
|
||||
import org.mercury_im.messenger.data.model.IkeyTrustModel;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -23,6 +22,8 @@ import java.util.UUID;
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
@ -36,6 +37,7 @@ public class RxIkeyRepository implements IkeyRepository {
|
|||
|
||||
private final ReactiveEntityStore<Persistable> data;
|
||||
|
||||
@Inject
|
||||
public RxIkeyRepository(ReactiveEntityStore<Persistable> data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
@ -102,30 +104,27 @@ public class RxIkeyRepository implements IkeyRepository {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Observable<Optional<IkeyTrust>> loadSuperordinateTrust(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint) {
|
||||
return data.select(IkeyTrustModel.class)
|
||||
.where(IkeyTrustModel.ACCOUNT_ID.eq(accountId).and(IkeyTrustModel.JID.eq(jid).and(IkeyTrustModel.FINGERPRINT.eq(fingerprint))))
|
||||
.get()
|
||||
.observableResult()
|
||||
.map(r -> {
|
||||
IkeyTrustModel m = r.firstOrNull();
|
||||
if (m == null) {
|
||||
public Observable<Optional<OpenPgpTrustStore.Trust>> loadSuperordinateTrust(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint) {
|
||||
return loadRecord(accountId, jid)
|
||||
.map(model -> {
|
||||
if (fingerprint.equals(new OpenPgpV4Fingerprint(model.getSuperordinate()))) {
|
||||
return new Optional<>(model.getTrust());
|
||||
} else {
|
||||
return new Optional<>();
|
||||
}
|
||||
IkeyTrust e = new IkeyTrust();
|
||||
e.setTrust(m.getTrust());
|
||||
return new Optional<>(e);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable storeSuperordinateTrust(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint, IkeyTrust trust) {
|
||||
IkeyTrustModel model = new IkeyTrustModel();
|
||||
model.setAccountId(accountId);
|
||||
model.setJid(jid);
|
||||
model.setFingerprint(fingerprint);
|
||||
model.setTrust(trust.getTrust());
|
||||
return data.upsert(model).ignoreElement();
|
||||
public Completable storeSuperordinateTrust(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust) {
|
||||
return loadRecord(accountId, jid)
|
||||
.map(record -> {
|
||||
record.setTrust(trust);
|
||||
return record;
|
||||
})
|
||||
.firstOrError()
|
||||
.doOnSuccess(m -> LOGGER.log(Level.INFO, "First Record: " + m))
|
||||
.flatMapCompletable(record -> storeRecord(accountId, jid, record));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -136,11 +135,13 @@ public class RxIkeyRepository implements IkeyRepository {
|
|||
for (IkeySubordinateModel sub : m.getSubordinates()) {
|
||||
if (sub.getType().equals(OpenPgpElement.NAMESPACE)) {
|
||||
OxSubordinateRecord r = new OxSubordinateRecord();
|
||||
r.setId(sub.getId());
|
||||
r.setOxFingerprint(new OpenPgpV4Fingerprint(sub.getFpr()));
|
||||
r.setUri(sub.getUri());
|
||||
subordinateRecords.add(r);
|
||||
}
|
||||
}
|
||||
return new IkeyRecord(jid, m.getTimestamp(), m.getSuperordinate(), subordinateRecords);
|
||||
return new IkeyRecord(jid, m.getTimestamp(), m.getSuperordinate(), subordinateRecords, m.getTrust());
|
||||
})
|
||||
.doOnError(e -> LOGGER.log(Level.SEVERE, "Error loading ikey record", e));
|
||||
}
|
||||
|
@ -158,11 +159,12 @@ public class RxIkeyRepository implements IkeyRepository {
|
|||
m.setFingerprint(new OpenPgpV4Fingerprint(record.getSuperordinate()));
|
||||
m.setSuperordinate(record.getSuperordinate());
|
||||
m.setTimestamp(record.getTimestamp());
|
||||
m.setTrust(record.getTrust());
|
||||
|
||||
m.getSubordinates().clear();
|
||||
for (IkeySubordinateRecord s : record.getSubordinates()) {
|
||||
IkeySubordinateModel sm = new IkeySubordinateModel();
|
||||
sm.setId(UUID.randomUUID());
|
||||
sm.setId(s.getId());
|
||||
sm.setRecord(m);
|
||||
sm.setFpr(s.getFingerprint());
|
||||
sm.setType(s.getType());
|
||||
|
@ -190,7 +192,7 @@ public class RxIkeyRepository implements IkeyRepository {
|
|||
subordinateRecords.add(sr);
|
||||
}
|
||||
}
|
||||
return new IkeyRecord(m.getJid(), m.getTimestamp(), m.getSuperordinate(), subordinateRecords);
|
||||
return new IkeyRecord(m.getJid(), m.getTimestamp(), m.getSuperordinate(), subordinateRecords, m.getTrust());
|
||||
})
|
||||
.doOnError(e -> LOGGER.log(Level.SEVERE, "Error loading contender ikey record", e));
|
||||
}
|
||||
|
@ -208,6 +210,7 @@ public class RxIkeyRepository implements IkeyRepository {
|
|||
m.setFingerprint(new OpenPgpV4Fingerprint(record.getSuperordinate()));
|
||||
m.setSuperordinate(record.getSuperordinate());
|
||||
m.setTimestamp(record.getTimestamp());
|
||||
m.setTrust(record.getTrust());
|
||||
|
||||
for (IkeySubordinateRecord s : record.getSubordinates()) {
|
||||
IkeySubordinateModel sm = new IkeySubordinateModel();
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package org.mercury_im.messenger.data.repository;
|
||||
|
||||
import org.jivesoftware.smackx.ikey.record.IkeyRecord;
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.core.data.repository.IkeyRecordRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
public class RxIkeyTrustRepository implements OpenPgpTrustRepository {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(RxIkeyTrustRepository.class.getName());
|
||||
|
||||
private final IkeyRecordRepository recordRepository;
|
||||
|
||||
@Inject
|
||||
public RxIkeyTrustRepository(IkeyRecordRepository ikeyRecordRepository) {
|
||||
this.recordRepository = ikeyRecordRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<OpenPgpTrustStore.Trust> loadTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint) {
|
||||
return recordRepository.loadRecord(accountId, owner)
|
||||
.filter(r -> r.hasSubordinate(fingerprint))
|
||||
.map(IkeyRecord::getTrust)
|
||||
.first(OpenPgpTrustStore.Trust.undecided);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable storeTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust) {
|
||||
return Completable.complete();
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@ import org.jxmpp.jid.EntityBareJid;
|
|||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.mercury_im.messenger.core.data.repository.AccountRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||
import org.mercury_im.messenger.core.util.Optional;
|
||||
import org.mercury_im.messenger.core.viewmodel.openpgp.FingerprintViewItem;
|
||||
import org.mercury_im.messenger.data.model.AnnouncedOpenPgpContactKey;
|
||||
|
@ -25,6 +26,7 @@ import java.util.Date;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
|
@ -40,7 +42,7 @@ import io.requery.query.ResultDelegate;
|
|||
import io.requery.query.Tuple;
|
||||
import io.requery.reactivex.ReactiveEntityStore;
|
||||
|
||||
public class RxOpenPgpRepository implements OpenPgpRepository {
|
||||
public class RxOpenPgpRepository implements OpenPgpRepository, OpenPgpTrustRepository {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(RxOpenPgpRepository.class.getName());
|
||||
|
||||
|
@ -132,6 +134,12 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
|||
|
||||
@Override
|
||||
public Completable storeAnnouncedFingerprints(UUID accountId, EntityBareJid owner, Map<OpenPgpV4Fingerprint, Date> metadata) {
|
||||
Set<OpenPgpV4Fingerprint> fingerprints = metadata.keySet();
|
||||
Completable deleteAll = data.delete(AnnouncedOpenPgpContactKey.class).where(
|
||||
AnnouncedOpenPgpContactKey.ACCOUNT_ID.eq(accountId)
|
||||
.and(AnnouncedOpenPgpContactKey.OWNER.eq(owner))
|
||||
.and(AnnouncedOpenPgpContactKey.FINGERPRINT.notIn(fingerprints)))
|
||||
.get().single().ignoreElement();
|
||||
List<AnnouncedOpenPgpContactKey> entities = new LinkedList<>();
|
||||
for (Map.Entry<OpenPgpV4Fingerprint, Date> entry : metadata.entrySet()) {
|
||||
AnnouncedOpenPgpContactKey entity = new AnnouncedOpenPgpContactKey();
|
||||
|
@ -141,9 +149,11 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
|||
entity.setModificationDate(entry.getValue());
|
||||
entities.add(entity);
|
||||
}
|
||||
return data.upsert(entities).ignoreElement()
|
||||
Completable upsertNew = data.upsert(entities).ignoreElement()
|
||||
.doOnComplete(() -> LOGGER.log(Level.INFO, "Successfully stored announced fingerprints of " +
|
||||
owner + " for account " + accountId + ": " + Arrays.toString(metadata.keySet().toArray())));
|
||||
|
||||
return deleteAll.andThen(upsertNew);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -196,12 +206,6 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
|||
.map(entity -> entity.getTrust() != null ? entity.getTrust() : OpenPgpTrustStore.Trust.undecided);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<OpenPgpTrustStore.Trust> loadTrust(UUID accountId, OpenPgpV4Fingerprint fingerprint) {
|
||||
return accountRepository.getAccount(accountId).toSingle()
|
||||
.flatMap(account -> loadTrust(accountId, account.getJid(), fingerprint));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable storeTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust) {
|
||||
OpenPgpKeyTrust entity = new OpenPgpKeyTrust();
|
||||
|
@ -212,12 +216,6 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
|||
return data.upsert(entity).ignoreElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable storeTrust(UUID accountId, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust) {
|
||||
return accountRepository.getAccount(accountId).toSingle()
|
||||
.flatMapCompletable(account -> storeTrust(accountId, account.getJid(), fingerprint, trust));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<List<OpenPgpV4Fingerprint>> observeFingerprintsOf(UUID accountId, String peerAddress) {
|
||||
return data.select(OpenPgpPublicKeyRing.class)
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package org.mercury_im.messenger.data.di.component;
|
||||
|
||||
import org.mercury_im.messenger.core.di.module.IkeyModule;
|
||||
import org.mercury_im.messenger.data.di.RepositoryModule;
|
||||
import org.mercury_im.messenger.data.di.module.SqliteTestDatabaseModule;
|
||||
import org.mercury_im.messenger.data.di.module.TestingSchedulerModule;
|
||||
import org.mercury_im.messenger.data.repository.AccountRepositoryTest;
|
||||
import org.mercury_im.messenger.data.repository.IkeyRepositoryTest;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
|
@ -11,6 +13,7 @@ import dagger.Component;
|
|||
|
||||
@Component(modules = {
|
||||
RepositoryModule.class,
|
||||
IkeyModule.class,
|
||||
SqliteTestDatabaseModule.class,
|
||||
TestingSchedulerModule.class
|
||||
})
|
||||
|
@ -18,4 +21,6 @@ import dagger.Component;
|
|||
public interface InMemoryDatabaseComponent {
|
||||
|
||||
void inject(AccountRepositoryTest test);
|
||||
|
||||
void inject(IkeyRepositoryTest test);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package org.mercury_im.messenger.data.repository;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.jivesoftware.smackx.ikey.record.IkeyRecord;
|
||||
import org.jivesoftware.smackx.ikey.record.OxSubordinateRecord;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.mercury_im.messenger.data.di.component.DaggerInMemoryDatabaseComponent;
|
||||
import org.mercury_im.messenger.data.di.component.InMemoryDatabaseComponent;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.net.URI;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.requery.Persistable;
|
||||
import io.requery.reactivex.ReactiveEntityStore;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class IkeyRepositoryTest {
|
||||
|
||||
@Inject
|
||||
ReactiveEntityStore<Persistable> dataStore;
|
||||
|
||||
@Inject
|
||||
RxIkeyRepository ikeyRepository;
|
||||
|
||||
@Inject
|
||||
public IkeyRepositoryTest() {
|
||||
InMemoryDatabaseComponent testComponent = DaggerInMemoryDatabaseComponent.builder()
|
||||
.build();
|
||||
testComponent.inject(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException {
|
||||
UUID accountId = UUID.randomUUID();
|
||||
EntityBareJid jid = JidCreate.entityBareFromOrThrowUnchecked("test@test.test");
|
||||
PGPPublicKeyRing publicKey = PGPainless.generateKeyRing().simpleEcKeyRing(jid.asEntityBareJidString()).getPublicKeys();
|
||||
Date date = new Date();
|
||||
|
||||
IkeyRecord firstRecord = new IkeyRecord();
|
||||
firstRecord.setSuperordinate(publicKey);
|
||||
firstRecord.setTimestamp(date);
|
||||
firstRecord.setJid(jid);
|
||||
|
||||
OxSubordinateRecord firstSub = new OxSubordinateRecord();
|
||||
firstSub.setUri(URI.create("xmpp.pubsub:pubsub.shakespeare.lit"));
|
||||
firstSub.setOxFingerprint(new OpenPgpV4Fingerprint("1357B01865B2503C18453D208CAC2A9678548E35"));
|
||||
firstRecord.getSubordinates().add(firstSub);
|
||||
|
||||
ikeyRepository.storeRecord(accountId, jid, firstRecord)
|
||||
.blockingAwait();
|
||||
IkeyRecord loaded = ikeyRepository.loadRecord(accountId, jid).blockingFirst();
|
||||
assertEquals(1, loaded.getSubordinates().size());
|
||||
assertEquals(firstSub, loaded.getSubordinates().get(0));
|
||||
|
||||
|
||||
IkeyRecord secondRecord = new IkeyRecord();
|
||||
secondRecord.setJid(jid);
|
||||
secondRecord.setTimestamp(new Date());
|
||||
secondRecord.setSuperordinate(publicKey);
|
||||
|
||||
OxSubordinateRecord secondSub = new OxSubordinateRecord();
|
||||
secondSub.setOxFingerprint(new OpenPgpV4Fingerprint("1357B01865B2503C18453D208CAC2A9678448E15"));
|
||||
secondSub.setUri(URI.create("xmpp.pubsub:pubsub.shakespeare.lot"));
|
||||
secondRecord.getSubordinates().add(secondSub);
|
||||
|
||||
ikeyRepository.storeRecord(accountId, jid, secondRecord)
|
||||
.blockingAwait();
|
||||
IkeyRecord loaded1 = ikeyRepository.loadRecord(accountId, jid).blockingFirst();
|
||||
assertEquals(1, loaded1.getSubordinates().size(), "Second record must only contain one subordinate.");
|
||||
assertEquals(secondSub, loaded1.getSubordinates().get(0));
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
|||
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
import org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil;
|
||||
import org.jivesoftware.smackx.ox.util.SecretKeyBackupHelper;
|
||||
import org.jivesoftware.smackx.pep.PepEventListener;
|
||||
|
@ -166,7 +167,9 @@ public final class IkeyManager extends Manager {
|
|||
public void storeAndPublishElement(IkeyElement ikeyElement)
|
||||
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, SmackException.NoResponseException, IOException, PGPException {
|
||||
store.storeIkeyRecord(connection().getUser().asEntityBareJid(), elementToRecord(ikeyElement));
|
||||
IkeyRecord record = elementToRecord(ikeyElement);
|
||||
record.setTrust(OpenPgpTrustStore.Trust.trusted);
|
||||
store.storeIkeyRecord(connection().getUser().asEntityBareJid(), record);
|
||||
publishIkeyElement(ikeyElement);
|
||||
}
|
||||
|
||||
|
@ -214,7 +217,9 @@ public final class IkeyManager extends Manager {
|
|||
return;
|
||||
}
|
||||
|
||||
if (existsSameOrNewerRecord(newRecord)) {
|
||||
IkeyRecord existingRecord = store.loadIkeyRecord(newRecord.getJid());
|
||||
|
||||
if (existsSameOrNewerRecord(newRecord, existingRecord)) {
|
||||
LOGGER.log(Level.WARNING, "There exists this exact, or a newer ikey record in the database for " + from);
|
||||
return;
|
||||
}
|
||||
|
@ -225,8 +230,17 @@ public final class IkeyManager extends Manager {
|
|||
}
|
||||
|
||||
if (isContenderElement(newRecord, store.loadIkeyRecord(from))) {
|
||||
LOGGER.log(Level.INFO, "Storing contender element for " + from);
|
||||
store.storeContenderIkeyRecord(from, newRecord);
|
||||
} else {
|
||||
if (existingRecord != null) {
|
||||
newRecord.setTrust(existingRecord.getTrust());
|
||||
}
|
||||
PGPSecretKeyRing secretKeys = store.loadSecretKey();
|
||||
if (secretKeys != null && new OpenPgpV4Fingerprint(secretKeys).equals(new OpenPgpV4Fingerprint(newRecord.getSuperordinate()))) {
|
||||
newRecord.setTrust(OpenPgpTrustStore.Trust.trusted);
|
||||
}
|
||||
LOGGER.log(Level.INFO, "Storing ikey record " + newRecord);
|
||||
store.storeIkeyRecord(from, newRecord);
|
||||
}
|
||||
}
|
||||
|
@ -321,8 +335,7 @@ public final class IkeyManager extends Manager {
|
|||
return timestamp.after(now);
|
||||
}
|
||||
|
||||
private boolean existsSameOrNewerRecord(IkeyRecord record) throws IOException {
|
||||
IkeyRecord existingRecord = store.loadIkeyRecord(record.getJid());
|
||||
private boolean existsSameOrNewerRecord(IkeyRecord record, IkeyRecord existingRecord) throws IOException {
|
||||
if (existingRecord == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package org.jivesoftware.smackx.ikey.record;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
@ -10,7 +13,9 @@ import java.util.List;
|
|||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
public class IkeyRecord implements Serializable {
|
||||
|
||||
@Getter
|
||||
|
@ -18,7 +23,6 @@ public class IkeyRecord implements Serializable {
|
|||
private PGPPublicKeyRing superordinate;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private final List<IkeySubordinateRecord> subordinates = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
|
@ -29,14 +33,31 @@ public class IkeyRecord implements Serializable {
|
|||
@Setter
|
||||
private EntityBareJid jid;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private OpenPgpTrustStore.Trust trust = OpenPgpTrustStore.Trust.undecided;
|
||||
|
||||
public IkeyRecord() {
|
||||
|
||||
}
|
||||
|
||||
public IkeyRecord(EntityBareJid jid, Date timestamp, PGPPublicKeyRing superordinate, List<IkeySubordinateRecord> subordinates) {
|
||||
public IkeyRecord(EntityBareJid jid, Date timestamp, PGPPublicKeyRing superordinate, List<IkeySubordinateRecord> subordinates, OpenPgpTrustStore.Trust trust) {
|
||||
this.jid = jid;
|
||||
this.timestamp = timestamp;
|
||||
this.superordinate = superordinate;
|
||||
this.subordinates.addAll(subordinates);
|
||||
this.trust = trust;
|
||||
}
|
||||
|
||||
public boolean hasSubordinate(OpenPgpV4Fingerprint fingerprint) {
|
||||
for (IkeySubordinateRecord subordinateRecord : subordinates) {
|
||||
if (subordinateRecord.getType().equals(OxSubordinateRecord.TYPE)) {
|
||||
OxSubordinateRecord record = (OxSubordinateRecord) subordinateRecord;
|
||||
if (record.getOxFingerprint().equals(fingerprint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package org.jivesoftware.smackx.ikey.record;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface IkeySubordinateRecord {
|
||||
|
||||
UUID getId();
|
||||
|
||||
String getType();
|
||||
|
||||
String getFingerprint();
|
||||
|
|
|
@ -6,11 +6,17 @@ import org.pgpainless.key.OpenPgpV4Fingerprint;
|
|||
import java.net.URI;
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@EqualsAndHashCode
|
||||
public class OxSubordinateRecord implements IkeySubordinateRecord {
|
||||
|
||||
public static final String TYPE = OpenPgpElement.NAMESPACE;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private UUID id;
|
||||
|
||||
public OxSubordinateRecord() {
|
||||
|
@ -33,7 +39,7 @@ public class OxSubordinateRecord implements IkeySubordinateRecord {
|
|||
|
||||
@Override
|
||||
public String getType() {
|
||||
return OpenPgpElement.NAMESPACE;
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package org.jivesoftware.smackx.ikey.util;
|
||||
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class IkeyTrust {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
OpenPgpTrustStore.Trust trust;
|
||||
}
|
|
@ -6,6 +6,7 @@ import org.mercury_im.messenger.core.SchedulersFacade;
|
|||
import org.mercury_im.messenger.core.connection.state.ConnectionPoolState;
|
||||
import org.mercury_im.messenger.core.connection.state.ConnectionState;
|
||||
import org.mercury_im.messenger.core.crypto.MercuryOpenPgpManager;
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyInitializer;
|
||||
import org.mercury_im.messenger.core.data.repository.AccountRepository;
|
||||
import org.mercury_im.messenger.core.store.caps.MercuryEntityCapsStore;
|
||||
import org.mercury_im.messenger.core.store.message.MercuryMessageStore;
|
||||
|
@ -43,6 +44,7 @@ public class MercuryConnectionManager {
|
|||
private final AccountRepository accountRepository;
|
||||
private final RosterStoreBinder rosterStoreBinder;
|
||||
private final MercuryOpenPgpManager cryptoManager;
|
||||
private final IkeyInitializer ikeyInitializer;
|
||||
private final SchedulersFacade schedulers;
|
||||
|
||||
private final Map<UUID, MercuryConnection> connectionsMap = new ConcurrentHashMap<>();
|
||||
|
@ -63,12 +65,14 @@ public class MercuryConnectionManager {
|
|||
MercuryMessageStoreFactory messageStoreFactory,
|
||||
XmppConnectionFactory connectionFactory,
|
||||
MercuryOpenPgpManager cryptoManager,
|
||||
IkeyInitializer ikeyInitializer,
|
||||
SchedulersFacade schedulers) {
|
||||
this.accountRepository = accountRepository;
|
||||
this.rosterStoreBinder = rosterStoreBinder;
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.messageStoreFactory = messageStoreFactory;
|
||||
this.cryptoManager = cryptoManager;
|
||||
this.ikeyInitializer = ikeyInitializer;
|
||||
this.schedulers = schedulers;
|
||||
|
||||
EntityCapsManager.setPersistentCache(entityCapsStore);
|
||||
|
@ -170,6 +174,7 @@ public class MercuryConnectionManager {
|
|||
chatManager.addIncomingListener(mercuryMessageStore);
|
||||
}));
|
||||
cryptoManager.initialize(connection);
|
||||
ikeyInitializer.initFor(connection);
|
||||
}
|
||||
|
||||
private synchronized void handleOptionalAccountChangedEvent(MercuryConnection connection, Optional<Account> event) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.mercury_im.messenger.core.crypto;
|
|||
|
||||
import org.jivesoftware.smack.AbstractConnectionListener;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smackx.ikey.record.IkeyRecord;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.jivesoftware.smackx.ox.callback.SecretKeyPassphraseCallback;
|
||||
|
@ -14,10 +15,15 @@ import org.jivesoftware.smackx.ox_im.OXInstantMessagingManager;
|
|||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||
import org.mercury_im.messenger.core.SchedulersFacade;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.DirectChatRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.IkeyKeyRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.IkeyRecordRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.MessageRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.PeerRepository;
|
||||
import org.mercury_im.messenger.core.store.crypto.IkeyAwareOpenPgpStore;
|
||||
import org.mercury_im.messenger.core.store.crypto.MercuryOpenPgpStore;
|
||||
import org.mercury_im.messenger.core.store.message.MercuryMessageStore;
|
||||
|
||||
|
@ -34,6 +40,8 @@ public class MercuryOpenPgpManager {
|
|||
private final DirectChatRepository directChatRepository;
|
||||
private final MessageRepository messageRepository;
|
||||
private final OpenPgpRepository openPgpRepository;
|
||||
private final OpenPgpTrustRepository openPgpTrustRepository;
|
||||
private final IkeyRepository ikeyRepository;
|
||||
private final SchedulersFacade schedulers;
|
||||
private final OpenPgpSecretKeyBackupPassphraseGenerator passphraseGenerator;
|
||||
private final LocalOxKeyGenerationStrategy keyGenerationStrategy;
|
||||
|
@ -43,16 +51,20 @@ public class MercuryOpenPgpManager {
|
|||
DirectChatRepository directChatRepository,
|
||||
MessageRepository messageRepository,
|
||||
OpenPgpRepository openPgpRepository,
|
||||
OpenPgpTrustRepository openPgpTrustRepository,
|
||||
OpenPgpSecretKeyBackupPassphraseGenerator passphraseGenerator,
|
||||
LocalOxKeyGenerationStrategy keyGenerationStrategy,
|
||||
IkeyRepository ikeyRepository,
|
||||
SchedulersFacade schedulers) {
|
||||
this.peerRepository = peerRepository;
|
||||
this.directChatRepository = directChatRepository;
|
||||
this.messageRepository = messageRepository;
|
||||
this.openPgpRepository = openPgpRepository;
|
||||
this.openPgpTrustRepository = openPgpTrustRepository;
|
||||
this.schedulers = schedulers;
|
||||
this.keyGenerationStrategy = keyGenerationStrategy;
|
||||
this.passphraseGenerator = passphraseGenerator;
|
||||
this.ikeyRepository = ikeyRepository;
|
||||
}
|
||||
|
||||
public void initialize(MercuryConnection connection) {
|
||||
|
@ -71,13 +83,11 @@ public class MercuryOpenPgpManager {
|
|||
}
|
||||
|
||||
private void setup(MercuryConnection connection) {
|
||||
OpenPgpStore store = new MercuryOpenPgpStore(connection.getAccountId(), openPgpRepository, schedulers);
|
||||
OpenPgpStore store = new IkeyAwareOpenPgpStore(connection.getAccountId(), openPgpRepository, openPgpTrustRepository, ikeyRepository, schedulers);
|
||||
OpenPgpProvider provider = new PainlessOpenPgpProvider(store);
|
||||
OpenPgpManager oxManager = OpenPgpManager.getInstanceFor(connection.getConnection());
|
||||
oxManager.setOpenPgpProvider(provider);
|
||||
OpenPgpSecretKeyBackupPassphrase passphrase = passphraseGenerator.generateBackupPassphrase();
|
||||
|
||||
|
||||
generateAndPublish(connection, oxManager, passphrase);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package org.mercury_im.messenger.core.data.repository;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.jivesoftware.smackx.ikey.util.IkeyTrust;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.core.util.Optional;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
|
@ -11,7 +11,6 @@ import org.pgpainless.key.OpenPgpV4Fingerprint;
|
|||
import java.util.UUID;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
|
@ -47,15 +46,15 @@ public interface IkeyKeyRepository {
|
|||
|
||||
Completable storeBackupPassphrase(UUID accountID, OpenPgpSecretKeyBackupPassphrase passphrase);
|
||||
|
||||
default Observable<Optional<IkeyTrust>> loadSuperordinateTrust(Account account, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint) {
|
||||
default Observable<Optional<OpenPgpTrustStore.Trust>> loadSuperordinateTrust(Account account, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint) {
|
||||
return loadSuperordinateTrust(account.getId(), jid, fingerprint);
|
||||
}
|
||||
|
||||
Observable<Optional<IkeyTrust>> loadSuperordinateTrust(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint);
|
||||
Observable<Optional<OpenPgpTrustStore.Trust>> loadSuperordinateTrust(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint);
|
||||
|
||||
default Completable storeSuperordinateTrust(Account account, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint, IkeyTrust trust) {
|
||||
default Completable storeSuperordinateTrust(Account account, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust) {
|
||||
return storeSuperordinateTrust(account.getId(), jid, fingerprint, trust);
|
||||
}
|
||||
|
||||
Completable storeSuperordinateTrust(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint, IkeyTrust trust);
|
||||
Completable storeSuperordinateTrust(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust);
|
||||
}
|
||||
|
|
|
@ -43,14 +43,6 @@ public interface OpenPgpRepository {
|
|||
|
||||
Single<Date> loadAnnouncementDate(UUID accountId, OpenPgpV4Fingerprint fingerprint);
|
||||
|
||||
Single<OpenPgpTrustStore.Trust> loadTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint);
|
||||
|
||||
Single<OpenPgpTrustStore.Trust> loadTrust(UUID accountId, OpenPgpV4Fingerprint fingerprint);
|
||||
|
||||
Completable storeTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust);
|
||||
|
||||
Completable storeTrust(UUID accountId, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust);
|
||||
|
||||
Observable<List<OpenPgpV4Fingerprint>> observeFingerprintsOf(UUID accountId, String peerAddress);
|
||||
|
||||
Observable<Optional<OpenPgpV4Fingerprint>> observeLocalFingerprintOf(UUID accountId);
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package org.mercury_im.messenger.core.data.repository;
|
||||
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
public interface OpenPgpTrustRepository {
|
||||
|
||||
Single<OpenPgpTrustStore.Trust> loadTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint);
|
||||
|
||||
Completable storeTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust);
|
||||
|
||||
}
|
|
@ -2,6 +2,7 @@ package org.mercury_im.messenger.core.di.module;
|
|||
|
||||
import org.mercury_im.messenger.core.crypto.LocalOxKeyGenerationStrategy;
|
||||
import org.mercury_im.messenger.core.crypto.OxPlusIkeyKeyGenerationStrategy;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.mercury_im.messenger.core.data.repository.AccountRepository;
|
|||
import org.mercury_im.messenger.core.data.repository.DirectChatRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.MessageRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.PeerRepository;
|
||||
import org.mercury_im.messenger.core.viewmodel.account.LoginViewModel;
|
||||
import org.mercury_im.messenger.core.viewmodel.account.detail.AccountDetailsViewModel;
|
||||
|
@ -45,11 +46,12 @@ public class ViewModelModule {
|
|||
@Singleton
|
||||
static AccountDetailsViewModel provideAccountDetailsViewModel(MercuryConnectionManager connectionManager,
|
||||
OpenPgpRepository openPgpRepository,
|
||||
OpenPgpTrustRepository trustRepository,
|
||||
IkeyRepository ikeyRepository,
|
||||
AccountRepository accountRepository,
|
||||
SchedulersFacade schedulers,
|
||||
IkeyInitializer ikeyInitializer) {
|
||||
return new AccountDetailsViewModel(connectionManager, openPgpRepository, ikeyRepository, accountRepository, schedulers, ikeyInitializer);
|
||||
return new AccountDetailsViewModel(connectionManager, openPgpRepository, trustRepository, ikeyRepository, accountRepository, schedulers, ikeyInitializer);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
package org.mercury_im.messenger.core.store.crypto;
|
||||
|
||||
import org.jivesoftware.smackx.ikey.record.IkeyRecord;
|
||||
import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpTrustStore;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.core.SchedulersFacade;
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.IkeyKeyRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.IkeyRecordRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
||||
public class IkeyAwareOpenPgpStore extends MercuryOpenPgpStore {
|
||||
|
||||
public IkeyAwareOpenPgpStore(UUID accountId,
|
||||
OpenPgpRepository openPgpRepository,
|
||||
OpenPgpTrustRepository openPgpTrustRepository,
|
||||
IkeyRepository ikeyRepository,
|
||||
SchedulersFacade schedulersFacade) {
|
||||
this(accountId, openPgpRepository, openPgpTrustRepository, ikeyRepository, ikeyRepository, schedulersFacade);
|
||||
}
|
||||
|
||||
public IkeyAwareOpenPgpStore(UUID accountId,
|
||||
OpenPgpRepository openPgpRepository,
|
||||
OpenPgpTrustRepository openPgpTrustRepository,
|
||||
IkeyKeyRepository ikeyKeyRepository,
|
||||
IkeyRecordRepository ikeyRecordRepository,
|
||||
SchedulersFacade schedulers) {
|
||||
super(new KeyStore(accountId, openPgpRepository, schedulers),
|
||||
new MetadataStore(accountId, openPgpRepository, schedulers),
|
||||
new TrustStore(accountId, openPgpTrustRepository, ikeyKeyRepository, ikeyRecordRepository, schedulers));
|
||||
}
|
||||
|
||||
public static class TrustStore extends AbstractOpenPgpTrustStore {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(TrustStore.class.getName());
|
||||
|
||||
private final UUID accountId;
|
||||
private final OpenPgpTrustRepository openPgpTrustRepository;
|
||||
private final IkeyKeyRepository ikeyKeyRepository;
|
||||
private final IkeyRecordRepository ikeyRecordRepository;
|
||||
private final SchedulersFacade schedulers;
|
||||
|
||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||
|
||||
public TrustStore(UUID accountId, OpenPgpTrustRepository openPgpTrustRepository, IkeyKeyRepository ikeyKeyRepository, IkeyRecordRepository ikeyRecordRepository, SchedulersFacade schedulersFacade) {
|
||||
this.accountId = accountId;
|
||||
this.openPgpTrustRepository = openPgpTrustRepository;
|
||||
this.ikeyKeyRepository = ikeyKeyRepository;
|
||||
this.ikeyRecordRepository = ikeyRecordRepository;
|
||||
this.schedulers = schedulersFacade;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Trust readTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException {
|
||||
EntityBareJid jid = owner.asEntityBareJidOrThrow();
|
||||
Trust trust = readIkeyTrust(jid, fingerprint)
|
||||
.blockingGet();
|
||||
return trust == null ? Trust.undecided : trust;
|
||||
}
|
||||
|
||||
private Single<Trust> readIkeyTrust(EntityBareJid owner, OpenPgpV4Fingerprint fingerprint) {
|
||||
return ikeyRecordRepository.loadRecord(accountId, owner)
|
||||
.filter(record -> record.getTrust() == Trust.trusted)
|
||||
.filter(record -> record.hasSubordinate(fingerprint))
|
||||
.map(IkeyRecord::getTrust)
|
||||
.map(trust -> trust == Trust.trusted ? Trust.ikey_trusted : trust)
|
||||
.firstElement()
|
||||
.flatMap(t -> t == Trust.undecided ? readManualTrust(owner, fingerprint).toMaybe() : Maybe.just(t))
|
||||
.switchIfEmpty(readManualTrust(owner, fingerprint));
|
||||
}
|
||||
|
||||
private Single<Trust> readManualTrust(EntityBareJid owner, OpenPgpV4Fingerprint fingerprint) {
|
||||
return openPgpTrustRepository.loadTrust(accountId, owner, fingerprint)
|
||||
.doOnSuccess(trust -> LOGGER.log(Level.INFO, "Read manual trust " + trust + " for device key " + fingerprint + " of contact " + owner));
|
||||
}
|
||||
|
||||
private Completable writeManualTrust(EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, Trust trust) {
|
||||
return openPgpTrustRepository.storeTrust(accountId, owner.asEntityBareJidIfPossible(), fingerprint, trust)
|
||||
.doOnComplete(() -> LOGGER.log(Level.INFO,
|
||||
"Successfully marked device key " + fingerprint + " of " + owner + " as " + trust));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint, Trust trust) throws IOException {
|
||||
EntityBareJid jid = owner.asEntityBareJidOrThrow();
|
||||
disposable.add(
|
||||
writeManualTrust(jid, fingerprint, trust)
|
||||
.compose(schedulers.executeUiSafeCompletable())
|
||||
.subscribe(() -> {},
|
||||
e -> LOGGER.log(Level.SEVERE, "An error happened while marking device key " + fingerprint + " of " + jid + " as " + trust, e)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpTrustStore;
|
|||
import org.jxmpp.jid.BareJid;
|
||||
import org.mercury_im.messenger.core.SchedulersFacade;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -26,14 +27,20 @@ public class MercuryOpenPgpStore extends AbstractOpenPgpStore {
|
|||
|
||||
protected static final Logger LOGGER = Logger.getLogger(MercuryOpenPgpStore.class.getName());
|
||||
|
||||
public MercuryOpenPgpStore(UUID accountId, OpenPgpRepository repository, SchedulersFacade schedulers) {
|
||||
super(
|
||||
public MercuryOpenPgpStore(UUID accountId, OpenPgpRepository repository, OpenPgpTrustRepository trustRepository, SchedulersFacade schedulers) {
|
||||
this(
|
||||
new KeyStore(accountId, repository, schedulers),
|
||||
new MetadataStore(accountId, repository, schedulers),
|
||||
new TrustStore(accountId, repository, schedulers));
|
||||
new TrustStore(accountId, trustRepository, schedulers));
|
||||
}
|
||||
|
||||
private static class KeyStore extends AbstractOpenPgpKeyStore {
|
||||
public MercuryOpenPgpStore(AbstractOpenPgpKeyStore keyStore,
|
||||
AbstractOpenPgpMetadataStore metadataStore,
|
||||
AbstractOpenPgpTrustStore trustStore) {
|
||||
super(keyStore, metadataStore, trustStore);
|
||||
}
|
||||
|
||||
public static class KeyStore extends AbstractOpenPgpKeyStore {
|
||||
|
||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||
private final OpenPgpRepository repository;
|
||||
|
@ -117,7 +124,7 @@ public class MercuryOpenPgpStore extends AbstractOpenPgpStore {
|
|||
}
|
||||
}
|
||||
|
||||
private static class MetadataStore extends AbstractOpenPgpMetadataStore {
|
||||
public static class MetadataStore extends AbstractOpenPgpMetadataStore {
|
||||
|
||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||
private final OpenPgpRepository repository;
|
||||
|
@ -141,7 +148,8 @@ public class MercuryOpenPgpStore extends AbstractOpenPgpStore {
|
|||
@Override
|
||||
protected void writeAnnouncedFingerprintsOf(BareJid contact, Map<OpenPgpV4Fingerprint, Date> metadata)
|
||||
throws IOException {
|
||||
disposable.add(repository.storeAnnouncedFingerprints(accountId, contact.asEntityBareJidIfPossible(), metadata)
|
||||
disposable.add(
|
||||
repository.storeAnnouncedFingerprints(accountId, contact.asEntityBareJidIfPossible(), metadata)
|
||||
.subscribeOn(schedulers.getIoScheduler())
|
||||
.subscribe(
|
||||
() -> MercuryOpenPgpStore.LOGGER.log(Level.FINER, "Successfully updated announced OX fingerprints for " + contact + " (accountId=" + accountId + ")"),
|
||||
|
@ -159,12 +167,12 @@ public class MercuryOpenPgpStore extends AbstractOpenPgpStore {
|
|||
public static class TrustStore extends AbstractOpenPgpTrustStore {
|
||||
|
||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||
private final OpenPgpRepository repository;
|
||||
private final OpenPgpTrustRepository repository;
|
||||
private final SchedulersFacade schedulers;
|
||||
|
||||
private final UUID accountId;
|
||||
|
||||
public TrustStore(UUID accountId, OpenPgpRepository repository, SchedulersFacade schedulers) {
|
||||
public TrustStore(UUID accountId, OpenPgpTrustRepository repository, SchedulersFacade schedulers) {
|
||||
this.accountId = accountId;
|
||||
this.repository = repository;
|
||||
this.schedulers = schedulers;
|
||||
|
|
|
@ -5,8 +5,10 @@ import org.jivesoftware.smack.XMPPConnection;
|
|||
import org.jivesoftware.smackx.ikey.IkeyManager;
|
||||
import org.jivesoftware.smackx.ikey.element.IkeyElement;
|
||||
import org.jivesoftware.smackx.ikey.element.SubordinateElement;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
||||
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore;
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
import org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil;
|
||||
import org.jivesoftware.smackx.pep.PepManager;
|
||||
|
@ -25,6 +27,8 @@ import org.mercury_im.messenger.core.crypto.ikey.IkeyInitializer;
|
|||
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.AccountRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||
import org.mercury_im.messenger.core.store.crypto.IkeyAwareOpenPgpStore;
|
||||
import org.mercury_im.messenger.core.util.Optional;
|
||||
import org.mercury_im.messenger.core.viewmodel.MercuryViewModel;
|
||||
import org.mercury_im.messenger.core.viewmodel.openpgp.FingerprintViewItem;
|
||||
|
@ -55,6 +59,7 @@ public class AccountDetailsViewModel implements MercuryViewModel {
|
|||
|
||||
private MercuryConnectionManager connectionManager;
|
||||
private final OpenPgpRepository openPgpRepository;
|
||||
private final OpenPgpTrustRepository deviceKeyTrustRepository;
|
||||
private final IkeyRepository ikeyRepository;
|
||||
private final AccountRepository accountRepository;
|
||||
private final SchedulersFacade schedulers;
|
||||
|
@ -63,12 +68,14 @@ public class AccountDetailsViewModel implements MercuryViewModel {
|
|||
@Inject
|
||||
public AccountDetailsViewModel(MercuryConnectionManager connectionManager,
|
||||
OpenPgpRepository openPgpRepository,
|
||||
OpenPgpTrustRepository deviceKeyTrustRepository,
|
||||
IkeyRepository ikeyRepository,
|
||||
AccountRepository accountRepository,
|
||||
SchedulersFacade schedulers,
|
||||
IkeyInitializer ikeyInitializer) {
|
||||
this.connectionManager = connectionManager;
|
||||
this.openPgpRepository = openPgpRepository;
|
||||
this.deviceKeyTrustRepository = deviceKeyTrustRepository;
|
||||
this.ikeyRepository = ikeyRepository;
|
||||
this.accountRepository = accountRepository;
|
||||
this.schedulers = schedulers;
|
||||
|
@ -76,9 +83,14 @@ public class AccountDetailsViewModel implements MercuryViewModel {
|
|||
}
|
||||
|
||||
public void markFingerprintTrusted(UUID accountId, OpenPgpV4Fingerprint fingerprint, boolean trusted) {
|
||||
addDisposable(openPgpRepository.storeTrust(accountId, fingerprint, trusted ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted)
|
||||
OpenPgpTrustStore.Trust trust = trusted ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted;
|
||||
addDisposable(accountRepository.getAccount(accountId)
|
||||
.toSingle()
|
||||
.flatMapCompletable(account -> deviceKeyTrustRepository
|
||||
.storeTrust(account.getId(), account.getJid(), fingerprint, trust))
|
||||
.compose(schedulers.executeUiSafeCompletable())
|
||||
.subscribe());
|
||||
.subscribe(() -> LOGGER.log(Level.INFO, "Fingerprint " + fingerprint + " marked " + trust),
|
||||
e -> LOGGER.log(Level.SEVERE, "An exception occurred marking fingerprint " + fingerprint + " as " + trusted, e)));
|
||||
}
|
||||
|
||||
public void sendIkeyElement(UUID accountId) {
|
||||
|
@ -150,12 +162,14 @@ public class AccountDetailsViewModel implements MercuryViewModel {
|
|||
.map(key -> key.isPresent() ? new Optional<>(new OpenPgpV4Fingerprint(key.getItem().getPublicKey())) : new Optional<>());
|
||||
}
|
||||
|
||||
public Observable<Optional<OpenPgpV4Fingerprint>> observeLocalFingerprint(UUID accountId) {
|
||||
public Observable<Optional<OpenPgpV4Fingerprint>> observeLocalDeviceFingerprint(UUID accountId) {
|
||||
return openPgpRepository.observeLocalFingerprintOf(accountId);
|
||||
}
|
||||
|
||||
public Observable<List<FingerprintViewItem>> observeRemoteFingerprints(UUID accountId) {
|
||||
return observeLocalFingerprint(accountId)
|
||||
public Observable<List<FingerprintViewItem>> observeRemoteDeviceFingerprints(UUID accountId) {
|
||||
OpenPgpStore store = OpenPgpManager.getInstanceFor(connectionManager.getConnection(accountId).getConnection())
|
||||
.getOpenPgpProvider().getStore();
|
||||
return observeLocalDeviceFingerprint(accountId)
|
||||
.flatMap(optional -> accountRepository.getAccount(accountId).toSingle()
|
||||
.flatMapObservable(account -> openPgpRepository.observeFingerprints(accountId, account.getJid())
|
||||
.map(list -> {
|
||||
|
@ -165,6 +179,7 @@ public class AccountDetailsViewModel implements MercuryViewModel {
|
|||
|
||||
List<FingerprintViewItem> remoteFingerprints = new ArrayList<>();
|
||||
for(FingerprintViewItem f : list) {
|
||||
f.setTrusted(store.getTrust(f.getOwner(), f.getFingerprint()));
|
||||
if (!f.getFingerprint().equals(optional.getItem())) {
|
||||
remoteFingerprints.add(f);
|
||||
}
|
||||
|
|
|
@ -6,17 +6,19 @@ import org.jivesoftware.smack.packet.Presence;
|
|||
import org.jivesoftware.smack.roster.Roster;
|
||||
import org.jivesoftware.smack.roster.RosterEntry;
|
||||
import org.jivesoftware.smack.roster.RosterGroup;
|
||||
import org.jivesoftware.smackx.ikey.util.IkeyTrust;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
||||
import org.jivesoftware.smackx.ox.crypto.OpenPgpProvider;
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore;
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.jxmpp.jid.Jid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.mercury_im.messenger.core.SchedulersFacade;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.DirectChatRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.PeerRepository;
|
||||
import org.mercury_im.messenger.core.util.CombinedPresenceListener;
|
||||
import org.mercury_im.messenger.core.util.Optional;
|
||||
|
@ -27,10 +29,13 @@ import org.mercury_im.messenger.entity.chat.Chat;
|
|||
import org.mercury_im.messenger.entity.contact.Peer;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
@ -42,10 +47,13 @@ import lombok.Getter;
|
|||
|
||||
public class ContactDetailViewModel implements MercuryViewModel {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ContactDetailViewModel.class.getName());
|
||||
|
||||
private final MercuryConnectionManager connectionManager;
|
||||
private final PeerRepository peerRepository;
|
||||
private final DirectChatRepository directChatRepository;
|
||||
private final OpenPgpRepository openPgpRepository;
|
||||
private final OpenPgpTrustRepository trustRepository;
|
||||
private final IkeyRepository ikeyRepository;
|
||||
private final SchedulersFacade schedulers;
|
||||
|
||||
|
@ -62,6 +70,8 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
|||
private BehaviorSubject<List<FingerprintViewItem>> contactDeviceFingerprints = BehaviorSubject.createDefault(Collections.emptyList());
|
||||
private BehaviorSubject<Tuple<String, EntityBareJid>> contactAvatarBase = BehaviorSubject.create();
|
||||
|
||||
private OpenPgpStore openPgpStore;
|
||||
|
||||
@Getter
|
||||
private UUID peerId;
|
||||
|
||||
|
@ -73,12 +83,14 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
|||
PeerRepository peerRepository,
|
||||
DirectChatRepository directChatRepository,
|
||||
OpenPgpRepository openPgpRepository,
|
||||
OpenPgpTrustRepository trustRepository,
|
||||
IkeyRepository ikeyRepository,
|
||||
SchedulersFacade schedulers) {
|
||||
this.connectionManager = connectionManager;
|
||||
this.peerRepository = peerRepository;
|
||||
this.directChatRepository = directChatRepository;
|
||||
this.openPgpRepository = openPgpRepository;
|
||||
this.trustRepository = trustRepository;
|
||||
this.ikeyRepository = ikeyRepository;
|
||||
this.schedulers = schedulers;
|
||||
}
|
||||
|
@ -89,6 +101,10 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
|||
}
|
||||
|
||||
public Completable init(Peer peer) {
|
||||
OpenPgpManager openPgpManager = OpenPgpManager.getInstanceFor(connectionManager.getConnection(peer.getAccount().getId()).getConnection());
|
||||
OpenPgpProvider provider = openPgpManager.getOpenPgpProvider();
|
||||
this.openPgpStore = provider.getStore();
|
||||
|
||||
return Completable.fromAction(() -> {
|
||||
this.peerId = peer.getId();
|
||||
this.accountId = peer.getAccount().getId();
|
||||
|
@ -109,11 +125,19 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
|||
contactAvatarBase.onNext(new Tuple<>(peer.getDisplayName(), peer.getJid()));
|
||||
}));
|
||||
|
||||
// Device keys
|
||||
addDisposable(openPgpRepository
|
||||
.observeFingerprints(peer.getAccount().getId(), peer.getJid())
|
||||
.map(viewItems -> {
|
||||
for (FingerprintViewItem item : viewItems) {
|
||||
item.setTrusted(openPgpStore.getTrust(item.getOwner(), item.getFingerprint()));
|
||||
}
|
||||
return viewItems;
|
||||
})
|
||||
.compose(schedulers.executeUiSafeObservable())
|
||||
.subscribe(fingerprints -> contactDeviceFingerprints.onNext(fingerprints)));
|
||||
|
||||
// Ikey
|
||||
addDisposable(ikeyRepository
|
||||
.loadRecord(peer.getAccount().getId(), peer.getJid())
|
||||
.compose(schedulers.executeUiSafeObservable())
|
||||
|
@ -121,7 +145,7 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
|||
peer.getAccount().getId(), record.getJid(),
|
||||
new OpenPgpV4Fingerprint(record.getSuperordinate()),
|
||||
record.getTimestamp(), record.getTimestamp(),
|
||||
OpenPgpTrustStore.Trust.trusted) // TODO
|
||||
record.getTrust())
|
||||
)
|
||||
.map(Optional::new)
|
||||
.subscribe(contactIdentityFingerprint::onNext));
|
||||
|
@ -219,17 +243,15 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
public void markDeviceFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, boolean checked) {
|
||||
openPgpRepository.storeTrust(accountId, contactAddress.getValue(), fingerprint,
|
||||
checked ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted)
|
||||
.subscribe();
|
||||
public void markDeviceFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, boolean checked) throws IOException {
|
||||
openPgpStore.setTrust(getContactAddress().blockingFirst(), fingerprint, checked ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted);
|
||||
}
|
||||
|
||||
public void markIkeyFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, EntityBareJid owner, boolean isChecked) {
|
||||
IkeyTrust trust = new IkeyTrust();
|
||||
trust.setTrust(isChecked ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted);
|
||||
ikeyRepository.storeSuperordinateTrust(accountId, owner, fingerprint, trust)
|
||||
.subscribe();
|
||||
OpenPgpTrustStore.Trust trust = isChecked ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted;
|
||||
addDisposable(ikeyRepository.storeSuperordinateTrust(accountId, owner, fingerprint, trust)
|
||||
.subscribe(() -> LOGGER.log(Level.INFO, "Marked Ikey " + fingerprint + " of " + owner + " as " + trust),
|
||||
e -> LOGGER.log(Level.SEVERE, "Error marking ikey " + fingerprint + " of " + owner + " as " + trust, e)));
|
||||
}
|
||||
|
||||
public static abstract class FilteredPresenceEventListener extends CombinedPresenceListener {
|
||||
|
|
|
@ -23,6 +23,6 @@ public class FingerprintViewItem {
|
|||
OpenPgpTrustStore.Trust trusted;
|
||||
|
||||
public boolean isTrusted() {
|
||||
return trusted == OpenPgpTrustStore.Trust.trusted;
|
||||
return trusted == OpenPgpTrustStore.Trust.trusted || trusted == OpenPgpTrustStore.Trust.ikey_trusted;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue