Ikey decisions are synced after reconnect
This commit is contained in:
parent
b67af2c702
commit
4b9e9f9ae9
|
@ -53,7 +53,7 @@ public class AndroidAccountDetailsViewModel extends AndroidViewModel implements
|
||||||
LOGGER.log(Level.INFO, "Creating AndroidAccountDetailsViewModel");
|
LOGGER.log(Level.INFO, "Creating AndroidAccountDetailsViewModel");
|
||||||
((MercuryImApplication) application).getAppComponent().inject(this);
|
((MercuryImApplication) application).getAppComponent().inject(this);
|
||||||
|
|
||||||
addDisposable(getCommonViewModel().observeLocalFingerprint(accountId)
|
addDisposable(getCommonViewModel().observeLocalDeviceFingerprint(accountId)
|
||||||
.compose(schedulers.executeUiSafeObservable())
|
.compose(schedulers.executeUiSafeObservable())
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::getItem)
|
.map(Optional::getItem)
|
||||||
|
@ -64,7 +64,7 @@ public class AndroidAccountDetailsViewModel extends AndroidViewModel implements
|
||||||
.subscribe(ikeyFingerprint::postValue,
|
.subscribe(ikeyFingerprint::postValue,
|
||||||
e -> LOGGER.log(Level.SEVERE, "Error displaying ikey fingerprint", e)));
|
e -> LOGGER.log(Level.SEVERE, "Error displaying ikey fingerprint", e)));
|
||||||
|
|
||||||
addDisposable(getCommonViewModel().observeRemoteFingerprints(accountId)
|
addDisposable(getCommonViewModel().observeRemoteDeviceFingerprints(accountId)
|
||||||
.compose(schedulers.executeUiSafeObservable())
|
.compose(schedulers.executeUiSafeObservable())
|
||||||
.subscribe(list -> {
|
.subscribe(list -> {
|
||||||
LOGGER.log(Level.INFO, "Set remote fingerprints to list: " + Arrays.toString(list.toArray()));
|
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.Completable;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
public class AndroidContactDetailViewModel extends ViewModel implements MercuryAndroidViewModel<ContactDetailViewModel> {
|
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) {
|
public void markDeviceFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, boolean checked) {
|
||||||
commonViewModel.markDeviceFingerprintTrusted(fingerprint, checked);
|
commonViewModel.markDeviceFingerprintTrusted(fingerprint, checked);
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,8 +57,10 @@ public class ToggleableFingerprintsAdapter extends RecyclerView.Adapter<Toggleab
|
||||||
holder.fingerprintTimestamp.setText(dateFormat.format(f.getModificationDate()));
|
holder.fingerprintTimestamp.setText(dateFormat.format(f.getModificationDate()));
|
||||||
|
|
||||||
holder.trustSwitch.setChecked(f.isTrusted());
|
holder.trustSwitch.setChecked(f.isTrusted());
|
||||||
holder.trustSwitch.setOnCheckedChangeListener(
|
holder.trustSwitch.setOnClickListener(v -> {
|
||||||
(buttonView, isChecked) -> toggleListener.onFingerprintToggled(fingerprint, isChecked));
|
boolean checked = ((Switch) v).isChecked();
|
||||||
|
toggleListener.onFingerprintToggled(fingerprint, checked);
|
||||||
|
});
|
||||||
holder.divider.setVisibility(position == fingerprints.size() - 1 ? View.GONE : View.VISIBLE);
|
holder.divider.setVisibility(position == fingerprints.size() - 1 ? View.GONE : View.VISIBLE);
|
||||||
|
|
||||||
holder.itemView.setOnLongClickListener(v -> {
|
holder.itemView.setOnLongClickListener(v -> {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.mercury_im.messenger.data.di;
|
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.crypto.ikey.IkeyRepository;
|
||||||
import org.mercury_im.messenger.core.data.repository.AccountRepository;
|
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.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.IkeyRecordRepository;
|
||||||
import org.mercury_im.messenger.core.data.repository.MessageRepository;
|
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.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.data.repository.PeerRepository;
|
||||||
import org.mercury_im.messenger.data.mapping.AccountMapping;
|
import org.mercury_im.messenger.data.mapping.AccountMapping;
|
||||||
import org.mercury_im.messenger.data.mapping.DirectChatMapping;
|
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.RxEntityCapsRepository;
|
||||||
import org.mercury_im.messenger.data.repository.RxGroupChatRepository;
|
import org.mercury_im.messenger.data.repository.RxGroupChatRepository;
|
||||||
import org.mercury_im.messenger.data.repository.RxIkeyRepository;
|
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.RxMessageRepository;
|
||||||
import org.mercury_im.messenger.data.repository.RxOpenPgpRepository;
|
import org.mercury_im.messenger.data.repository.RxOpenPgpRepository;
|
||||||
import org.mercury_im.messenger.data.repository.RxPeerRepository;
|
import org.mercury_im.messenger.data.repository.RxPeerRepository;
|
||||||
|
@ -100,4 +103,11 @@ public class RepositoryModule {
|
||||||
static IkeyRepository provideIkeyRepository(ReactiveEntityStore<Persistable> data) {
|
static IkeyRepository provideIkeyRepository(ReactiveEntityStore<Persistable> data) {
|
||||||
return new RxIkeyRepository(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;
|
package org.mercury_im.messenger.data.model;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.mercury_im.messenger.data.converter.Base64PGPPublicKeyRingConverter;
|
import org.mercury_im.messenger.data.converter.Base64PGPPublicKeyRingConverter;
|
||||||
import org.mercury_im.messenger.data.converter.EntityBareJidConverter;
|
import org.mercury_im.messenger.data.converter.EntityBareJidConverter;
|
||||||
|
@ -15,23 +16,28 @@ import io.requery.CascadeAction;
|
||||||
import io.requery.Column;
|
import io.requery.Column;
|
||||||
import io.requery.Convert;
|
import io.requery.Convert;
|
||||||
import io.requery.Entity;
|
import io.requery.Entity;
|
||||||
|
import io.requery.Index;
|
||||||
import io.requery.Key;
|
import io.requery.Key;
|
||||||
import io.requery.OneToMany;
|
import io.requery.OneToMany;
|
||||||
import io.requery.Table;
|
import io.requery.Table;
|
||||||
import io.requery.converter.UUIDConverter;
|
import io.requery.converter.UUIDConverter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
@Table(name = "ikey_record")
|
@Table(name = "ikey_record", uniqueIndexes = "unique_ikey_record")
|
||||||
@Entity
|
@Entity
|
||||||
|
@ToString
|
||||||
public class AbstractIkeyRecordModel {
|
public class AbstractIkeyRecordModel {
|
||||||
|
|
||||||
@Key
|
@Key
|
||||||
@Convert(UUIDConverter.class)
|
@Convert(UUIDConverter.class)
|
||||||
UUID id;
|
UUID id;
|
||||||
|
|
||||||
@Column(name = "account")
|
@Index("unique_ikey_record")
|
||||||
|
@Column
|
||||||
@Convert(UUIDConverter.class)
|
@Convert(UUIDConverter.class)
|
||||||
UUID accountId;
|
UUID accountId;
|
||||||
|
|
||||||
|
@Index("unique_ikey_record")
|
||||||
@Column(name = "jid")
|
@Column(name = "jid")
|
||||||
@Convert(EntityBareJidConverter.class)
|
@Convert(EntityBareJidConverter.class)
|
||||||
EntityBareJid jid;
|
EntityBareJid jid;
|
||||||
|
@ -50,7 +56,11 @@ public class AbstractIkeyRecordModel {
|
||||||
@Convert(Base64PGPPublicKeyRingConverter.class)
|
@Convert(Base64PGPPublicKeyRingConverter.class)
|
||||||
PGPPublicKeyRing superordinate;
|
PGPPublicKeyRing superordinate;
|
||||||
|
|
||||||
|
@Index("unique_ikey_record")
|
||||||
@Column(name = "fingerprint")
|
@Column(name = "fingerprint")
|
||||||
@Convert(OpenPgpV4FingerprintConverter.class)
|
@Convert(OpenPgpV4FingerprintConverter.class)
|
||||||
OpenPgpV4Fingerprint fingerprint;
|
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.IkeyRecord;
|
||||||
import org.jivesoftware.smackx.ikey.record.IkeySubordinateRecord;
|
import org.jivesoftware.smackx.ikey.record.IkeySubordinateRecord;
|
||||||
import org.jivesoftware.smackx.ikey.record.OxSubordinateRecord;
|
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.OpenPgpSecretKeyBackupPassphrase;
|
||||||
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
||||||
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
|
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
|
||||||
import org.mercury_im.messenger.core.util.Optional;
|
import org.mercury_im.messenger.core.util.Optional;
|
||||||
import org.mercury_im.messenger.data.model.IkeyRecordModel;
|
import org.mercury_im.messenger.data.model.IkeyRecordModel;
|
||||||
import org.mercury_im.messenger.data.model.IkeySecretKeyModel;
|
import org.mercury_im.messenger.data.model.IkeySecretKeyModel;
|
||||||
import org.mercury_im.messenger.data.model.IkeySubordinateModel;
|
import org.mercury_im.messenger.data.model.IkeySubordinateModel;
|
||||||
import org.mercury_im.messenger.data.model.IkeyTrustModel;
|
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -23,6 +22,8 @@ import java.util.UUID;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import io.reactivex.Completable;
|
import io.reactivex.Completable;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
@ -36,6 +37,7 @@ public class RxIkeyRepository implements IkeyRepository {
|
||||||
|
|
||||||
private final ReactiveEntityStore<Persistable> data;
|
private final ReactiveEntityStore<Persistable> data;
|
||||||
|
|
||||||
|
@Inject
|
||||||
public RxIkeyRepository(ReactiveEntityStore<Persistable> data) {
|
public RxIkeyRepository(ReactiveEntityStore<Persistable> data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
@ -102,30 +104,27 @@ public class RxIkeyRepository implements IkeyRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Observable<Optional<IkeyTrust>> loadSuperordinateTrust(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint) {
|
public Observable<Optional<OpenPgpTrustStore.Trust>> loadSuperordinateTrust(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint) {
|
||||||
return data.select(IkeyTrustModel.class)
|
return loadRecord(accountId, jid)
|
||||||
.where(IkeyTrustModel.ACCOUNT_ID.eq(accountId).and(IkeyTrustModel.JID.eq(jid).and(IkeyTrustModel.FINGERPRINT.eq(fingerprint))))
|
.map(model -> {
|
||||||
.get()
|
if (fingerprint.equals(new OpenPgpV4Fingerprint(model.getSuperordinate()))) {
|
||||||
.observableResult()
|
return new Optional<>(model.getTrust());
|
||||||
.map(r -> {
|
} else {
|
||||||
IkeyTrustModel m = r.firstOrNull();
|
|
||||||
if (m == null) {
|
|
||||||
return new Optional<>();
|
return new Optional<>();
|
||||||
}
|
}
|
||||||
IkeyTrust e = new IkeyTrust();
|
|
||||||
e.setTrust(m.getTrust());
|
|
||||||
return new Optional<>(e);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Completable storeSuperordinateTrust(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint, IkeyTrust trust) {
|
public Completable storeSuperordinateTrust(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust) {
|
||||||
IkeyTrustModel model = new IkeyTrustModel();
|
return loadRecord(accountId, jid)
|
||||||
model.setAccountId(accountId);
|
.map(record -> {
|
||||||
model.setJid(jid);
|
record.setTrust(trust);
|
||||||
model.setFingerprint(fingerprint);
|
return record;
|
||||||
model.setTrust(trust.getTrust());
|
})
|
||||||
return data.upsert(model).ignoreElement();
|
.firstOrError()
|
||||||
|
.doOnSuccess(m -> LOGGER.log(Level.INFO, "First Record: " + m))
|
||||||
|
.flatMapCompletable(record -> storeRecord(accountId, jid, record));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -136,11 +135,13 @@ public class RxIkeyRepository implements IkeyRepository {
|
||||||
for (IkeySubordinateModel sub : m.getSubordinates()) {
|
for (IkeySubordinateModel sub : m.getSubordinates()) {
|
||||||
if (sub.getType().equals(OpenPgpElement.NAMESPACE)) {
|
if (sub.getType().equals(OpenPgpElement.NAMESPACE)) {
|
||||||
OxSubordinateRecord r = new OxSubordinateRecord();
|
OxSubordinateRecord r = new OxSubordinateRecord();
|
||||||
|
r.setId(sub.getId());
|
||||||
r.setOxFingerprint(new OpenPgpV4Fingerprint(sub.getFpr()));
|
r.setOxFingerprint(new OpenPgpV4Fingerprint(sub.getFpr()));
|
||||||
r.setUri(sub.getUri());
|
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));
|
.doOnError(e -> LOGGER.log(Level.SEVERE, "Error loading ikey record", e));
|
||||||
}
|
}
|
||||||
|
@ -149,8 +150,11 @@ public class RxIkeyRepository implements IkeyRepository {
|
||||||
public Completable storeRecord(UUID accountId, EntityBareJid jid, IkeyRecord record) {
|
public Completable storeRecord(UUID accountId, EntityBareJid jid, IkeyRecord record) {
|
||||||
assert jid.equals(record.getJid());
|
assert jid.equals(record.getJid());
|
||||||
return getRecordModel(accountId, jid, false)
|
return getRecordModel(accountId, jid, false)
|
||||||
|
.doOnNext(m -> LOGGER.log(Level.INFO, "Loaded model: " + m))
|
||||||
.single(new IkeyRecordModel())
|
.single(new IkeyRecordModel())
|
||||||
|
.doOnSuccess(m -> LOGGER.log(Level.INFO, "Singled model: " + m))
|
||||||
.map(m -> {
|
.map(m -> {
|
||||||
|
LOGGER.log(Level.INFO, "Mapping model: " + m);
|
||||||
if (m.getId() == null) m.setId(UUID.randomUUID());
|
if (m.getId() == null) m.setId(UUID.randomUUID());
|
||||||
m.setAccountId(accountId);
|
m.setAccountId(accountId);
|
||||||
m.setJid(jid);
|
m.setJid(jid);
|
||||||
|
@ -158,22 +162,26 @@ public class RxIkeyRepository implements IkeyRepository {
|
||||||
m.setFingerprint(new OpenPgpV4Fingerprint(record.getSuperordinate()));
|
m.setFingerprint(new OpenPgpV4Fingerprint(record.getSuperordinate()));
|
||||||
m.setSuperordinate(record.getSuperordinate());
|
m.setSuperordinate(record.getSuperordinate());
|
||||||
m.setTimestamp(record.getTimestamp());
|
m.setTimestamp(record.getTimestamp());
|
||||||
|
m.setTrust(record.getTrust());
|
||||||
|
|
||||||
m.getSubordinates().clear();
|
m.getSubordinates().clear();
|
||||||
for (IkeySubordinateRecord s : record.getSubordinates()) {
|
for (IkeySubordinateRecord s : record.getSubordinates()) {
|
||||||
IkeySubordinateModel sm = new IkeySubordinateModel();
|
IkeySubordinateModel sm = new IkeySubordinateModel();
|
||||||
sm.setId(UUID.randomUUID());
|
sm.setId(s.getId());
|
||||||
sm.setRecord(m);
|
sm.setRecord(m);
|
||||||
sm.setFpr(s.getFingerprint());
|
sm.setFpr(s.getFingerprint());
|
||||||
sm.setType(s.getType());
|
sm.setType(s.getType());
|
||||||
sm.setUri(s.getUri());
|
sm.setUri(s.getUri());
|
||||||
m.getSubordinates().add(sm);
|
m.getSubordinates().add(sm);
|
||||||
}
|
}
|
||||||
|
LOGGER.log(Level.INFO, "Return model: " + m);
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
})
|
})
|
||||||
.flatMap(data::upsert)
|
.flatMap(data::upsert)
|
||||||
|
.doOnSuccess(m -> LOGGER.log(Level.INFO, "Upserting model: " + m))
|
||||||
.ignoreElement()
|
.ignoreElement()
|
||||||
|
.doOnComplete(() -> LOGGER.log(Level.INFO, "Updated model"))
|
||||||
.doOnError(e -> LOGGER.log(Level.SEVERE, "Error storing ikey record", e));
|
.doOnError(e -> LOGGER.log(Level.SEVERE, "Error storing ikey record", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +198,7 @@ public class RxIkeyRepository implements IkeyRepository {
|
||||||
subordinateRecords.add(sr);
|
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));
|
.doOnError(e -> LOGGER.log(Level.SEVERE, "Error loading contender ikey record", e));
|
||||||
}
|
}
|
||||||
|
@ -208,6 +216,7 @@ public class RxIkeyRepository implements IkeyRepository {
|
||||||
m.setFingerprint(new OpenPgpV4Fingerprint(record.getSuperordinate()));
|
m.setFingerprint(new OpenPgpV4Fingerprint(record.getSuperordinate()));
|
||||||
m.setSuperordinate(record.getSuperordinate());
|
m.setSuperordinate(record.getSuperordinate());
|
||||||
m.setTimestamp(record.getTimestamp());
|
m.setTimestamp(record.getTimestamp());
|
||||||
|
m.setTrust(record.getTrust());
|
||||||
|
|
||||||
for (IkeySubordinateRecord s : record.getSubordinates()) {
|
for (IkeySubordinateRecord s : record.getSubordinates()) {
|
||||||
IkeySubordinateModel sm = new IkeySubordinateModel();
|
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.jxmpp.jid.impl.JidCreate;
|
||||||
import org.mercury_im.messenger.core.data.repository.AccountRepository;
|
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.OpenPgpRepository;
|
||||||
|
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||||
import org.mercury_im.messenger.core.util.Optional;
|
import org.mercury_im.messenger.core.util.Optional;
|
||||||
import org.mercury_im.messenger.core.viewmodel.openpgp.FingerprintViewItem;
|
import org.mercury_im.messenger.core.viewmodel.openpgp.FingerprintViewItem;
|
||||||
import org.mercury_im.messenger.data.model.AnnouncedOpenPgpContactKey;
|
import org.mercury_im.messenger.data.model.AnnouncedOpenPgpContactKey;
|
||||||
|
@ -25,6 +26,7 @@ import java.util.Date;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
@ -40,7 +42,7 @@ import io.requery.query.ResultDelegate;
|
||||||
import io.requery.query.Tuple;
|
import io.requery.query.Tuple;
|
||||||
import io.requery.reactivex.ReactiveEntityStore;
|
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());
|
private static final Logger LOGGER = Logger.getLogger(RxOpenPgpRepository.class.getName());
|
||||||
|
|
||||||
|
@ -132,6 +134,12 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Completable storeAnnouncedFingerprints(UUID accountId, EntityBareJid owner, Map<OpenPgpV4Fingerprint, Date> metadata) {
|
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<>();
|
List<AnnouncedOpenPgpContactKey> entities = new LinkedList<>();
|
||||||
for (Map.Entry<OpenPgpV4Fingerprint, Date> entry : metadata.entrySet()) {
|
for (Map.Entry<OpenPgpV4Fingerprint, Date> entry : metadata.entrySet()) {
|
||||||
AnnouncedOpenPgpContactKey entity = new AnnouncedOpenPgpContactKey();
|
AnnouncedOpenPgpContactKey entity = new AnnouncedOpenPgpContactKey();
|
||||||
|
@ -141,9 +149,11 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
||||||
entity.setModificationDate(entry.getValue());
|
entity.setModificationDate(entry.getValue());
|
||||||
entities.add(entity);
|
entities.add(entity);
|
||||||
}
|
}
|
||||||
return data.upsert(entities).ignoreElement()
|
Completable upsertNew = data.upsert(entities).ignoreElement()
|
||||||
.doOnComplete(() -> LOGGER.log(Level.INFO, "Successfully stored announced fingerprints of " +
|
.doOnComplete(() -> LOGGER.log(Level.INFO, "Successfully stored announced fingerprints of " +
|
||||||
owner + " for account " + accountId + ": " + Arrays.toString(metadata.keySet().toArray())));
|
owner + " for account " + accountId + ": " + Arrays.toString(metadata.keySet().toArray())));
|
||||||
|
|
||||||
|
return deleteAll.andThen(upsertNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -196,12 +206,6 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
||||||
.map(entity -> entity.getTrust() != null ? entity.getTrust() : OpenPgpTrustStore.Trust.undecided);
|
.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
|
@Override
|
||||||
public Completable storeTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust) {
|
public Completable storeTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust) {
|
||||||
OpenPgpKeyTrust entity = new OpenPgpKeyTrust();
|
OpenPgpKeyTrust entity = new OpenPgpKeyTrust();
|
||||||
|
@ -212,12 +216,6 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
||||||
return data.upsert(entity).ignoreElement();
|
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
|
@Override
|
||||||
public Observable<List<OpenPgpV4Fingerprint>> observeFingerprintsOf(UUID accountId, String peerAddress) {
|
public Observable<List<OpenPgpV4Fingerprint>> observeFingerprintsOf(UUID accountId, String peerAddress) {
|
||||||
return data.select(OpenPgpPublicKeyRing.class)
|
return data.select(OpenPgpPublicKeyRing.class)
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package org.mercury_im.messenger.data.di.component;
|
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.RepositoryModule;
|
||||||
import org.mercury_im.messenger.data.di.module.SqliteTestDatabaseModule;
|
import org.mercury_im.messenger.data.di.module.SqliteTestDatabaseModule;
|
||||||
import org.mercury_im.messenger.data.di.module.TestingSchedulerModule;
|
import org.mercury_im.messenger.data.di.module.TestingSchedulerModule;
|
||||||
import org.mercury_im.messenger.data.repository.AccountRepositoryTest;
|
import org.mercury_im.messenger.data.repository.AccountRepositoryTest;
|
||||||
|
import org.mercury_im.messenger.data.repository.IkeyRepositoryTest;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
@ -11,6 +13,7 @@ import dagger.Component;
|
||||||
|
|
||||||
@Component(modules = {
|
@Component(modules = {
|
||||||
RepositoryModule.class,
|
RepositoryModule.class,
|
||||||
|
IkeyModule.class,
|
||||||
SqliteTestDatabaseModule.class,
|
SqliteTestDatabaseModule.class,
|
||||||
TestingSchedulerModule.class
|
TestingSchedulerModule.class
|
||||||
})
|
})
|
||||||
|
@ -18,4 +21,6 @@ import dagger.Component;
|
||||||
public interface InMemoryDatabaseComponent {
|
public interface InMemoryDatabaseComponent {
|
||||||
|
|
||||||
void inject(AccountRepositoryTest test);
|
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.OpenPgpElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||||
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
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.OpenPgpPubSubUtil;
|
||||||
import org.jivesoftware.smackx.ox.util.SecretKeyBackupHelper;
|
import org.jivesoftware.smackx.ox.util.SecretKeyBackupHelper;
|
||||||
import org.jivesoftware.smackx.pep.PepEventListener;
|
import org.jivesoftware.smackx.pep.PepEventListener;
|
||||||
|
@ -166,7 +167,9 @@ public final class IkeyManager extends Manager {
|
||||||
public void storeAndPublishElement(IkeyElement ikeyElement)
|
public void storeAndPublishElement(IkeyElement ikeyElement)
|
||||||
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
||||||
SmackException.NotConnectedException, SmackException.NoResponseException, IOException, PGPException {
|
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);
|
publishIkeyElement(ikeyElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +217,9 @@ public final class IkeyManager extends Manager {
|
||||||
return;
|
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);
|
LOGGER.log(Level.WARNING, "There exists this exact, or a newer ikey record in the database for " + from);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -225,8 +230,17 @@ public final class IkeyManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isContenderElement(newRecord, store.loadIkeyRecord(from))) {
|
if (isContenderElement(newRecord, store.loadIkeyRecord(from))) {
|
||||||
|
LOGGER.log(Level.INFO, "Storing contender element for " + from);
|
||||||
store.storeContenderIkeyRecord(from, newRecord);
|
store.storeContenderIkeyRecord(from, newRecord);
|
||||||
} else {
|
} 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);
|
store.storeIkeyRecord(from, newRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,8 +335,7 @@ public final class IkeyManager extends Manager {
|
||||||
return timestamp.after(now);
|
return timestamp.after(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean existsSameOrNewerRecord(IkeyRecord record) throws IOException {
|
private boolean existsSameOrNewerRecord(IkeyRecord record, IkeyRecord existingRecord) throws IOException {
|
||||||
IkeyRecord existingRecord = store.loadIkeyRecord(record.getJid());
|
|
||||||
if (existingRecord == null) {
|
if (existingRecord == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package org.jivesoftware.smackx.ikey.record;
|
package org.jivesoftware.smackx.ikey.record;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
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.jxmpp.jid.EntityBareJid;
|
||||||
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -10,7 +13,9 @@ import java.util.List;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
public class IkeyRecord implements Serializable {
|
public class IkeyRecord implements Serializable {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -18,7 +23,6 @@ public class IkeyRecord implements Serializable {
|
||||||
private PGPPublicKeyRing superordinate;
|
private PGPPublicKeyRing superordinate;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
|
||||||
private final List<IkeySubordinateRecord> subordinates = new ArrayList<>();
|
private final List<IkeySubordinateRecord> subordinates = new ArrayList<>();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -29,14 +33,31 @@ public class IkeyRecord implements Serializable {
|
||||||
@Setter
|
@Setter
|
||||||
private EntityBareJid jid;
|
private EntityBareJid jid;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private OpenPgpTrustStore.Trust trust = OpenPgpTrustStore.Trust.undecided;
|
||||||
|
|
||||||
public IkeyRecord() {
|
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.jid = jid;
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.superordinate = superordinate;
|
this.superordinate = superordinate;
|
||||||
this.subordinates.addAll(subordinates);
|
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;
|
package org.jivesoftware.smackx.ikey.record;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public interface IkeySubordinateRecord {
|
public interface IkeySubordinateRecord {
|
||||||
|
|
||||||
|
UUID getId();
|
||||||
|
|
||||||
String getType();
|
String getType();
|
||||||
|
|
||||||
String getFingerprint();
|
String getFingerprint();
|
||||||
|
|
|
@ -6,11 +6,17 @@ import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@EqualsAndHashCode
|
||||||
public class OxSubordinateRecord implements IkeySubordinateRecord {
|
public class OxSubordinateRecord implements IkeySubordinateRecord {
|
||||||
|
|
||||||
|
public static final String TYPE = OpenPgpElement.NAMESPACE;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
private UUID id;
|
private UUID id;
|
||||||
|
|
||||||
public OxSubordinateRecord() {
|
public OxSubordinateRecord() {
|
||||||
|
@ -33,7 +39,7 @@ public class OxSubordinateRecord implements IkeySubordinateRecord {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return OpenPgpElement.NAMESPACE;
|
return TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.ConnectionPoolState;
|
||||||
import org.mercury_im.messenger.core.connection.state.ConnectionState;
|
import org.mercury_im.messenger.core.connection.state.ConnectionState;
|
||||||
import org.mercury_im.messenger.core.crypto.MercuryOpenPgpManager;
|
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.data.repository.AccountRepository;
|
||||||
import org.mercury_im.messenger.core.store.caps.MercuryEntityCapsStore;
|
import org.mercury_im.messenger.core.store.caps.MercuryEntityCapsStore;
|
||||||
import org.mercury_im.messenger.core.store.message.MercuryMessageStore;
|
import org.mercury_im.messenger.core.store.message.MercuryMessageStore;
|
||||||
|
@ -43,6 +44,7 @@ public class MercuryConnectionManager {
|
||||||
private final AccountRepository accountRepository;
|
private final AccountRepository accountRepository;
|
||||||
private final RosterStoreBinder rosterStoreBinder;
|
private final RosterStoreBinder rosterStoreBinder;
|
||||||
private final MercuryOpenPgpManager cryptoManager;
|
private final MercuryOpenPgpManager cryptoManager;
|
||||||
|
private final IkeyInitializer ikeyInitializer;
|
||||||
private final SchedulersFacade schedulers;
|
private final SchedulersFacade schedulers;
|
||||||
|
|
||||||
private final Map<UUID, MercuryConnection> connectionsMap = new ConcurrentHashMap<>();
|
private final Map<UUID, MercuryConnection> connectionsMap = new ConcurrentHashMap<>();
|
||||||
|
@ -63,12 +65,14 @@ public class MercuryConnectionManager {
|
||||||
MercuryMessageStoreFactory messageStoreFactory,
|
MercuryMessageStoreFactory messageStoreFactory,
|
||||||
XmppConnectionFactory connectionFactory,
|
XmppConnectionFactory connectionFactory,
|
||||||
MercuryOpenPgpManager cryptoManager,
|
MercuryOpenPgpManager cryptoManager,
|
||||||
|
IkeyInitializer ikeyInitializer,
|
||||||
SchedulersFacade schedulers) {
|
SchedulersFacade schedulers) {
|
||||||
this.accountRepository = accountRepository;
|
this.accountRepository = accountRepository;
|
||||||
this.rosterStoreBinder = rosterStoreBinder;
|
this.rosterStoreBinder = rosterStoreBinder;
|
||||||
this.connectionFactory = connectionFactory;
|
this.connectionFactory = connectionFactory;
|
||||||
this.messageStoreFactory = messageStoreFactory;
|
this.messageStoreFactory = messageStoreFactory;
|
||||||
this.cryptoManager = cryptoManager;
|
this.cryptoManager = cryptoManager;
|
||||||
|
this.ikeyInitializer = ikeyInitializer;
|
||||||
this.schedulers = schedulers;
|
this.schedulers = schedulers;
|
||||||
|
|
||||||
EntityCapsManager.setPersistentCache(entityCapsStore);
|
EntityCapsManager.setPersistentCache(entityCapsStore);
|
||||||
|
@ -170,6 +174,7 @@ public class MercuryConnectionManager {
|
||||||
chatManager.addIncomingListener(mercuryMessageStore);
|
chatManager.addIncomingListener(mercuryMessageStore);
|
||||||
}));
|
}));
|
||||||
cryptoManager.initialize(connection);
|
cryptoManager.initialize(connection);
|
||||||
|
ikeyInitializer.initFor(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void handleOptionalAccountChangedEvent(MercuryConnection connection, Optional<Account> event) {
|
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.AbstractConnectionListener;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smackx.ikey.record.IkeyRecord;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||||
import org.jivesoftware.smackx.ox.callback.SecretKeyPassphraseCallback;
|
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.jivesoftware.smackx.pubsub.PubSubException;
|
||||||
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;
|
||||||
|
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.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.MessageRepository;
|
||||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
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.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.crypto.MercuryOpenPgpStore;
|
||||||
import org.mercury_im.messenger.core.store.message.MercuryMessageStore;
|
import org.mercury_im.messenger.core.store.message.MercuryMessageStore;
|
||||||
|
|
||||||
|
@ -34,6 +40,8 @@ public class MercuryOpenPgpManager {
|
||||||
private final DirectChatRepository directChatRepository;
|
private final DirectChatRepository directChatRepository;
|
||||||
private final MessageRepository messageRepository;
|
private final MessageRepository messageRepository;
|
||||||
private final OpenPgpRepository openPgpRepository;
|
private final OpenPgpRepository openPgpRepository;
|
||||||
|
private final OpenPgpTrustRepository openPgpTrustRepository;
|
||||||
|
private final IkeyRepository ikeyRepository;
|
||||||
private final SchedulersFacade schedulers;
|
private final SchedulersFacade schedulers;
|
||||||
private final OpenPgpSecretKeyBackupPassphraseGenerator passphraseGenerator;
|
private final OpenPgpSecretKeyBackupPassphraseGenerator passphraseGenerator;
|
||||||
private final LocalOxKeyGenerationStrategy keyGenerationStrategy;
|
private final LocalOxKeyGenerationStrategy keyGenerationStrategy;
|
||||||
|
@ -43,16 +51,20 @@ public class MercuryOpenPgpManager {
|
||||||
DirectChatRepository directChatRepository,
|
DirectChatRepository directChatRepository,
|
||||||
MessageRepository messageRepository,
|
MessageRepository messageRepository,
|
||||||
OpenPgpRepository openPgpRepository,
|
OpenPgpRepository openPgpRepository,
|
||||||
|
OpenPgpTrustRepository openPgpTrustRepository,
|
||||||
OpenPgpSecretKeyBackupPassphraseGenerator passphraseGenerator,
|
OpenPgpSecretKeyBackupPassphraseGenerator passphraseGenerator,
|
||||||
LocalOxKeyGenerationStrategy keyGenerationStrategy,
|
LocalOxKeyGenerationStrategy keyGenerationStrategy,
|
||||||
|
IkeyRepository ikeyRepository,
|
||||||
SchedulersFacade schedulers) {
|
SchedulersFacade schedulers) {
|
||||||
this.peerRepository = peerRepository;
|
this.peerRepository = peerRepository;
|
||||||
this.directChatRepository = directChatRepository;
|
this.directChatRepository = directChatRepository;
|
||||||
this.messageRepository = messageRepository;
|
this.messageRepository = messageRepository;
|
||||||
this.openPgpRepository = openPgpRepository;
|
this.openPgpRepository = openPgpRepository;
|
||||||
|
this.openPgpTrustRepository = openPgpTrustRepository;
|
||||||
this.schedulers = schedulers;
|
this.schedulers = schedulers;
|
||||||
this.keyGenerationStrategy = keyGenerationStrategy;
|
this.keyGenerationStrategy = keyGenerationStrategy;
|
||||||
this.passphraseGenerator = passphraseGenerator;
|
this.passphraseGenerator = passphraseGenerator;
|
||||||
|
this.ikeyRepository = ikeyRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize(MercuryConnection connection) {
|
public void initialize(MercuryConnection connection) {
|
||||||
|
@ -71,13 +83,11 @@ public class MercuryOpenPgpManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setup(MercuryConnection connection) {
|
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);
|
OpenPgpProvider provider = new PainlessOpenPgpProvider(store);
|
||||||
OpenPgpManager oxManager = OpenPgpManager.getInstanceFor(connection.getConnection());
|
OpenPgpManager oxManager = OpenPgpManager.getInstanceFor(connection.getConnection());
|
||||||
oxManager.setOpenPgpProvider(provider);
|
oxManager.setOpenPgpProvider(provider);
|
||||||
OpenPgpSecretKeyBackupPassphrase passphrase = passphraseGenerator.generateBackupPassphrase();
|
OpenPgpSecretKeyBackupPassphrase passphrase = passphraseGenerator.generateBackupPassphrase();
|
||||||
|
|
||||||
|
|
||||||
generateAndPublish(connection, oxManager, passphrase);
|
generateAndPublish(connection, oxManager, passphrase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package org.mercury_im.messenger.core.data.repository;
|
package org.mercury_im.messenger.core.data.repository;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.jivesoftware.smackx.ikey.util.IkeyTrust;
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||||
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.mercury_im.messenger.core.util.Optional;
|
import org.mercury_im.messenger.core.util.Optional;
|
||||||
import org.mercury_im.messenger.entity.Account;
|
import org.mercury_im.messenger.entity.Account;
|
||||||
|
@ -11,7 +11,6 @@ import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -47,15 +46,15 @@ public interface IkeyKeyRepository {
|
||||||
|
|
||||||
Completable storeBackupPassphrase(UUID accountID, OpenPgpSecretKeyBackupPassphrase passphrase);
|
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);
|
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);
|
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<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<List<OpenPgpV4Fingerprint>> observeFingerprintsOf(UUID accountId, String peerAddress);
|
||||||
|
|
||||||
Observable<Optional<OpenPgpV4Fingerprint>> observeLocalFingerprintOf(UUID accountId);
|
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.LocalOxKeyGenerationStrategy;
|
||||||
import org.mercury_im.messenger.core.crypto.OxPlusIkeyKeyGenerationStrategy;
|
import org.mercury_im.messenger.core.crypto.OxPlusIkeyKeyGenerationStrategy;
|
||||||
|
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
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.DirectChatRepository;
|
||||||
import org.mercury_im.messenger.core.data.repository.MessageRepository;
|
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.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.data.repository.PeerRepository;
|
||||||
import org.mercury_im.messenger.core.viewmodel.account.LoginViewModel;
|
import org.mercury_im.messenger.core.viewmodel.account.LoginViewModel;
|
||||||
import org.mercury_im.messenger.core.viewmodel.account.detail.AccountDetailsViewModel;
|
import org.mercury_im.messenger.core.viewmodel.account.detail.AccountDetailsViewModel;
|
||||||
|
@ -45,11 +46,12 @@ public class ViewModelModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
static AccountDetailsViewModel provideAccountDetailsViewModel(MercuryConnectionManager connectionManager,
|
static AccountDetailsViewModel provideAccountDetailsViewModel(MercuryConnectionManager connectionManager,
|
||||||
OpenPgpRepository openPgpRepository,
|
OpenPgpRepository openPgpRepository,
|
||||||
|
OpenPgpTrustRepository trustRepository,
|
||||||
IkeyRepository ikeyRepository,
|
IkeyRepository ikeyRepository,
|
||||||
AccountRepository accountRepository,
|
AccountRepository accountRepository,
|
||||||
SchedulersFacade schedulers,
|
SchedulersFacade schedulers,
|
||||||
IkeyInitializer ikeyInitializer) {
|
IkeyInitializer ikeyInitializer) {
|
||||||
return new AccountDetailsViewModel(connectionManager, openPgpRepository, ikeyRepository, accountRepository, schedulers, ikeyInitializer);
|
return new AccountDetailsViewModel(connectionManager, openPgpRepository, trustRepository, ikeyRepository, accountRepository, schedulers, ikeyInitializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
package org.mercury_im.messenger.core.store.crypto;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.ikey.mechanism.IkeyType;
|
||||||
|
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.mercury_im.messenger.core.util.Optional;
|
||||||
|
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.Observable;
|
||||||
|
import io.reactivex.Single;
|
||||||
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
import io.reactivex.subjects.BehaviorSubject;
|
||||||
|
|
||||||
|
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();
|
||||||
|
private final BehaviorSubject<Boolean> accountIsIkeyAware = BehaviorSubject.createDefault(false);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
accountIsIkeyAware().subscribe(accountIsIkeyAware);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Trust readTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException {
|
||||||
|
EntityBareJid jid = owner.asEntityBareJidOrThrow();
|
||||||
|
Trust trust = accountIsIkeyAware.firstOrError()
|
||||||
|
.flatMap(isAware -> isAware ?
|
||||||
|
readIkeyTrust(jid, fingerprint) :
|
||||||
|
readManualTrust(jid, fingerprint))
|
||||||
|
.blockingGet();
|
||||||
|
return trust == null ? Trust.undecided : trust;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Single<Trust> readIkeyTrust(EntityBareJid owner, OpenPgpV4Fingerprint fingerprint) {
|
||||||
|
return ikeyRecordRepository.loadRecord(accountId, owner)
|
||||||
|
.map(record -> record.hasSubordinate(fingerprint) ?
|
||||||
|
record.getTrust() : Trust.undecided)
|
||||||
|
.doOnNext(trust -> LOGGER.log(Level.INFO, "Read ikey trust " + trust + " for device key " + fingerprint + " of contact " + owner))
|
||||||
|
.firstElement()
|
||||||
|
.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(
|
||||||
|
contactHasIkeyRecord(owner.asEntityBareJidIfPossible())
|
||||||
|
.flatMapCompletable(hasRecord -> hasRecord && accountIsIkeyAware.getValue() ?
|
||||||
|
skipManualTrustForIkeyContact(jid, fingerprint, trust) :
|
||||||
|
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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Completable skipManualTrustForIkeyContact(EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, Trust trust) {
|
||||||
|
return Completable.complete()
|
||||||
|
.doOnComplete(() -> LOGGER.log(Level.INFO,
|
||||||
|
"Contact " + owner + " has an Ikey Record, so do not mark " + fingerprint + " as " + trust));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Single<Boolean> contactHasIkeyRecord(EntityBareJid owner) {
|
||||||
|
return ikeyRecordRepository.loadRecord(accountId, owner.asEntityBareJidOrThrow())
|
||||||
|
.isEmpty()
|
||||||
|
.map(resultEmpty -> !resultEmpty); // negate
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Observable<Boolean> accountIsIkeyAware() {
|
||||||
|
return ikeyKeyRepository.loadSecretKey(accountId)
|
||||||
|
.map(Optional::isPresent)
|
||||||
|
.compose(schedulers.executeUiSafeObservable());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpTrustStore;
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
import org.mercury_im.messenger.core.SchedulersFacade;
|
import org.mercury_im.messenger.core.SchedulersFacade;
|
||||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
||||||
|
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -26,14 +27,20 @@ public class MercuryOpenPgpStore extends AbstractOpenPgpStore {
|
||||||
|
|
||||||
protected static final Logger LOGGER = Logger.getLogger(MercuryOpenPgpStore.class.getName());
|
protected static final Logger LOGGER = Logger.getLogger(MercuryOpenPgpStore.class.getName());
|
||||||
|
|
||||||
public MercuryOpenPgpStore(UUID accountId, OpenPgpRepository repository, SchedulersFacade schedulers) {
|
public MercuryOpenPgpStore(UUID accountId, OpenPgpRepository repository, OpenPgpTrustRepository trustRepository, SchedulersFacade schedulers) {
|
||||||
super(
|
this(
|
||||||
new KeyStore(accountId, repository, schedulers),
|
new KeyStore(accountId, repository, schedulers),
|
||||||
new MetadataStore(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 CompositeDisposable disposable = new CompositeDisposable();
|
||||||
private final OpenPgpRepository repository;
|
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 CompositeDisposable disposable = new CompositeDisposable();
|
||||||
private final OpenPgpRepository repository;
|
private final OpenPgpRepository repository;
|
||||||
|
@ -141,7 +148,8 @@ public class MercuryOpenPgpStore extends AbstractOpenPgpStore {
|
||||||
@Override
|
@Override
|
||||||
protected void writeAnnouncedFingerprintsOf(BareJid contact, Map<OpenPgpV4Fingerprint, Date> metadata)
|
protected void writeAnnouncedFingerprintsOf(BareJid contact, Map<OpenPgpV4Fingerprint, Date> metadata)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
disposable.add(repository.storeAnnouncedFingerprints(accountId, contact.asEntityBareJidIfPossible(), metadata)
|
disposable.add(
|
||||||
|
repository.storeAnnouncedFingerprints(accountId, contact.asEntityBareJidIfPossible(), metadata)
|
||||||
.subscribeOn(schedulers.getIoScheduler())
|
.subscribeOn(schedulers.getIoScheduler())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
() -> MercuryOpenPgpStore.LOGGER.log(Level.FINER, "Successfully updated announced OX fingerprints for " + contact + " (accountId=" + accountId + ")"),
|
() -> 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 {
|
public static class TrustStore extends AbstractOpenPgpTrustStore {
|
||||||
|
|
||||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||||
private final OpenPgpRepository repository;
|
private final OpenPgpTrustRepository repository;
|
||||||
private final SchedulersFacade schedulers;
|
private final SchedulersFacade schedulers;
|
||||||
|
|
||||||
private final UUID accountId;
|
private final UUID accountId;
|
||||||
|
|
||||||
public TrustStore(UUID accountId, OpenPgpRepository repository, SchedulersFacade schedulers) {
|
public TrustStore(UUID accountId, OpenPgpTrustRepository repository, SchedulersFacade schedulers) {
|
||||||
this.accountId = accountId;
|
this.accountId = accountId;
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.schedulers = schedulers;
|
this.schedulers = schedulers;
|
||||||
|
|
|
@ -5,8 +5,10 @@ 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.IkeyElement;
|
||||||
import org.jivesoftware.smackx.ikey.element.SubordinateElement;
|
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.PublicKeysListElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
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.store.definition.OpenPgpTrustStore;
|
||||||
import org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil;
|
import org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil;
|
||||||
import org.jivesoftware.smackx.pep.PepManager;
|
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.crypto.ikey.IkeyRepository;
|
||||||
import org.mercury_im.messenger.core.data.repository.AccountRepository;
|
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.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.util.Optional;
|
||||||
import org.mercury_im.messenger.core.viewmodel.MercuryViewModel;
|
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;
|
||||||
|
@ -55,6 +59,7 @@ public class AccountDetailsViewModel implements MercuryViewModel {
|
||||||
|
|
||||||
private MercuryConnectionManager connectionManager;
|
private MercuryConnectionManager connectionManager;
|
||||||
private final OpenPgpRepository openPgpRepository;
|
private final OpenPgpRepository openPgpRepository;
|
||||||
|
private final OpenPgpTrustRepository deviceKeyTrustRepository;
|
||||||
private final IkeyRepository ikeyRepository;
|
private final IkeyRepository ikeyRepository;
|
||||||
private final AccountRepository accountRepository;
|
private final AccountRepository accountRepository;
|
||||||
private final SchedulersFacade schedulers;
|
private final SchedulersFacade schedulers;
|
||||||
|
@ -63,12 +68,14 @@ public class AccountDetailsViewModel implements MercuryViewModel {
|
||||||
@Inject
|
@Inject
|
||||||
public AccountDetailsViewModel(MercuryConnectionManager connectionManager,
|
public AccountDetailsViewModel(MercuryConnectionManager connectionManager,
|
||||||
OpenPgpRepository openPgpRepository,
|
OpenPgpRepository openPgpRepository,
|
||||||
|
OpenPgpTrustRepository deviceKeyTrustRepository,
|
||||||
IkeyRepository ikeyRepository,
|
IkeyRepository ikeyRepository,
|
||||||
AccountRepository accountRepository,
|
AccountRepository accountRepository,
|
||||||
SchedulersFacade schedulers,
|
SchedulersFacade schedulers,
|
||||||
IkeyInitializer ikeyInitializer) {
|
IkeyInitializer ikeyInitializer) {
|
||||||
this.connectionManager = connectionManager;
|
this.connectionManager = connectionManager;
|
||||||
this.openPgpRepository = openPgpRepository;
|
this.openPgpRepository = openPgpRepository;
|
||||||
|
this.deviceKeyTrustRepository = deviceKeyTrustRepository;
|
||||||
this.ikeyRepository = ikeyRepository;
|
this.ikeyRepository = ikeyRepository;
|
||||||
this.accountRepository = accountRepository;
|
this.accountRepository = accountRepository;
|
||||||
this.schedulers = schedulers;
|
this.schedulers = schedulers;
|
||||||
|
@ -76,9 +83,14 @@ public class AccountDetailsViewModel implements MercuryViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markFingerprintTrusted(UUID accountId, OpenPgpV4Fingerprint fingerprint, boolean trusted) {
|
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())
|
.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) {
|
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<>());
|
.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);
|
return openPgpRepository.observeLocalFingerprintOf(accountId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Observable<List<FingerprintViewItem>> observeRemoteFingerprints(UUID accountId) {
|
public Observable<List<FingerprintViewItem>> observeRemoteDeviceFingerprints(UUID accountId) {
|
||||||
return observeLocalFingerprint(accountId)
|
OpenPgpStore store = OpenPgpManager.getInstanceFor(connectionManager.getConnection(accountId).getConnection())
|
||||||
|
.getOpenPgpProvider().getStore();
|
||||||
|
return observeLocalDeviceFingerprint(accountId)
|
||||||
.flatMap(optional -> accountRepository.getAccount(accountId).toSingle()
|
.flatMap(optional -> accountRepository.getAccount(accountId).toSingle()
|
||||||
.flatMapObservable(account -> openPgpRepository.observeFingerprints(accountId, account.getJid())
|
.flatMapObservable(account -> openPgpRepository.observeFingerprints(accountId, account.getJid())
|
||||||
.map(list -> {
|
.map(list -> {
|
||||||
|
@ -165,6 +179,7 @@ public class AccountDetailsViewModel implements MercuryViewModel {
|
||||||
|
|
||||||
List<FingerprintViewItem> remoteFingerprints = new ArrayList<>();
|
List<FingerprintViewItem> remoteFingerprints = new ArrayList<>();
|
||||||
for(FingerprintViewItem f : list) {
|
for(FingerprintViewItem f : list) {
|
||||||
|
f.setTrusted(store.getTrust(f.getOwner(), f.getFingerprint()));
|
||||||
if (!f.getFingerprint().equals(optional.getItem())) {
|
if (!f.getFingerprint().equals(optional.getItem())) {
|
||||||
remoteFingerprints.add(f);
|
remoteFingerprints.add(f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,17 +6,18 @@ import org.jivesoftware.smack.packet.Presence;
|
||||||
import org.jivesoftware.smack.roster.Roster;
|
import org.jivesoftware.smack.roster.Roster;
|
||||||
import org.jivesoftware.smack.roster.RosterEntry;
|
import org.jivesoftware.smack.roster.RosterEntry;
|
||||||
import org.jivesoftware.smack.roster.RosterGroup;
|
import org.jivesoftware.smack.roster.RosterGroup;
|
||||||
import org.jivesoftware.smackx.ikey.util.IkeyTrust;
|
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
||||||
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore;
|
||||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
|
||||||
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;
|
||||||
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
|
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
|
||||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
|
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.DirectChatRepository;
|
||||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
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.data.repository.PeerRepository;
|
||||||
import org.mercury_im.messenger.core.util.CombinedPresenceListener;
|
import org.mercury_im.messenger.core.util.CombinedPresenceListener;
|
||||||
import org.mercury_im.messenger.core.util.Optional;
|
import org.mercury_im.messenger.core.util.Optional;
|
||||||
|
@ -27,10 +28,13 @@ import org.mercury_im.messenger.entity.chat.Chat;
|
||||||
import org.mercury_im.messenger.entity.contact.Peer;
|
import org.mercury_im.messenger.entity.contact.Peer;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
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;
|
||||||
|
|
||||||
|
@ -42,10 +46,13 @@ import lombok.Getter;
|
||||||
|
|
||||||
public class ContactDetailViewModel implements MercuryViewModel {
|
public class ContactDetailViewModel implements MercuryViewModel {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(ContactDetailViewModel.class.getName());
|
||||||
|
|
||||||
private final MercuryConnectionManager connectionManager;
|
private final MercuryConnectionManager connectionManager;
|
||||||
private final PeerRepository peerRepository;
|
private final PeerRepository peerRepository;
|
||||||
private final DirectChatRepository directChatRepository;
|
private final DirectChatRepository directChatRepository;
|
||||||
private final OpenPgpRepository openPgpRepository;
|
private final OpenPgpRepository openPgpRepository;
|
||||||
|
private final OpenPgpTrustRepository trustRepository;
|
||||||
private final IkeyRepository ikeyRepository;
|
private final IkeyRepository ikeyRepository;
|
||||||
private final SchedulersFacade schedulers;
|
private final SchedulersFacade schedulers;
|
||||||
|
|
||||||
|
@ -62,6 +69,8 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
||||||
private BehaviorSubject<List<FingerprintViewItem>> contactDeviceFingerprints = BehaviorSubject.createDefault(Collections.emptyList());
|
private BehaviorSubject<List<FingerprintViewItem>> contactDeviceFingerprints = BehaviorSubject.createDefault(Collections.emptyList());
|
||||||
private BehaviorSubject<Tuple<String, EntityBareJid>> contactAvatarBase = BehaviorSubject.create();
|
private BehaviorSubject<Tuple<String, EntityBareJid>> contactAvatarBase = BehaviorSubject.create();
|
||||||
|
|
||||||
|
private OpenPgpStore openPgpStore;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private UUID peerId;
|
private UUID peerId;
|
||||||
|
|
||||||
|
@ -73,12 +82,14 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
||||||
PeerRepository peerRepository,
|
PeerRepository peerRepository,
|
||||||
DirectChatRepository directChatRepository,
|
DirectChatRepository directChatRepository,
|
||||||
OpenPgpRepository openPgpRepository,
|
OpenPgpRepository openPgpRepository,
|
||||||
|
OpenPgpTrustRepository trustRepository,
|
||||||
IkeyRepository ikeyRepository,
|
IkeyRepository ikeyRepository,
|
||||||
SchedulersFacade schedulers) {
|
SchedulersFacade schedulers) {
|
||||||
this.connectionManager = connectionManager;
|
this.connectionManager = connectionManager;
|
||||||
this.peerRepository = peerRepository;
|
this.peerRepository = peerRepository;
|
||||||
this.directChatRepository = directChatRepository;
|
this.directChatRepository = directChatRepository;
|
||||||
this.openPgpRepository = openPgpRepository;
|
this.openPgpRepository = openPgpRepository;
|
||||||
|
this.trustRepository = trustRepository;
|
||||||
this.ikeyRepository = ikeyRepository;
|
this.ikeyRepository = ikeyRepository;
|
||||||
this.schedulers = schedulers;
|
this.schedulers = schedulers;
|
||||||
}
|
}
|
||||||
|
@ -89,6 +100,9 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Completable init(Peer peer) {
|
public Completable init(Peer peer) {
|
||||||
|
this.openPgpStore = OpenPgpManager.getInstanceFor(connectionManager.getConnection(peer.getAccount().getId()).getConnection())
|
||||||
|
.getOpenPgpProvider().getStore();
|
||||||
|
|
||||||
return Completable.fromAction(() -> {
|
return Completable.fromAction(() -> {
|
||||||
this.peerId = peer.getId();
|
this.peerId = peer.getId();
|
||||||
this.accountId = peer.getAccount().getId();
|
this.accountId = peer.getAccount().getId();
|
||||||
|
@ -109,11 +123,19 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
||||||
contactAvatarBase.onNext(new Tuple<>(peer.getDisplayName(), peer.getJid()));
|
contactAvatarBase.onNext(new Tuple<>(peer.getDisplayName(), peer.getJid()));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Device keys
|
||||||
addDisposable(openPgpRepository
|
addDisposable(openPgpRepository
|
||||||
.observeFingerprints(peer.getAccount().getId(), peer.getJid())
|
.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())
|
.compose(schedulers.executeUiSafeObservable())
|
||||||
.subscribe(fingerprints -> contactDeviceFingerprints.onNext(fingerprints)));
|
.subscribe(fingerprints -> contactDeviceFingerprints.onNext(fingerprints)));
|
||||||
|
|
||||||
|
// Ikey
|
||||||
addDisposable(ikeyRepository
|
addDisposable(ikeyRepository
|
||||||
.loadRecord(peer.getAccount().getId(), peer.getJid())
|
.loadRecord(peer.getAccount().getId(), peer.getJid())
|
||||||
.compose(schedulers.executeUiSafeObservable())
|
.compose(schedulers.executeUiSafeObservable())
|
||||||
|
@ -121,7 +143,7 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
||||||
peer.getAccount().getId(), record.getJid(),
|
peer.getAccount().getId(), record.getJid(),
|
||||||
new OpenPgpV4Fingerprint(record.getSuperordinate()),
|
new OpenPgpV4Fingerprint(record.getSuperordinate()),
|
||||||
record.getTimestamp(), record.getTimestamp(),
|
record.getTimestamp(), record.getTimestamp(),
|
||||||
OpenPgpTrustStore.Trust.trusted) // TODO
|
record.getTrust())
|
||||||
)
|
)
|
||||||
.map(Optional::new)
|
.map(Optional::new)
|
||||||
.subscribe(contactIdentityFingerprint::onNext));
|
.subscribe(contactIdentityFingerprint::onNext));
|
||||||
|
@ -219,17 +241,15 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markDeviceFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, boolean checked) {
|
public void markDeviceFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, boolean checked) throws IOException {
|
||||||
openPgpRepository.storeTrust(accountId, contactAddress.getValue(), fingerprint,
|
openPgpStore.setTrust(getContactAddress().blockingFirst(), fingerprint, checked ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted);
|
||||||
checked ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted)
|
|
||||||
.subscribe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markIkeyFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, EntityBareJid owner, boolean isChecked) {
|
public void markIkeyFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, EntityBareJid owner, boolean isChecked) {
|
||||||
IkeyTrust trust = new IkeyTrust();
|
OpenPgpTrustStore.Trust trust = isChecked ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted;
|
||||||
trust.setTrust(isChecked ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted);
|
addDisposable(ikeyRepository.storeSuperordinateTrust(accountId, owner, fingerprint, trust)
|
||||||
ikeyRepository.storeSuperordinateTrust(accountId, owner, fingerprint, trust)
|
.subscribe(() -> LOGGER.log(Level.INFO, "Marked Ikey " + fingerprint + " of " + owner + " as " + trust),
|
||||||
.subscribe();
|
e -> LOGGER.log(Level.SEVERE, "Error marking ikey " + fingerprint + " of " + owner + " as " + trust, e)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static abstract class FilteredPresenceEventListener extends CombinedPresenceListener {
|
public static abstract class FilteredPresenceEventListener extends CombinedPresenceListener {
|
||||||
|
|
Loading…
Reference in New Issue