package org.mercury_im.messenger.core.store.crypto; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpKeyStore; import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpMetadataStore; import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpStore; import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpTrustStore; import org.jxmpp.jid.BareJid; import org.mercury_im.messenger.core.SchedulersFacade; import org.mercury_im.messenger.core.data.repository.OpenPgpRepository; import org.pgpainless.key.OpenPgpV4Fingerprint; import java.io.IOException; import java.util.Date; import java.util.Map; import java.util.NoSuchElementException; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import io.reactivex.disposables.CompositeDisposable; public class MercuryOpenPgpStore extends AbstractOpenPgpStore { protected static final Logger LOGGER = Logger.getLogger(MercuryOpenPgpStore.class.getName()); public MercuryOpenPgpStore(UUID accountId, OpenPgpRepository repository, SchedulersFacade schedulers) { super( new KeyStore(accountId, repository, schedulers), new MetadataStore(accountId, repository, schedulers), new TrustStore(accountId, repository, schedulers)); } private static class KeyStore extends AbstractOpenPgpKeyStore { private final CompositeDisposable disposable = new CompositeDisposable(); private final OpenPgpRepository repository; private final SchedulersFacade schedulers; private final UUID accountId; public KeyStore(UUID accountId, OpenPgpRepository repository, SchedulersFacade schedulers) { this.accountId = accountId; this.repository = repository; this.schedulers = schedulers; } @Override protected PGPPublicKeyRingCollection readPublicKeysOf(BareJid owner) throws IOException, PGPException { try { return repository.loadPublicKeysOf(accountId, owner.asEntityBareJidIfPossible()) .blockingGet(); } catch (NoSuchElementException e) { return null; } } @Override protected void writePublicKeysOf(BareJid owner, PGPPublicKeyRingCollection publicKeys) throws IOException { disposable.add(repository.storePublicKeysOf(accountId, owner.asEntityBareJidIfPossible(), publicKeys) .subscribeOn(schedulers.getIoScheduler()) .subscribe( () -> MercuryOpenPgpStore.LOGGER.log(Level.FINER, "Successfully wrote OX public keys for " + owner + " (accountId=" + accountId + ")"), e -> MercuryOpenPgpStore.LOGGER.log(Level.SEVERE, "Error writing OX public keys for " + owner + " (accountId=" + accountId + ")") )); } @Override protected PGPSecretKeyRingCollection readSecretKeysOf(BareJid owner) throws IOException, PGPException { try { return repository.loadSecretKeysOf(accountId, owner.asEntityBareJidIfPossible()) .blockingGet(); } catch (NoSuchElementException e) { return null; } } @Override protected void writeSecretKeysOf(BareJid owner, PGPSecretKeyRingCollection secretKeys) throws IOException { disposable.add(repository.storeSecretKeysOf(accountId, owner.asEntityBareJidIfPossible(), secretKeys) .subscribeOn(schedulers.getIoScheduler()) .subscribe( () -> MercuryOpenPgpStore.LOGGER.log(Level.FINER, "Successfully wrote OX secret keys for " + owner + " (accountId=" + accountId + ")"), e -> MercuryOpenPgpStore.LOGGER.log(Level.SEVERE, "Error writing OX secret keys for " + owner + " (accountId=" + accountId + ")") )); } @Override protected Map readKeyFetchDates(BareJid owner) throws IOException { return repository.loadPublicKeyFetchDates(accountId, owner.asEntityBareJidIfPossible()) .blockingGet(); } @Override protected void writeKeyFetchDates(BareJid owner, Map dates) throws IOException { disposable.add(repository.storePublicKeyFetchDates(accountId, owner.asEntityBareJidIfPossible(), dates) .subscribeOn(schedulers.getIoScheduler()) .subscribe( () -> MercuryOpenPgpStore.LOGGER.log(Level.FINER, "Successfully updated OX fetch dates for " + owner + " (accountId=" + accountId + ")"), e -> MercuryOpenPgpStore.LOGGER.log(Level.SEVERE, "Error updating OX key fetch dates for " + owner + " (accountId=" + accountId + ")") )); } @Override protected void finalize() throws Throwable { disposable.dispose(); super.finalize(); } } private static class MetadataStore extends AbstractOpenPgpMetadataStore { private final CompositeDisposable disposable = new CompositeDisposable(); private final OpenPgpRepository repository; private final SchedulersFacade schedulers; private final UUID accountId; public MetadataStore(UUID accountId, OpenPgpRepository repository, SchedulersFacade schedulers) { this.accountId = accountId; this.repository = repository; this.schedulers = schedulers; } @Override protected Map readAnnouncedFingerprintsOf(BareJid contact) throws IOException { return repository.loadAnnouncedFingerprints(accountId, contact.asEntityBareJidIfPossible()) .blockingGet(); } @Override protected void writeAnnouncedFingerprintsOf(BareJid contact, Map metadata) throws IOException { disposable.add(repository.storeAnnouncedFingerprints(accountId, contact.asEntityBareJidIfPossible(), metadata) .subscribeOn(schedulers.getIoScheduler()) .subscribe( () -> MercuryOpenPgpStore.LOGGER.log(Level.FINER, "Successfully updated announced OX fingerprints for " + contact + " (accountId=" + accountId + ")"), e -> MercuryOpenPgpStore.LOGGER.log(Level.SEVERE, "Error updating announced OX fingerprints for " + contact + " (accountId=" + accountId + ")") )); } @Override protected void finalize() throws Throwable { disposable.dispose(); super.finalize(); } } public static class TrustStore extends AbstractOpenPgpTrustStore { private final CompositeDisposable disposable = new CompositeDisposable(); private final OpenPgpRepository repository; private final SchedulersFacade schedulers; private final UUID accountId; public TrustStore(UUID accountId, OpenPgpRepository repository, SchedulersFacade schedulers) { this.accountId = accountId; this.repository = repository; this.schedulers = schedulers; } @Override protected Trust readTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException { return repository.loadTrust(accountId, owner.asEntityBareJidIfPossible(), fingerprint) .blockingGet(); } @Override protected void writeTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint, Trust trust) throws IOException { disposable.add(repository.storeTrust(accountId, owner.asEntityBareJidIfPossible(), fingerprint, trust) .subscribeOn(schedulers.getIoScheduler()) .subscribe( () -> MercuryOpenPgpStore.LOGGER.log(Level.FINER, "Successfully set trust in key " + fingerprint + " to " + trust + " for " + owner + " (accountId=" + accountId + ")"), e -> MercuryOpenPgpStore.LOGGER.log(Level.SEVERE, "Error setting trust in key " + fingerprint + " to " + trust + " for " + owner + " (accountId=" + accountId + ")") )); } @Override protected void finalize() throws Throwable { disposable.dispose(); super.finalize(); } } }