134 lines
6.9 KiB
Java
134 lines
6.9 KiB
Java
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());
|
|
}
|
|
}
|
|
}
|