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 f96bb3f..cbd97a5 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 @@ -3,6 +3,7 @@ package org.mercury_im.messenger.android.di.component; import org.mercury_im.messenger.android.MercuryImApplication; import org.mercury_im.messenger.android.di.module.AndroidDatabaseModule; import org.mercury_im.messenger.android.di.module.AndroidSchedulersModule; +import org.mercury_im.messenger.core.di.module.OpenPgpModule; import org.mercury_im.messenger.core.di.module.RxMercuryMessageStoreFactoryModule; import org.mercury_im.messenger.core.di.module.RxMercuryRosterStoreFactoryModule; import org.mercury_im.messenger.core.di.module.XmppTcpConnectionFactoryModule; @@ -43,7 +44,8 @@ import dagger.Component; ViewModelModule.class, XmppTcpConnectionFactoryModule.class, RxMercuryMessageStoreFactoryModule.class, - RxMercuryRosterStoreFactoryModule.class + RxMercuryRosterStoreFactoryModule.class, + OpenPgpModule.class }) public interface AppComponent { diff --git a/data/src/main/java/org/mercury_im/messenger/data/di/RepositoryModule.java b/data/src/main/java/org/mercury_im/messenger/data/di/RepositoryModule.java index 4139151..abffe85 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/di/RepositoryModule.java +++ b/data/src/main/java/org/mercury_im/messenger/data/di/RepositoryModule.java @@ -5,6 +5,7 @@ import org.mercury_im.messenger.core.data.repository.DirectChatRepository; import org.mercury_im.messenger.core.data.repository.EntityCapsRepository; import org.mercury_im.messenger.core.data.repository.GroupChatRepository; 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.PeerRepository; import org.mercury_im.messenger.core.data.repository.Repositories; import org.mercury_im.messenger.data.mapping.AccountMapping; @@ -18,6 +19,7 @@ import org.mercury_im.messenger.data.repository.RxDirectChatRepository; import org.mercury_im.messenger.data.repository.RxEntityCapsRepository; import org.mercury_im.messenger.data.repository.RxGroupChatRepository; import org.mercury_im.messenger.data.repository.RxMessageRepository; +import org.mercury_im.messenger.data.repository.RxOpenPgpRepository; import org.mercury_im.messenger.data.repository.RxPeerRepository; import javax.inject.Singleton; @@ -84,6 +86,13 @@ public class RepositoryModule { return new RxEntityCapsRepository(data, entityCapsMapping); } + @Provides + @Singleton + static OpenPgpRepository provideOpenPgpRepository( + ReactiveEntityStore data) { + return new RxOpenPgpRepository(data); + } + @Provides @Singleton static Repositories provideRepositories( @@ -92,8 +101,9 @@ public class RepositoryModule { GroupChatRepository groupChatRepository, MessageRepository messageRepository, PeerRepository peerRepository, - RxEntityCapsRepository entityCapsRepository) { + EntityCapsRepository entityCapsRepository, + OpenPgpRepository openPgpRepository) { return new Repositories(accountRepository, directChatRepository, groupChatRepository, - messageRepository, peerRepository, entityCapsRepository); + messageRepository, peerRepository, entityCapsRepository, openPgpRepository); } } 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/AbstractOpenPgpKeyFetchDates.java new file mode 100644 index 0000000..7f0cb52 --- /dev/null +++ b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractOpenPgpKeyFetchDates.java @@ -0,0 +1,27 @@ +package org.mercury_im.messenger.data.model; + +import org.jxmpp.jid.EntityBareJid; +import org.mercury_im.messenger.data.converter.EntityBareJidConverter; +import org.pgpainless.key.OpenPgpV4Fingerprint; + +import java.util.Date; +import java.util.Map; + +import io.requery.Column; +import io.requery.Convert; +import io.requery.Entity; +import io.requery.Key; +import io.requery.Table; + +@Entity +@Table(name = "ox_key_fetch_dates") +public class AbstractOpenPgpKeyFetchDates { + + @Key + @Column(name = "owner") + @Convert(EntityBareJidConverter.class) + EntityBareJid owner; + + @Column(name = "fetch_dates") + Map fetchDates; +} 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 18c98df..902220c 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 @@ -2,41 +2,18 @@ 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.UUID; - -import io.requery.CascadeAction; import io.requery.Column; import io.requery.Convert; import io.requery.Entity; -import io.requery.ForeignKey; -import io.requery.Index; import io.requery.Key; -import io.requery.ManyToOne; import io.requery.Table; -import io.requery.converter.UUIDConverter; @Entity @Table(name = "ox_public_keys") public class AbstractOpenPgpPublicKeyRing { + @Key - @Convert(UUIDConverter.class) - UUID id; - - @Index("unique_address") - @ManyToOne(cascade = CascadeAction.NONE) - @ForeignKey(referencedColumn = "id") - AccountModel account; - - @Column(name = "key_id", nullable = false) - long keyId; - - @Column(name = "fingerprint", nullable = false) - @Convert(OpenPgpV4FingerprintConverter.class) - OpenPgpV4Fingerprint fingerprint; - @Column(name = "owner", nullable = false) @Convert(EntityBareJidConverter.class) EntityBareJid owner; 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 29ab420..630e862 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 @@ -2,41 +2,18 @@ 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.UUID; - -import io.requery.CascadeAction; import io.requery.Column; import io.requery.Convert; import io.requery.Entity; -import io.requery.ForeignKey; -import io.requery.Index; import io.requery.Key; -import io.requery.ManyToOne; import io.requery.Table; -import io.requery.converter.UUIDConverter; @Entity @Table(name = "ox_secret_keys") public class AbstractOpenPgpSecretKeyRing { + @Key - @Convert(UUIDConverter.class) - UUID id; - - @Index("unique_address") - @ManyToOne(cascade = CascadeAction.NONE) - @ForeignKey(referencedColumn = "id") - AccountModel account; - - @Column(name = "key_id", nullable = false) - long keyId; - - @Column(name = "fingerprint", nullable = false) - @Convert(OpenPgpV4FingerprintConverter.class) - OpenPgpV4Fingerprint fingerprint; - @Column(name = "owner", nullable = false) @Convert(EntityBareJidConverter.class) EntityBareJid owner; 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 eb50ea5..e8a23b1 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 @@ -1,53 +1,78 @@ package org.mercury_im.messenger.data.repository; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; +import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.jxmpp.jid.EntityBareJid; import org.mercury_im.messenger.core.data.repository.OpenPgpRepository; 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.UUID; +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 { private final ReactiveEntityStore data; + @Inject public RxOpenPgpRepository(ReactiveEntityStore data) { this.data = data; } @Override - public Single loadPublicKeysOfContact(UUID accountId, EntityBareJid jid) { - return data.select(OpenPgpPublicKeyRing.class) - .where(OpenPgpPublicKeyRing.ACCOUNT_ID.eq(accountId)) - .and(OpenPgpPublicKeyRing.OWNER.eq(jid)) - .get().observableResult() - .map(ResultDelegate::toList) - .map(keys -> PGPainless.readKeyRing().publicKeyRing(keys.get(0).getBytes()).) + public Completable storePublicKeysOf(EntityBareJid owner, PGPPublicKeyRingCollection keys) { + return Single.fromCallable(() -> { + OpenPgpPublicKeyRing keyRing = new OpenPgpPublicKeyRing(); + keyRing.setOwner(owner); + keyRing.setBytes(keys.getEncoded()); + return keyRing; + }).flatMap(data::upsert).ignoreElement(); } @Override - public Single deletePublicKeyRing(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint) { + public Single loadPublicKeysOf(EntityBareJid owner) { + return data.select(OpenPgpPublicKeyRing.class) + .where(OpenPgpPublicKeyRing.OWNER.eq(owner)) + .limit(1).get() + .maybe().toSingle() + .map(keyring -> PGPainless.readKeyRing().publicKeyRingCollection(keyring.getBytes())); + } + + @Override + public Single deletePublicKeysOf(EntityBareJid owner) { return data.delete(OpenPgpPublicKeyRing.class) - .where(OpenPgpPublicKeyRing.ACCOUNT_ID.eq(accountId)) - .and(OpenPgpPublicKeyRing.OWNER.eq(jid)) - .and(OpenPgpPublicKeyRing.FINGERPRINT.eq(fingerprint)) + .where(OpenPgpPublicKeyRing.OWNER.eq(owner)) .get().single(); } @Override - public Single deleteSecretKeyRing(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint) { + public Completable storeSecretKeysOf(EntityBareJid owner, PGPSecretKeyRingCollection keys) { + return Single.fromCallable(() -> { + OpenPgpSecretKeyRing keyRing = new OpenPgpSecretKeyRing(); + keyRing.setOwner(owner); + keyRing.setBytes(keys.getEncoded()); + return keyRing; + }).flatMap(data::upsert).ignoreElement(); + } + + @Override + public Single loadSecretKeysOf(EntityBareJid owner) { + return data.select(OpenPgpSecretKeyRing.class) + .where(OpenPgpSecretKeyRing.OWNER.eq(owner)) + .limit(1).get() + .maybe().toSingle() + .map(keyring -> PGPainless.readKeyRing().secretKeyRingCollection(keyring.getBytes())); + } + + @Override + public Single deleteSecretKeysOf(EntityBareJid owner) { return data.delete(OpenPgpSecretKeyRing.class) - .where(OpenPgpSecretKeyRing.ACCOUNT_ID.eq(accountId)) - .and(OpenPgpPublicKeyRing.OWNER.eq(jid)) - .and(OpenPgpSecretKeyRing.FINGERPRINT.eq(fingerprint)) + .where(OpenPgpPublicKeyRing.OWNER.eq(owner)) .get().single(); } } 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 new file mode 100644 index 0000000..1ae7382 --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/core/crypto/MercuryOpenPgpManager.java @@ -0,0 +1,69 @@ +package org.mercury_im.messenger.core.crypto; + +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.exception.InvalidBackupCodeException; +import org.jivesoftware.smackx.ox.exception.NoBackupFoundException; +import org.jivesoftware.smackx.ox_im.OXInstantMessagingManager; +import org.jivesoftware.smackx.pubsub.PubSubException; +import org.mercury_im.messenger.core.xmpp.MercuryConnection; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.inject.Inject; + +public class MercuryOpenPgpManager { + + private static final Logger LOGGER = Logger.getLogger(MercuryOpenPgpManager.class.getName()); + + private final OpenPgpProvider openPgpProvider; + + @Inject + public MercuryOpenPgpManager(OpenPgpProvider openPgpProvider) { + this.openPgpProvider = openPgpProvider; + } + + public void initialize(MercuryConnection connection) { + if (connection.getConnection().isAuthenticated()) { + setup(connection); + } else { + connection.getConnection().addConnectionListener(new AbstractConnectionListener() { + @Override + public void authenticated(XMPPConnection con, boolean resumed) { + if (!resumed) { + setup(connection); + } + } + }); + } + } + + private void setup(MercuryConnection connection) { + try { + OpenPgpManager oxManager = OpenPgpManager.getInstanceFor(connection.getConnection()); + oxManager.setOpenPgpProvider(openPgpProvider); + if (!oxManager.hasSecretKeysAvailable()) { + try { + oxManager.restoreSecretKeyServerBackup( + //() -> "RW8X-367S-A2C3-QYAL-VG6E-Z2IM"); + () -> "KISJ-5Z1T-FGDW-WMDK-SC2U-SQUA"); + 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); + oxManager.generateAndImportKeyPair(connection.getAccount().getJid()); + oxManager.backupSecretKeyToServer( + backupCode -> LOGGER.log(Level.INFO, "OpenPGP Backup Code: " + backupCode), + availableSecretKeys -> availableSecretKeys); + } + } + oxManager.announceSupportAndPublish(); + OXInstantMessagingManager oximManager = OXInstantMessagingManager.getInstanceFor(connection.getConnection()); + oximManager.announceSupportForOxInstantMessaging(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} 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 990b500..d1ab3bb 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 @@ -1,18 +1,23 @@ package org.mercury_im.messenger.core.data.repository; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; +import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.jxmpp.jid.EntityBareJid; -import org.pgpainless.key.OpenPgpV4Fingerprint; - -import java.util.UUID; +import io.reactivex.Completable; import io.reactivex.Single; public interface OpenPgpRepository { - Single loadPublicKeysOfContact(UUID accountId, EntityBareJid jid); + Completable storePublicKeysOf(EntityBareJid owner, PGPPublicKeyRingCollection keys); - Single deletePublicKeyRing(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint); + Single loadPublicKeysOf(EntityBareJid owner); - Single deleteSecretKeyRing(UUID accountId, EntityBareJid jid, OpenPgpV4Fingerprint fingerprint); + Single deletePublicKeysOf(EntityBareJid owner); + + Completable storeSecretKeysOf(EntityBareJid owner, PGPSecretKeyRingCollection keys); + + Single loadSecretKeysOf(EntityBareJid owner); + + Single deleteSecretKeysOf(EntityBareJid owner); } diff --git a/domain/src/main/java/org/mercury_im/messenger/core/data/repository/Repositories.java b/domain/src/main/java/org/mercury_im/messenger/core/data/repository/Repositories.java index 0ca68e0..6179ba5 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/data/repository/Repositories.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/data/repository/Repositories.java @@ -3,15 +3,19 @@ package org.mercury_im.messenger.core.data.repository; import javax.inject.Inject; import javax.inject.Singleton; +import lombok.Value; + @Singleton +@Value public class Repositories { - private final AccountRepository accountRepository; - private final DirectChatRepository directChatRepository; - private final GroupChatRepository groupChatRepository; - private final MessageRepository messageRepository; - private final PeerRepository peerRepository; - private final EntityCapsRepository entityCapsRepository; + AccountRepository accountRepository; + DirectChatRepository directChatRepository; + GroupChatRepository groupChatRepository; + MessageRepository messageRepository; + PeerRepository peerRepository; + EntityCapsRepository entityCapsRepository; + OpenPgpRepository openPgpRepository; @Inject public Repositories(AccountRepository accountRepository, @@ -19,36 +23,14 @@ public class Repositories { GroupChatRepository groupChatRepository, MessageRepository messageRepository, PeerRepository peerRepository, - EntityCapsRepository entityCapsRepository) { + EntityCapsRepository entityCapsRepository, + OpenPgpRepository openPgpRepository) { this.accountRepository = accountRepository; this.directChatRepository = directChatRepository; this.groupChatRepository = groupChatRepository; this.messageRepository = messageRepository; this.peerRepository = peerRepository; this.entityCapsRepository = entityCapsRepository; - } - - public AccountRepository getAccountRepository() { - return accountRepository; - } - - public DirectChatRepository getDirectChatRepository() { - return directChatRepository; - } - - public GroupChatRepository getGroupChatRepository() { - return groupChatRepository; - } - - public MessageRepository getMessageRepository() { - return messageRepository; - } - - public PeerRepository getPeerRepository() { - return peerRepository; - } - - public EntityCapsRepository getEntityCapsRepository() { - return entityCapsRepository; + this.openPgpRepository = openPgpRepository; } } 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 new file mode 100644 index 0000000..20f2be4 --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/core/di/module/OpenPgpModule.java @@ -0,0 +1,29 @@ +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 4ee5bda..879a8ee 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 @@ -1,136 +1,106 @@ package org.mercury_im.messenger.core.store.crypto; import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; -import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; -import org.jivesoftware.smackx.ox.OpenPgpContact; -import org.jivesoftware.smackx.ox.callback.SecretKeyPassphraseCallback; -import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException; -import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore; +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 org.pgpainless.key.collection.PGPKeyRing; -import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.key.protection.UnprotectedKeysProtector; import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; import java.util.Date; +import java.util.HashMap; import java.util.Map; -import java.util.UUID; +import java.util.NoSuchElementException; import javax.inject.Inject; -public class MercuryOpenPgpStore implements OpenPgpStore { +public class MercuryOpenPgpStore extends AbstractOpenPgpStore { - private final UUID accountId; - private SecretKeyRingProtector keyRingProtector; - private SecretKeyPassphraseCallback passphraseCallback; + private final OpenPgpRepository repository; + private final SchedulersFacade schedulers; @Inject - OpenPgpRepository repository; + 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) { - this.accountId = accountId; - } + @Override + protected void writePublicKeysOf(BareJid owner, PGPPublicKeyRingCollection publicKeys) + throws IOException { + repository.storePublicKeysOf(owner.asEntityBareJidIfPossible(), publicKeys) + .subscribeOn(schedulers.getIoScheduler()).subscribe(); + } - @Override - public OpenPgpContact getOpenPgpContact(BareJid contactsJid) { - return null; - } + @Override + protected PGPSecretKeyRingCollection readSecretKeysOf(BareJid owner) + throws IOException, PGPException { + try { + return repository.loadSecretKeysOf(owner.asEntityBareJidIfPossible()) + .blockingGet(); + } catch (NoSuchElementException e) { + return null; + } + } - @Override - public void setKeyRingProtector(SecretKeyRingProtector unlocker) { - this.keyRingProtector = unlocker; - } + @Override + protected void writeSecretKeysOf(BareJid owner, PGPSecretKeyRingCollection secretKeys) + throws IOException { + repository.storeSecretKeysOf(owner.asEntityBareJidIfPossible(), secretKeys) + .subscribeOn(schedulers.getIoScheduler()).subscribe(); + } - @Override - public SecretKeyRingProtector getKeyRingProtector() { - return keyRingProtector; - } + @Override + protected Map readKeyFetchDates(BareJid owner) + throws IOException { + return new HashMap<>(); + } - @Override - public void setSecretKeyPassphraseCallback(SecretKeyPassphraseCallback callback) { - passphraseCallback = callback; - } + @Override + protected void writeKeyFetchDates(BareJid owner, Map dates) + throws IOException { - @Override - public PGPPublicKeyRingCollection getPublicKeysOf(BareJid owner) throws IOException, PGPException { - return null; - } + } + }, new AbstractOpenPgpMetadataStore() { + @Override + protected Map readAnnouncedFingerprintsOf(BareJid contact) + throws IOException { + return null; + } - @Override - public PGPSecretKeyRingCollection getSecretKeysOf(BareJid owner) throws IOException, PGPException { - return null; - } + @Override + protected void writeAnnouncedFingerprintsOf(BareJid contact, Map metadata) + throws IOException { - @Override - public PGPPublicKeyRing getPublicKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { - return null; - } + } + }, new AbstractOpenPgpTrustStore() { + @Override + protected Trust readTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint) + throws IOException { + return Trust.trusted; + } - @Override - public PGPSecretKeyRing getSecretKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { - return null; - } - - @Override - public void deletePublicKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { - - } - - @Override - public void deleteSecretKeyRing(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException, PGPException { - - } - - @Override - public PGPKeyRing generateKeyRing(BareJid owner) throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { - return null; - } - - @Override - public void importSecretKey(BareJid owner, PGPSecretKeyRing secretKeys) throws IOException, PGPException, MissingUserIdOnKeyException { - - } - - @Override - public void importPublicKey(BareJid owner, PGPPublicKeyRing publicKeys) throws IOException, PGPException, MissingUserIdOnKeyException { - - } - - @Override - public Map getPublicKeyFetchDates(BareJid contact) throws IOException { - return null; - } - - @Override - public void setPublicKeyFetchDates(BareJid contact, Map dates) throws IOException { - - } - - @Override - public Map getAnnouncedFingerprintsOf(BareJid contact) throws IOException { - return null; - } - - @Override - public void setAnnouncedFingerprintsOf(BareJid contact, Map data) throws IOException { - - } - - @Override - public Trust getTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException { - return null; - } - - @Override - public void setTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint, Trust trust) throws IOException { + @Override + protected void writeTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint, Trust trust) + throws IOException { + } + }); + this.repository = repository; + this.schedulers = schedulers; } } diff --git a/domain/src/main/java/org/mercury_im/messenger/core/store/message/MercuryMessageStore.java b/domain/src/main/java/org/mercury_im/messenger/core/store/message/MercuryMessageStore.java index 1f54efb..b95aea8 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/store/message/MercuryMessageStore.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/store/message/MercuryMessageStore.java @@ -76,7 +76,7 @@ public class MercuryMessageStore implements IncomingChatMessageListener, Outgoin message.setSender(account.getAddress()); message.setRecipient(to.asBareJid().toString()); if (smackMessage.getBody() != null) { - message.setBody(smackMessage.getBody().getMessage()); + message.setBody(smackMessage.getBody()); } disposable.add(writeMessageToStore(to.asEntityBareJidString(), message)); } diff --git a/domain/src/main/java/org/mercury_im/messenger/core/xmpp/MercuryConnectionManager.java b/domain/src/main/java/org/mercury_im/messenger/core/xmpp/MercuryConnectionManager.java index dd84749..a917961 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/xmpp/MercuryConnectionManager.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/xmpp/MercuryConnectionManager.java @@ -3,6 +3,7 @@ package org.mercury_im.messenger.core.xmpp; import org.jivesoftware.smack.chat2.ChatManager; import org.jivesoftware.smackx.caps.EntityCapsManager; import org.mercury_im.messenger.core.SchedulersFacade; +import org.mercury_im.messenger.core.crypto.MercuryOpenPgpManager; import org.mercury_im.messenger.core.data.repository.AccountRepository; import org.mercury_im.messenger.core.data.repository.DirectChatRepository; import org.mercury_im.messenger.core.data.repository.MessageRepository; @@ -48,6 +49,7 @@ public class MercuryConnectionManager { private final PeerRepository peerRepository; private final DirectChatRepository directChatRepository; private final MessageRepository messageRepository; + private final MercuryOpenPgpManager cryptoManager; private final SchedulersFacade schedulers; private final Map connectionsMap = new ConcurrentHashMap<>(); @@ -67,6 +69,7 @@ public class MercuryConnectionManager { MercuryEntityCapsStore entityCapsStore, MercuryMessageStoreFactory messageStoreFactory, XmppConnectionFactory connectionFactory, + MercuryOpenPgpManager cryptoManager, SchedulersFacade schedulers) { this.accountRepository = repositories.getAccountRepository(); this.rosterStoreBinder = rosterStoreBinder; @@ -76,6 +79,7 @@ public class MercuryConnectionManager { this.messageRepository = repositories.getMessageRepository(); this.connectionFactory = connectionFactory; this.messageStoreFactory = messageStoreFactory; + this.cryptoManager = cryptoManager; this.schedulers = schedulers; EntityCapsManager.setPersistentCache(entityCapsStore); @@ -156,7 +160,7 @@ public class MercuryConnectionManager { chatManager.addIncomingListener(mercuryMessageStore); chatManager.addOutgoingListener(mercuryMessageStore); })); - + cryptoManager.initialize(connection); } private void handleOptionalAccountChangedEvent(MercuryConnection connection, Optional event) { diff --git a/domain/src/main/java/org/mercury_im/messenger/core/xmpp/SmackConfig.java b/domain/src/main/java/org/mercury_im/messenger/core/xmpp/SmackConfig.java index f348546..75e888d 100644 --- a/domain/src/main/java/org/mercury_im/messenger/core/xmpp/SmackConfig.java +++ b/domain/src/main/java/org/mercury_im/messenger/core/xmpp/SmackConfig.java @@ -1,6 +1,7 @@ package org.mercury_im.messenger.core.xmpp; import org.jivesoftware.smack.ReconnectionManager; +import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.roster.Roster; import org.jivesoftware.smackx.carbons.CarbonManager; import org.jivesoftware.smackx.iqversion.VersionManager; @@ -11,6 +12,7 @@ import org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager; public class SmackConfig { static void staticConfiguration() { + SmackConfiguration.DEBUG = true; ReconnectionManager.setEnabledPerDefault(true); ReconnectionManager.setDefaultReconnectionPolicy(ReconnectionManager.ReconnectionPolicy.RANDOM_INCREASING_DELAY); diff --git a/entity/build.gradle b/entity/build.gradle index f8f77ed..43fc962 100644 --- a/entity/build.gradle +++ b/entity/build.gradle @@ -3,6 +3,8 @@ apply plugin: 'java-library' dependencies { compileOnly "org.projectlombok:lombok:$lombokVersion" annotationProcessor "org.projectlombok:lombok:$lombokVersion" + + implementation "org.jxmpp:jxmpp-jid:$jxmppVersion" } sourceCompatibility = "8" diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/Account.java b/entity/src/main/java/org/mercury_im/messenger/entity/Account.java index 88d72a2..5d38c24 100644 --- a/entity/src/main/java/org/mercury_im/messenger/entity/Account.java +++ b/entity/src/main/java/org/mercury_im/messenger/entity/Account.java @@ -1,5 +1,8 @@ package org.mercury_im.messenger.entity; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; + import java.util.UUID; import lombok.Data; @@ -21,4 +24,8 @@ public class Account { this.id = UUID.randomUUID(); this.rosterVersion = ""; } + + public EntityBareJid getJid() { + return JidCreate.entityBareFromOrThrowUnchecked(getAddress()); + } } diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/contact/Peer.java b/entity/src/main/java/org/mercury_im/messenger/entity/contact/Peer.java index 9849763..aa509e6 100644 --- a/entity/src/main/java/org/mercury_im/messenger/entity/contact/Peer.java +++ b/entity/src/main/java/org/mercury_im/messenger/entity/contact/Peer.java @@ -1,5 +1,7 @@ package org.mercury_im.messenger.entity.contact; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; import org.mercury_im.messenger.entity.Account; import java.util.List; @@ -32,4 +34,8 @@ public class Peer { } return address.substring(0, address.indexOf('@')); } + + public EntityBareJid getJid() { + return JidCreate.entityBareFromOrThrowUnchecked(getAddress()); + } } diff --git a/libs/Smack b/libs/Smack index 219efa5..58ce1dd 160000 --- a/libs/Smack +++ b/libs/Smack @@ -1 +1 @@ -Subproject commit 219efa564c222b7add06870bb51ba85dfe353cc7 +Subproject commit 58ce1dd62f11b40a7063802fa079d827c90b0f84 diff --git a/version.gradle b/version.gradle index fbc1140..6dfecef 100644 --- a/version.gradle +++ b/version.gradle @@ -4,6 +4,7 @@ ext { // Smack Versions // Quickly switch between unique and normal releases using the toggle below + jxmppVersion = '0.7.0-alpha6' // Version Strings for non-unique releases //* smackVersion = shortVersion