From 6efd8a1940d5463424ea33f4e2a38e6e58225e13 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 18 Jun 2020 21:52:06 +0200 Subject: [PATCH] Add OX store implementation --- .../android/di/component/AppComponent.java | 3 +- .../data/converter/OpenPgpTrustConverter.java | 11 + .../AbstractAnnouncedOpenPgpContactKey.java | 36 +++ ....java => AbstractOpenPgpKeyFetchDate.java} | 21 +- .../data/model/AbstractOpenPgpKeyTrust.java | 36 +++ .../model/AbstractOpenPgpPublicKeyRing.java | 6 + .../model/AbstractOpenPgpSecretKeyRing.java | 6 + .../data/repository/RxOpenPgpRepository.java | 127 +++++++++- .../core/crypto/MercuryOpenPgpManager.java | 20 +- .../data/repository/OpenPgpRepository.java | 30 ++- .../core/di/module/OpenPgpModule.java | 21 -- .../store/crypto/MercuryOpenPgpStore.java | 225 ++++++++++++------ 12 files changed, 424 insertions(+), 118 deletions(-) create mode 100644 data/src/main/java/org/mercury_im/messenger/data/converter/OpenPgpTrustConverter.java create mode 100644 data/src/main/java/org/mercury_im/messenger/data/model/AbstractAnnouncedOpenPgpContactKey.java rename data/src/main/java/org/mercury_im/messenger/data/model/{AbstractOpenPgpKeyFetchDates.java => AbstractOpenPgpKeyFetchDate.java} (50%) create mode 100644 data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpKeyTrust.java diff --git a/app/src/main/java/org/mercury_im/messenger/android/di/component/AppComponent.java b/app/src/main/java/org/mercury_im/messenger/android/di/component/AppComponent.java index cbd97a5..269f449 100644 --- a/app/src/main/java/org/mercury_im/messenger/android/di/component/AppComponent.java +++ b/app/src/main/java/org/mercury_im/messenger/android/di/component/AppComponent.java @@ -44,8 +44,7 @@ import dagger.Component; ViewModelModule.class, XmppTcpConnectionFactoryModule.class, RxMercuryMessageStoreFactoryModule.class, - RxMercuryRosterStoreFactoryModule.class, - OpenPgpModule.class + RxMercuryRosterStoreFactoryModule.class }) public interface AppComponent { diff --git a/data/src/main/java/org/mercury_im/messenger/data/converter/OpenPgpTrustConverter.java b/data/src/main/java/org/mercury_im/messenger/data/converter/OpenPgpTrustConverter.java new file mode 100644 index 0000000..7bbd39b --- /dev/null +++ b/data/src/main/java/org/mercury_im/messenger/data/converter/OpenPgpTrustConverter.java @@ -0,0 +1,11 @@ +package org.mercury_im.messenger.data.converter; + +import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore; + +import io.requery.converter.EnumStringConverter; + +public class OpenPgpTrustConverter extends EnumStringConverter { + public OpenPgpTrustConverter() { + super(OpenPgpTrustStore.Trust.class); + } +} diff --git a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractAnnouncedOpenPgpContactKey.java b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractAnnouncedOpenPgpContactKey.java new file mode 100644 index 0000000..17c2ac7 --- /dev/null +++ b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractAnnouncedOpenPgpContactKey.java @@ -0,0 +1,36 @@ +package org.mercury_im.messenger.data.model; + +import org.jxmpp.jid.EntityBareJid; +import org.mercury_im.messenger.data.converter.EntityBareJidConverter; +import org.mercury_im.messenger.data.converter.OpenPgpV4FingerprintConverter; +import org.pgpainless.key.OpenPgpV4Fingerprint; + +import java.util.Date; +import java.util.UUID; + +import io.requery.Column; +import io.requery.Convert; +import io.requery.Entity; +import io.requery.Key; +import io.requery.Table; +import io.requery.converter.UUIDConverter; + +@Entity +@Table(name = "ox_announced_keys") +public class AbstractAnnouncedOpenPgpContactKey { + + @Key + @Convert(UUIDConverter.class) + UUID accountId; + + @Key + @Convert(EntityBareJidConverter.class) + EntityBareJid owner; + + @Key + @Convert(OpenPgpV4FingerprintConverter.class) + OpenPgpV4Fingerprint fingerprint; + + @Column(name = "modification_date", nullable = false) + Date modificationDate; +} diff --git a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpKeyFetchDates.java b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpKeyFetchDate.java similarity index 50% rename from data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpKeyFetchDates.java rename to data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpKeyFetchDate.java index 7f0cb52..3167d8c 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpKeyFetchDates.java +++ b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpKeyFetchDate.java @@ -2,26 +2,35 @@ package org.mercury_im.messenger.data.model; import org.jxmpp.jid.EntityBareJid; import org.mercury_im.messenger.data.converter.EntityBareJidConverter; +import org.mercury_im.messenger.data.converter.OpenPgpV4FingerprintConverter; import org.pgpainless.key.OpenPgpV4Fingerprint; import java.util.Date; -import java.util.Map; +import java.util.UUID; import io.requery.Column; import io.requery.Convert; import io.requery.Entity; import io.requery.Key; import io.requery.Table; +import io.requery.converter.UUIDConverter; @Entity -@Table(name = "ox_key_fetch_dates") -public class AbstractOpenPgpKeyFetchDates { +@Table(name = "ox_pubkey_fetchdates") +public class AbstractOpenPgpKeyFetchDate { + + @Key + @Convert(UUIDConverter.class) + UUID accountId; @Key - @Column(name = "owner") @Convert(EntityBareJidConverter.class) EntityBareJid owner; - @Column(name = "fetch_dates") - Map fetchDates; + @Key + @Convert(OpenPgpV4FingerprintConverter.class) + OpenPgpV4Fingerprint fingerprint; + + @Column(nullable = false) + Date fetchDate; } diff --git a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpKeyTrust.java b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpKeyTrust.java new file mode 100644 index 0000000..cc145ed --- /dev/null +++ b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpKeyTrust.java @@ -0,0 +1,36 @@ +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.Convert; +import io.requery.Entity; +import io.requery.Key; +import io.requery.Table; +import io.requery.converter.UUIDConverter; + +@Entity +@Table(name = "ox_key_trust") +public class AbstractOpenPgpKeyTrust { + + @Key + @Convert(UUIDConverter.class) + UUID accountId; + + @Key + @Convert(EntityBareJidConverter.class) + EntityBareJid owner; + + @Key + @Convert(OpenPgpV4FingerprintConverter.class) + OpenPgpV4Fingerprint fingerprint; + + @Convert(OpenPgpTrustConverter.class) + OpenPgpTrustStore.Trust trust; +} diff --git a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpPublicKeyRing.java b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpPublicKeyRing.java index 902220c..7e28ea7 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpPublicKeyRing.java +++ b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpPublicKeyRing.java @@ -3,6 +3,8 @@ package org.mercury_im.messenger.data.model; import org.jxmpp.jid.EntityBareJid; import org.mercury_im.messenger.data.converter.EntityBareJidConverter; +import java.util.UUID; + import io.requery.Column; import io.requery.Convert; import io.requery.Entity; @@ -13,6 +15,10 @@ import io.requery.Table; @Table(name = "ox_public_keys") public class AbstractOpenPgpPublicKeyRing { + @Key + @Column(name = "account_id", nullable = false) + UUID accountId; + @Key @Column(name = "owner", nullable = false) @Convert(EntityBareJidConverter.class) diff --git a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpSecretKeyRing.java b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpSecretKeyRing.java index 630e862..dba4a69 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpSecretKeyRing.java +++ b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpSecretKeyRing.java @@ -3,6 +3,8 @@ package org.mercury_im.messenger.data.model; import org.jxmpp.jid.EntityBareJid; import org.mercury_im.messenger.data.converter.EntityBareJidConverter; +import java.util.UUID; + import io.requery.Column; import io.requery.Convert; import io.requery.Entity; @@ -13,6 +15,10 @@ import io.requery.Table; @Table(name = "ox_secret_keys") public class AbstractOpenPgpSecretKeyRing { + @Key + @Column(name = "account_id", nullable = false) + UUID accountId; + @Key @Column(name = "owner", nullable = false) @Convert(EntityBareJidConverter.class) diff --git a/data/src/main/java/org/mercury_im/messenger/data/repository/RxOpenPgpRepository.java b/data/src/main/java/org/mercury_im/messenger/data/repository/RxOpenPgpRepository.java index e8a23b1..f7bc953 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/repository/RxOpenPgpRepository.java +++ b/data/src/main/java/org/mercury_im/messenger/data/repository/RxOpenPgpRepository.java @@ -2,17 +2,31 @@ package org.mercury_im.messenger.data.repository; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; +import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore; import org.jxmpp.jid.EntityBareJid; import org.mercury_im.messenger.core.data.repository.OpenPgpRepository; +import org.mercury_im.messenger.data.model.AnnouncedOpenPgpContactKey; +import org.mercury_im.messenger.data.model.OpenPgpKeyFetchDate; +import org.mercury_im.messenger.data.model.OpenPgpKeyTrust; import org.mercury_im.messenger.data.model.OpenPgpPublicKeyRing; import org.mercury_im.messenger.data.model.OpenPgpSecretKeyRing; import org.pgpainless.PGPainless; +import org.pgpainless.key.OpenPgpV4Fingerprint; + +import java.util.Collections; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import javax.inject.Inject; import io.reactivex.Completable; import io.reactivex.Single; import io.requery.Persistable; +import io.requery.query.ResultDelegate; import io.requery.reactivex.ReactiveEntityStore; public class RxOpenPgpRepository implements OpenPgpRepository { @@ -25,9 +39,10 @@ public class RxOpenPgpRepository implements OpenPgpRepository { } @Override - public Completable storePublicKeysOf(EntityBareJid owner, PGPPublicKeyRingCollection keys) { + public Completable storePublicKeysOf(UUID accountId, EntityBareJid owner, PGPPublicKeyRingCollection keys) { return Single.fromCallable(() -> { OpenPgpPublicKeyRing keyRing = new OpenPgpPublicKeyRing(); + keyRing.setAccountId(accountId); keyRing.setOwner(owner); keyRing.setBytes(keys.getEncoded()); return keyRing; @@ -35,25 +50,28 @@ public class RxOpenPgpRepository implements OpenPgpRepository { } @Override - public Single loadPublicKeysOf(EntityBareJid owner) { + public Single loadPublicKeysOf(UUID accountId, EntityBareJid owner) { return data.select(OpenPgpPublicKeyRing.class) - .where(OpenPgpPublicKeyRing.OWNER.eq(owner)) + .where(OpenPgpPublicKeyRing.ACCOUNT_ID.eq(accountId)) + .and(OpenPgpPublicKeyRing.OWNER.eq(owner)) .limit(1).get() .maybe().toSingle() .map(keyring -> PGPainless.readKeyRing().publicKeyRingCollection(keyring.getBytes())); } @Override - public Single deletePublicKeysOf(EntityBareJid owner) { + public Single deletePublicKeysOf(UUID accountId, EntityBareJid owner) { return data.delete(OpenPgpPublicKeyRing.class) - .where(OpenPgpPublicKeyRing.OWNER.eq(owner)) + .where(OpenPgpPublicKeyRing.ACCOUNT_ID.eq(accountId)) + .and(OpenPgpPublicKeyRing.OWNER.eq(owner)) .get().single(); } @Override - public Completable storeSecretKeysOf(EntityBareJid owner, PGPSecretKeyRingCollection keys) { + public Completable storeSecretKeysOf(UUID accountId, EntityBareJid owner, PGPSecretKeyRingCollection keys) { return Single.fromCallable(() -> { OpenPgpSecretKeyRing keyRing = new OpenPgpSecretKeyRing(); + keyRing.setAccountId(accountId); keyRing.setOwner(owner); keyRing.setBytes(keys.getEncoded()); return keyRing; @@ -61,18 +79,107 @@ public class RxOpenPgpRepository implements OpenPgpRepository { } @Override - public Single loadSecretKeysOf(EntityBareJid owner) { + public Single loadSecretKeysOf(UUID accountId, EntityBareJid owner) { return data.select(OpenPgpSecretKeyRing.class) - .where(OpenPgpSecretKeyRing.OWNER.eq(owner)) + .where(OpenPgpSecretKeyRing.ACCOUNT_ID.eq(accountId)) + .and(OpenPgpSecretKeyRing.OWNER.eq(owner)) .limit(1).get() .maybe().toSingle() .map(keyring -> PGPainless.readKeyRing().secretKeyRingCollection(keyring.getBytes())); } @Override - public Single deleteSecretKeysOf(EntityBareJid owner) { + public Single deleteSecretKeysOf(UUID accountId, EntityBareJid owner) { return data.delete(OpenPgpSecretKeyRing.class) - .where(OpenPgpPublicKeyRing.OWNER.eq(owner)) + .where(OpenPgpSecretKeyRing.ACCOUNT_ID.eq(accountId)) + .and(OpenPgpSecretKeyRing.OWNER.eq(owner)) .get().single(); } + + @Override + public Completable storePublicKeyFetchDates(UUID accountId, EntityBareJid owner, Map dates) { + List entities = new LinkedList<>(); + for (Map.Entry entry : dates.entrySet()) { + OpenPgpKeyFetchDate entity = new OpenPgpKeyFetchDate(); + entity.setAccountId(accountId); + entity.setOwner(owner); + entity.setFingerprint(entry.getKey()); + entity.setFetchDate(entry.getValue()); + entities.add(entity); + } + return data.upsert(entities).ignoreElement(); + } + + @Override + public Completable storeAnnouncedFingerprints(UUID accountId, EntityBareJid owner, Map metadata) { + List entities = new LinkedList<>(); + for (Map.Entry entry : metadata.entrySet()) { + AnnouncedOpenPgpContactKey entity = new AnnouncedOpenPgpContactKey(); + entity.setAccountId(accountId); + entity.setOwner(owner); + entity.setFingerprint(entry.getKey()); + entity.setModificationDate(entry.getValue()); + entities.add(entity); + } + return data.upsert(entities).ignoreElement(); + } + + @Override + public Single> loadAnnouncedFingerprints(UUID accountId, EntityBareJid contact) { + return data.select(AnnouncedOpenPgpContactKey.class) + .where(AnnouncedOpenPgpContactKey.ACCOUNT_ID.eq(accountId)) + .and(AnnouncedOpenPgpContactKey.OWNER.eq(contact)) + .get() + .observableResult() + .map(ResultDelegate::toList) + .single(Collections.emptyList()) + .map(list -> { + Map map = new ConcurrentHashMap<>(); + for (AnnouncedOpenPgpContactKey key : list) { + map.put(key.getFingerprint(), key.getModificationDate()); + } + return map; + }); + } + + @Override + public Single loadTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint) { + return data.select(OpenPgpKeyTrust.class) + .where(OpenPgpKeyTrust.ACCOUNT_ID.eq(accountId)) + .and(OpenPgpKeyTrust.OWNER.eq(owner)) + .and(OpenPgpKeyTrust.FINGERPRINT.eq(fingerprint)) + .limit(1) + .get() + .maybe() + .toSingle(new OpenPgpKeyTrust()) + .map(entity -> entity.getTrust() != null ? entity.getTrust() : OpenPgpTrustStore.Trust.undecided); + } + + @Override + public Completable storeTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust) { + OpenPgpKeyTrust entity = new OpenPgpKeyTrust(); + entity.setAccountId(accountId); + entity.setOwner(owner); + entity.setFingerprint(fingerprint); + entity.setTrust(trust); + return data.upsert(entity).ignoreElement(); + } + + @Override + public Single> loadPublicKeyFetchDates(UUID accountId, EntityBareJid owner) { + return data.select(OpenPgpKeyFetchDate.class) + .where(OpenPgpKeyFetchDate.ACCOUNT_ID.eq(accountId)) + .and(OpenPgpKeyFetchDate.OWNER.eq(owner)) + .get() + .observableResult() + .map(ResultDelegate::toList) + .single(Collections.emptyList()) + .map(list -> { + Map map = new ConcurrentHashMap<>(); + for (OpenPgpKeyFetchDate date : list) { + map.put(date.getFingerprint(), date.getFetchDate()); + } + return map; + }); + } } diff --git a/domain/src/main/java/org/mercury_im/messenger/core/crypto/MercuryOpenPgpManager.java b/domain/src/main/java/org/mercury_im/messenger/core/crypto/MercuryOpenPgpManager.java index 1ae7382..cbe3c04 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/crypto/MercuryOpenPgpManager.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/crypto/MercuryOpenPgpManager.java @@ -4,10 +4,15 @@ import org.jivesoftware.smack.AbstractConnectionListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smackx.ox.OpenPgpManager; import org.jivesoftware.smackx.ox.crypto.OpenPgpProvider; +import org.jivesoftware.smackx.ox.crypto.PainlessOpenPgpProvider; import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException; import org.jivesoftware.smackx.ox.exception.NoBackupFoundException; +import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore; import org.jivesoftware.smackx.ox_im.OXInstantMessagingManager; import org.jivesoftware.smackx.pubsub.PubSubException; +import org.mercury_im.messenger.core.SchedulersFacade; +import org.mercury_im.messenger.core.data.repository.OpenPgpRepository; +import org.mercury_im.messenger.core.store.crypto.MercuryOpenPgpStore; import org.mercury_im.messenger.core.xmpp.MercuryConnection; import java.util.logging.Level; @@ -19,11 +24,13 @@ public class MercuryOpenPgpManager { private static final Logger LOGGER = Logger.getLogger(MercuryOpenPgpManager.class.getName()); - private final OpenPgpProvider openPgpProvider; + private final OpenPgpRepository openPgpRepository; + private final SchedulersFacade schedulers; @Inject - public MercuryOpenPgpManager(OpenPgpProvider openPgpProvider) { - this.openPgpProvider = openPgpProvider; + public MercuryOpenPgpManager(OpenPgpRepository openPgpRepository, SchedulersFacade schedulers) { + this.openPgpRepository = openPgpRepository; + this.schedulers = schedulers; } public void initialize(MercuryConnection connection) { @@ -42,14 +49,17 @@ public class MercuryOpenPgpManager { } private void setup(MercuryConnection connection) { + OpenPgpStore store = new MercuryOpenPgpStore(connection.getAccountId(), openPgpRepository, schedulers); + OpenPgpProvider provider = new PainlessOpenPgpProvider(store); try { OpenPgpManager oxManager = OpenPgpManager.getInstanceFor(connection.getConnection()); - oxManager.setOpenPgpProvider(openPgpProvider); + oxManager.setOpenPgpProvider(provider); if (!oxManager.hasSecretKeysAvailable()) { try { oxManager.restoreSecretKeyServerBackup( //() -> "RW8X-367S-A2C3-QYAL-VG6E-Z2IM"); - () -> "KISJ-5Z1T-FGDW-WMDK-SC2U-SQUA"); + //() -> "KISJ-5Z1T-FGDW-WMDK-SC2U-SQUA"); + () -> "71ZA-Y416-UA7A-7NCE-3SNM-88EF"); LOGGER.log(Level.INFO, "Successfully restored secret key backup!"); } catch (NoBackupFoundException | PubSubException.NotALeafNodeException | InvalidBackupCodeException e) { LOGGER.log(Level.INFO, "Error restoring secret key backup.", e); diff --git a/domain/src/main/java/org/mercury_im/messenger/core/data/repository/OpenPgpRepository.java b/domain/src/main/java/org/mercury_im/messenger/core/data/repository/OpenPgpRepository.java index d1ab3bb..277fcf8 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/data/repository/OpenPgpRepository.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/data/repository/OpenPgpRepository.java @@ -2,22 +2,40 @@ package org.mercury_im.messenger.core.data.repository; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; +import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore; import org.jxmpp.jid.EntityBareJid; +import org.pgpainless.key.OpenPgpV4Fingerprint; + +import java.util.Date; +import java.util.Map; +import java.util.UUID; import io.reactivex.Completable; import io.reactivex.Single; public interface OpenPgpRepository { - Completable storePublicKeysOf(EntityBareJid owner, PGPPublicKeyRingCollection keys); + Completable storePublicKeysOf(UUID accountId, EntityBareJid owner, PGPPublicKeyRingCollection keys); - Single loadPublicKeysOf(EntityBareJid owner); + Single loadPublicKeysOf(UUID accountId, EntityBareJid owner); - Single deletePublicKeysOf(EntityBareJid owner); + Single deletePublicKeysOf(UUID accountId, EntityBareJid owner); - Completable storeSecretKeysOf(EntityBareJid owner, PGPSecretKeyRingCollection keys); + Completable storeSecretKeysOf(UUID accountId, EntityBareJid owner, PGPSecretKeyRingCollection keys); - Single loadSecretKeysOf(EntityBareJid owner); + Single loadSecretKeysOf(UUID accountId, EntityBareJid owner); - Single deleteSecretKeysOf(EntityBareJid owner); + Single deleteSecretKeysOf(UUID accountId, EntityBareJid owner); + + Completable storePublicKeyFetchDates(UUID accountId, EntityBareJid owner, Map dates); + + Single> loadPublicKeyFetchDates(UUID accountId, EntityBareJid owner); + + Completable storeAnnouncedFingerprints(UUID accountId, EntityBareJid owner, Map metadata); + + Single> loadAnnouncedFingerprints(UUID accountId, EntityBareJid contact); + + Single loadTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint); + + Completable storeTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust); } diff --git a/domain/src/main/java/org/mercury_im/messenger/core/di/module/OpenPgpModule.java b/domain/src/main/java/org/mercury_im/messenger/core/di/module/OpenPgpModule.java index 20f2be4..c1b699f 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/di/module/OpenPgpModule.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/di/module/OpenPgpModule.java @@ -1,29 +1,8 @@ package org.mercury_im.messenger.core.di.module; -import org.jivesoftware.smackx.ox.crypto.OpenPgpProvider; -import org.jivesoftware.smackx.ox.crypto.PainlessOpenPgpProvider; -import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore; -import org.mercury_im.messenger.core.SchedulersFacade; -import org.mercury_im.messenger.core.data.repository.OpenPgpRepository; -import org.mercury_im.messenger.core.store.crypto.MercuryOpenPgpStore; - -import javax.inject.Singleton; - import dagger.Module; -import dagger.Provides; @Module public class OpenPgpModule { - @Provides - @Singleton - static OpenPgpStore provideOpenPgpStore(OpenPgpRepository openPgpRepository, SchedulersFacade schedulersFacade) { - return new MercuryOpenPgpStore(openPgpRepository, schedulersFacade); - } - - @Provides - @Singleton - static OpenPgpProvider provideOpenPgpProvider(OpenPgpStore openPgpStore) { - return new PainlessOpenPgpProvider(openPgpStore); - } } diff --git a/domain/src/main/java/org/mercury_im/messenger/core/store/crypto/MercuryOpenPgpStore.java b/domain/src/main/java/org/mercury_im/messenger/core/store/crypto/MercuryOpenPgpStore.java index 879a8ee..928c85f 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/store/crypto/MercuryOpenPgpStore.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/store/crypto/MercuryOpenPgpStore.java @@ -14,93 +14,182 @@ import org.pgpainless.key.OpenPgpV4Fingerprint; import java.io.IOException; import java.util.Date; -import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; -import javax.inject.Inject; +import io.reactivex.disposables.CompositeDisposable; public class MercuryOpenPgpStore extends AbstractOpenPgpStore { - private final OpenPgpRepository repository; - private final SchedulersFacade schedulers; + protected static final Logger LOGGER = Logger.getLogger(MercuryOpenPgpStore.class.getName()); - @Inject - public MercuryOpenPgpStore(OpenPgpRepository repository, SchedulersFacade schedulers) { - super(new AbstractOpenPgpKeyStore() { - @Override - protected PGPPublicKeyRingCollection readPublicKeysOf(BareJid owner) - throws IOException, PGPException { - try { - return repository.loadPublicKeysOf(owner.asEntityBareJidIfPossible()) - .blockingGet(); - } catch (NoSuchElementException e) { - return null; - } - } + public MercuryOpenPgpStore(UUID accountId, OpenPgpRepository repository, SchedulersFacade schedulers) { + super( + new KeyStore(accountId, repository, schedulers), + new MetadataStore(accountId, repository, schedulers), + new TrustStore(accountId, repository, schedulers)); + } - @Override - protected void writePublicKeysOf(BareJid owner, PGPPublicKeyRingCollection publicKeys) - throws IOException { - repository.storePublicKeysOf(owner.asEntityBareJidIfPossible(), publicKeys) - .subscribeOn(schedulers.getIoScheduler()).subscribe(); - } + private static class KeyStore extends AbstractOpenPgpKeyStore { - @Override - protected PGPSecretKeyRingCollection readSecretKeysOf(BareJid owner) - throws IOException, PGPException { - try { - return repository.loadSecretKeysOf(owner.asEntityBareJidIfPossible()) - .blockingGet(); - } catch (NoSuchElementException e) { - return null; - } - } + private final CompositeDisposable disposable = new CompositeDisposable(); + private final OpenPgpRepository repository; + private final SchedulersFacade schedulers; - @Override - protected void writeSecretKeysOf(BareJid owner, PGPSecretKeyRingCollection secretKeys) - throws IOException { - repository.storeSecretKeysOf(owner.asEntityBareJidIfPossible(), secretKeys) - .subscribeOn(schedulers.getIoScheduler()).subscribe(); - } + private final UUID accountId; - @Override - protected Map readKeyFetchDates(BareJid owner) - throws IOException { - return new HashMap<>(); - } - @Override - protected void writeKeyFetchDates(BareJid owner, Map dates) - throws IOException { + public KeyStore(UUID accountId, OpenPgpRepository repository, SchedulersFacade schedulers) { + this.accountId = accountId; + this.repository = repository; + this.schedulers = schedulers; + } - } - }, new AbstractOpenPgpMetadataStore() { - @Override - protected Map readAnnouncedFingerprintsOf(BareJid contact) - throws IOException { + @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 writeAnnouncedFingerprintsOf(BareJid contact, Map metadata) - throws IOException { + @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; } - }, new AbstractOpenPgpTrustStore() { - @Override - protected Trust readTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint) - throws IOException { - return Trust.trusted; - } + } - @Override - protected void writeTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint, Trust trust) - throws IOException { + @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 + ")") + )); + } - } - }); - this.repository = repository; - this.schedulers = schedulers; + @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(); + } } }