Add OX store implementation
This commit is contained in:
parent
13ccdbc6e4
commit
6efd8a1940
|
@ -44,8 +44,7 @@ import dagger.Component;
|
||||||
ViewModelModule.class,
|
ViewModelModule.class,
|
||||||
XmppTcpConnectionFactoryModule.class,
|
XmppTcpConnectionFactoryModule.class,
|
||||||
RxMercuryMessageStoreFactoryModule.class,
|
RxMercuryMessageStoreFactoryModule.class,
|
||||||
RxMercuryRosterStoreFactoryModule.class,
|
RxMercuryRosterStoreFactoryModule.class
|
||||||
OpenPgpModule.class
|
|
||||||
})
|
})
|
||||||
public interface AppComponent {
|
public interface AppComponent {
|
||||||
|
|
||||||
|
|
|
@ -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<OpenPgpTrustStore.Trust> {
|
||||||
|
public OpenPgpTrustConverter() {
|
||||||
|
super(OpenPgpTrustStore.Trust.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -2,26 +2,35 @@ package org.mercury_im.messenger.data.model;
|
||||||
|
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.mercury_im.messenger.data.converter.EntityBareJidConverter;
|
import org.mercury_im.messenger.data.converter.EntityBareJidConverter;
|
||||||
|
import org.mercury_im.messenger.data.converter.OpenPgpV4FingerprintConverter;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.UUID;
|
||||||
|
|
||||||
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.Key;
|
import io.requery.Key;
|
||||||
import io.requery.Table;
|
import io.requery.Table;
|
||||||
|
import io.requery.converter.UUIDConverter;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "ox_key_fetch_dates")
|
@Table(name = "ox_pubkey_fetchdates")
|
||||||
public class AbstractOpenPgpKeyFetchDates {
|
public class AbstractOpenPgpKeyFetchDate {
|
||||||
|
|
||||||
|
@Key
|
||||||
|
@Convert(UUIDConverter.class)
|
||||||
|
UUID accountId;
|
||||||
|
|
||||||
@Key
|
@Key
|
||||||
@Column(name = "owner")
|
|
||||||
@Convert(EntityBareJidConverter.class)
|
@Convert(EntityBareJidConverter.class)
|
||||||
EntityBareJid owner;
|
EntityBareJid owner;
|
||||||
|
|
||||||
@Column(name = "fetch_dates")
|
@Key
|
||||||
Map<OpenPgpV4Fingerprint, Date> fetchDates;
|
@Convert(OpenPgpV4FingerprintConverter.class)
|
||||||
|
OpenPgpV4Fingerprint fingerprint;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
Date fetchDate;
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -3,6 +3,8 @@ package org.mercury_im.messenger.data.model;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.mercury_im.messenger.data.converter.EntityBareJidConverter;
|
import org.mercury_im.messenger.data.converter.EntityBareJidConverter;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import io.requery.Column;
|
import io.requery.Column;
|
||||||
import io.requery.Convert;
|
import io.requery.Convert;
|
||||||
import io.requery.Entity;
|
import io.requery.Entity;
|
||||||
|
@ -13,6 +15,10 @@ import io.requery.Table;
|
||||||
@Table(name = "ox_public_keys")
|
@Table(name = "ox_public_keys")
|
||||||
public class AbstractOpenPgpPublicKeyRing {
|
public class AbstractOpenPgpPublicKeyRing {
|
||||||
|
|
||||||
|
@Key
|
||||||
|
@Column(name = "account_id", nullable = false)
|
||||||
|
UUID accountId;
|
||||||
|
|
||||||
@Key
|
@Key
|
||||||
@Column(name = "owner", nullable = false)
|
@Column(name = "owner", nullable = false)
|
||||||
@Convert(EntityBareJidConverter.class)
|
@Convert(EntityBareJidConverter.class)
|
||||||
|
|
|
@ -3,6 +3,8 @@ package org.mercury_im.messenger.data.model;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.mercury_im.messenger.data.converter.EntityBareJidConverter;
|
import org.mercury_im.messenger.data.converter.EntityBareJidConverter;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import io.requery.Column;
|
import io.requery.Column;
|
||||||
import io.requery.Convert;
|
import io.requery.Convert;
|
||||||
import io.requery.Entity;
|
import io.requery.Entity;
|
||||||
|
@ -13,6 +15,10 @@ import io.requery.Table;
|
||||||
@Table(name = "ox_secret_keys")
|
@Table(name = "ox_secret_keys")
|
||||||
public class AbstractOpenPgpSecretKeyRing {
|
public class AbstractOpenPgpSecretKeyRing {
|
||||||
|
|
||||||
|
@Key
|
||||||
|
@Column(name = "account_id", nullable = false)
|
||||||
|
UUID accountId;
|
||||||
|
|
||||||
@Key
|
@Key
|
||||||
@Column(name = "owner", nullable = false)
|
@Column(name = "owner", nullable = false)
|
||||||
@Convert(EntityBareJidConverter.class)
|
@Convert(EntityBareJidConverter.class)
|
||||||
|
|
|
@ -2,17 +2,31 @@ package org.mercury_im.messenger.data.repository;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
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.OpenPgpPublicKeyRing;
|
||||||
import org.mercury_im.messenger.data.model.OpenPgpSecretKeyRing;
|
import org.mercury_im.messenger.data.model.OpenPgpSecretKeyRing;
|
||||||
import org.pgpainless.PGPainless;
|
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 javax.inject.Inject;
|
||||||
|
|
||||||
import io.reactivex.Completable;
|
import io.reactivex.Completable;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
import io.requery.Persistable;
|
import io.requery.Persistable;
|
||||||
|
import io.requery.query.ResultDelegate;
|
||||||
import io.requery.reactivex.ReactiveEntityStore;
|
import io.requery.reactivex.ReactiveEntityStore;
|
||||||
|
|
||||||
public class RxOpenPgpRepository implements OpenPgpRepository {
|
public class RxOpenPgpRepository implements OpenPgpRepository {
|
||||||
|
@ -25,9 +39,10 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Completable storePublicKeysOf(EntityBareJid owner, PGPPublicKeyRingCollection keys) {
|
public Completable storePublicKeysOf(UUID accountId, EntityBareJid owner, PGPPublicKeyRingCollection keys) {
|
||||||
return Single.fromCallable(() -> {
|
return Single.fromCallable(() -> {
|
||||||
OpenPgpPublicKeyRing keyRing = new OpenPgpPublicKeyRing();
|
OpenPgpPublicKeyRing keyRing = new OpenPgpPublicKeyRing();
|
||||||
|
keyRing.setAccountId(accountId);
|
||||||
keyRing.setOwner(owner);
|
keyRing.setOwner(owner);
|
||||||
keyRing.setBytes(keys.getEncoded());
|
keyRing.setBytes(keys.getEncoded());
|
||||||
return keyRing;
|
return keyRing;
|
||||||
|
@ -35,25 +50,28 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Single<PGPPublicKeyRingCollection> loadPublicKeysOf(EntityBareJid owner) {
|
public Single<PGPPublicKeyRingCollection> loadPublicKeysOf(UUID accountId, EntityBareJid owner) {
|
||||||
return data.select(OpenPgpPublicKeyRing.class)
|
return data.select(OpenPgpPublicKeyRing.class)
|
||||||
.where(OpenPgpPublicKeyRing.OWNER.eq(owner))
|
.where(OpenPgpPublicKeyRing.ACCOUNT_ID.eq(accountId))
|
||||||
|
.and(OpenPgpPublicKeyRing.OWNER.eq(owner))
|
||||||
.limit(1).get()
|
.limit(1).get()
|
||||||
.maybe().toSingle()
|
.maybe().toSingle()
|
||||||
.map(keyring -> PGPainless.readKeyRing().publicKeyRingCollection(keyring.getBytes()));
|
.map(keyring -> PGPainless.readKeyRing().publicKeyRingCollection(keyring.getBytes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Single<Integer> deletePublicKeysOf(EntityBareJid owner) {
|
public Single<Integer> deletePublicKeysOf(UUID accountId, EntityBareJid owner) {
|
||||||
return data.delete(OpenPgpPublicKeyRing.class)
|
return data.delete(OpenPgpPublicKeyRing.class)
|
||||||
.where(OpenPgpPublicKeyRing.OWNER.eq(owner))
|
.where(OpenPgpPublicKeyRing.ACCOUNT_ID.eq(accountId))
|
||||||
|
.and(OpenPgpPublicKeyRing.OWNER.eq(owner))
|
||||||
.get().single();
|
.get().single();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Completable storeSecretKeysOf(EntityBareJid owner, PGPSecretKeyRingCollection keys) {
|
public Completable storeSecretKeysOf(UUID accountId, EntityBareJid owner, PGPSecretKeyRingCollection keys) {
|
||||||
return Single.fromCallable(() -> {
|
return Single.fromCallable(() -> {
|
||||||
OpenPgpSecretKeyRing keyRing = new OpenPgpSecretKeyRing();
|
OpenPgpSecretKeyRing keyRing = new OpenPgpSecretKeyRing();
|
||||||
|
keyRing.setAccountId(accountId);
|
||||||
keyRing.setOwner(owner);
|
keyRing.setOwner(owner);
|
||||||
keyRing.setBytes(keys.getEncoded());
|
keyRing.setBytes(keys.getEncoded());
|
||||||
return keyRing;
|
return keyRing;
|
||||||
|
@ -61,18 +79,107 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Single<PGPSecretKeyRingCollection> loadSecretKeysOf(EntityBareJid owner) {
|
public Single<PGPSecretKeyRingCollection> loadSecretKeysOf(UUID accountId, EntityBareJid owner) {
|
||||||
return data.select(OpenPgpSecretKeyRing.class)
|
return data.select(OpenPgpSecretKeyRing.class)
|
||||||
.where(OpenPgpSecretKeyRing.OWNER.eq(owner))
|
.where(OpenPgpSecretKeyRing.ACCOUNT_ID.eq(accountId))
|
||||||
|
.and(OpenPgpSecretKeyRing.OWNER.eq(owner))
|
||||||
.limit(1).get()
|
.limit(1).get()
|
||||||
.maybe().toSingle()
|
.maybe().toSingle()
|
||||||
.map(keyring -> PGPainless.readKeyRing().secretKeyRingCollection(keyring.getBytes()));
|
.map(keyring -> PGPainless.readKeyRing().secretKeyRingCollection(keyring.getBytes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Single<Integer> deleteSecretKeysOf(EntityBareJid owner) {
|
public Single<Integer> deleteSecretKeysOf(UUID accountId, EntityBareJid owner) {
|
||||||
return data.delete(OpenPgpSecretKeyRing.class)
|
return data.delete(OpenPgpSecretKeyRing.class)
|
||||||
.where(OpenPgpPublicKeyRing.OWNER.eq(owner))
|
.where(OpenPgpSecretKeyRing.ACCOUNT_ID.eq(accountId))
|
||||||
|
.and(OpenPgpSecretKeyRing.OWNER.eq(owner))
|
||||||
.get().single();
|
.get().single();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Completable storePublicKeyFetchDates(UUID accountId, EntityBareJid owner, Map<OpenPgpV4Fingerprint, Date> dates) {
|
||||||
|
List<OpenPgpKeyFetchDate> entities = new LinkedList<>();
|
||||||
|
for (Map.Entry<OpenPgpV4Fingerprint, Date> 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<OpenPgpV4Fingerprint, Date> metadata) {
|
||||||
|
List<AnnouncedOpenPgpContactKey> entities = new LinkedList<>();
|
||||||
|
for (Map.Entry<OpenPgpV4Fingerprint, Date> 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<Map<OpenPgpV4Fingerprint, Date>> 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<OpenPgpV4Fingerprint, Date> map = new ConcurrentHashMap<>();
|
||||||
|
for (AnnouncedOpenPgpContactKey key : list) {
|
||||||
|
map.put(key.getFingerprint(), key.getModificationDate());
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Single<OpenPgpTrustStore.Trust> 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<Map<OpenPgpV4Fingerprint, Date>> 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<OpenPgpV4Fingerprint, Date> map = new ConcurrentHashMap<>();
|
||||||
|
for (OpenPgpKeyFetchDate date : list) {
|
||||||
|
map.put(date.getFingerprint(), date.getFetchDate());
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,15 @@ import org.jivesoftware.smack.AbstractConnectionListener;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
||||||
import org.jivesoftware.smackx.ox.crypto.OpenPgpProvider;
|
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.InvalidBackupCodeException;
|
||||||
import org.jivesoftware.smackx.ox.exception.NoBackupFoundException;
|
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.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.data.repository.OpenPgpRepository;
|
||||||
|
import org.mercury_im.messenger.core.store.crypto.MercuryOpenPgpStore;
|
||||||
import org.mercury_im.messenger.core.xmpp.MercuryConnection;
|
import org.mercury_im.messenger.core.xmpp.MercuryConnection;
|
||||||
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
@ -19,11 +24,13 @@ public class MercuryOpenPgpManager {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(MercuryOpenPgpManager.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(MercuryOpenPgpManager.class.getName());
|
||||||
|
|
||||||
private final OpenPgpProvider openPgpProvider;
|
private final OpenPgpRepository openPgpRepository;
|
||||||
|
private final SchedulersFacade schedulers;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MercuryOpenPgpManager(OpenPgpProvider openPgpProvider) {
|
public MercuryOpenPgpManager(OpenPgpRepository openPgpRepository, SchedulersFacade schedulers) {
|
||||||
this.openPgpProvider = openPgpProvider;
|
this.openPgpRepository = openPgpRepository;
|
||||||
|
this.schedulers = schedulers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize(MercuryConnection connection) {
|
public void initialize(MercuryConnection connection) {
|
||||||
|
@ -42,14 +49,17 @@ public class MercuryOpenPgpManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setup(MercuryConnection connection) {
|
private void setup(MercuryConnection connection) {
|
||||||
|
OpenPgpStore store = new MercuryOpenPgpStore(connection.getAccountId(), openPgpRepository, schedulers);
|
||||||
|
OpenPgpProvider provider = new PainlessOpenPgpProvider(store);
|
||||||
try {
|
try {
|
||||||
OpenPgpManager oxManager = OpenPgpManager.getInstanceFor(connection.getConnection());
|
OpenPgpManager oxManager = OpenPgpManager.getInstanceFor(connection.getConnection());
|
||||||
oxManager.setOpenPgpProvider(openPgpProvider);
|
oxManager.setOpenPgpProvider(provider);
|
||||||
if (!oxManager.hasSecretKeysAvailable()) {
|
if (!oxManager.hasSecretKeysAvailable()) {
|
||||||
try {
|
try {
|
||||||
oxManager.restoreSecretKeyServerBackup(
|
oxManager.restoreSecretKeyServerBackup(
|
||||||
//() -> "RW8X-367S-A2C3-QYAL-VG6E-Z2IM");
|
//() -> "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!");
|
LOGGER.log(Level.INFO, "Successfully restored secret key backup!");
|
||||||
} catch (NoBackupFoundException | PubSubException.NotALeafNodeException | InvalidBackupCodeException e) {
|
} catch (NoBackupFoundException | PubSubException.NotALeafNodeException | InvalidBackupCodeException e) {
|
||||||
LOGGER.log(Level.INFO, "Error restoring secret key backup.", e);
|
LOGGER.log(Level.INFO, "Error restoring secret key backup.", e);
|
||||||
|
|
|
@ -2,22 +2,40 @@ package org.mercury_im.messenger.core.data.repository;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
|
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.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import io.reactivex.Completable;
|
import io.reactivex.Completable;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
|
||||||
public interface OpenPgpRepository {
|
public interface OpenPgpRepository {
|
||||||
|
|
||||||
Completable storePublicKeysOf(EntityBareJid owner, PGPPublicKeyRingCollection keys);
|
Completable storePublicKeysOf(UUID accountId, EntityBareJid owner, PGPPublicKeyRingCollection keys);
|
||||||
|
|
||||||
Single<PGPPublicKeyRingCollection> loadPublicKeysOf(EntityBareJid owner);
|
Single<PGPPublicKeyRingCollection> loadPublicKeysOf(UUID accountId, EntityBareJid owner);
|
||||||
|
|
||||||
Single<Integer> deletePublicKeysOf(EntityBareJid owner);
|
Single<Integer> deletePublicKeysOf(UUID accountId, EntityBareJid owner);
|
||||||
|
|
||||||
Completable storeSecretKeysOf(EntityBareJid owner, PGPSecretKeyRingCollection keys);
|
Completable storeSecretKeysOf(UUID accountId, EntityBareJid owner, PGPSecretKeyRingCollection keys);
|
||||||
|
|
||||||
Single<PGPSecretKeyRingCollection> loadSecretKeysOf(EntityBareJid owner);
|
Single<PGPSecretKeyRingCollection> loadSecretKeysOf(UUID accountId, EntityBareJid owner);
|
||||||
|
|
||||||
Single<Integer> deleteSecretKeysOf(EntityBareJid owner);
|
Single<Integer> deleteSecretKeysOf(UUID accountId, EntityBareJid owner);
|
||||||
|
|
||||||
|
Completable storePublicKeyFetchDates(UUID accountId, EntityBareJid owner, Map<OpenPgpV4Fingerprint, Date> dates);
|
||||||
|
|
||||||
|
Single<Map<OpenPgpV4Fingerprint, Date>> loadPublicKeyFetchDates(UUID accountId, EntityBareJid owner);
|
||||||
|
|
||||||
|
Completable storeAnnouncedFingerprints(UUID accountId, EntityBareJid owner, Map<OpenPgpV4Fingerprint, Date> metadata);
|
||||||
|
|
||||||
|
Single<Map<OpenPgpV4Fingerprint, Date>> loadAnnouncedFingerprints(UUID accountId, EntityBareJid contact);
|
||||||
|
|
||||||
|
Single<OpenPgpTrustStore.Trust> loadTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint);
|
||||||
|
|
||||||
|
Completable storeTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,8 @@
|
||||||
package org.mercury_im.messenger.core.di.module;
|
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.Module;
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class OpenPgpModule {
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,93 +14,182 @@ import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
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 {
|
public class MercuryOpenPgpStore extends AbstractOpenPgpStore {
|
||||||
|
|
||||||
private final OpenPgpRepository repository;
|
protected static final Logger LOGGER = Logger.getLogger(MercuryOpenPgpStore.class.getName());
|
||||||
private final SchedulersFacade schedulers;
|
|
||||||
|
|
||||||
@Inject
|
public MercuryOpenPgpStore(UUID accountId, OpenPgpRepository repository, SchedulersFacade schedulers) {
|
||||||
public MercuryOpenPgpStore(OpenPgpRepository repository, SchedulersFacade schedulers) {
|
super(
|
||||||
super(new AbstractOpenPgpKeyStore() {
|
new KeyStore(accountId, repository, schedulers),
|
||||||
@Override
|
new MetadataStore(accountId, repository, schedulers),
|
||||||
protected PGPPublicKeyRingCollection readPublicKeysOf(BareJid owner)
|
new TrustStore(accountId, repository, schedulers));
|
||||||
throws IOException, PGPException {
|
}
|
||||||
try {
|
|
||||||
return repository.loadPublicKeysOf(owner.asEntityBareJidIfPossible())
|
|
||||||
.blockingGet();
|
|
||||||
} catch (NoSuchElementException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private static class KeyStore extends AbstractOpenPgpKeyStore {
|
||||||
protected void writePublicKeysOf(BareJid owner, PGPPublicKeyRingCollection publicKeys)
|
|
||||||
throws IOException {
|
|
||||||
repository.storePublicKeysOf(owner.asEntityBareJidIfPossible(), publicKeys)
|
|
||||||
.subscribeOn(schedulers.getIoScheduler()).subscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||||
protected PGPSecretKeyRingCollection readSecretKeysOf(BareJid owner)
|
private final OpenPgpRepository repository;
|
||||||
throws IOException, PGPException {
|
private final SchedulersFacade schedulers;
|
||||||
try {
|
|
||||||
return repository.loadSecretKeysOf(owner.asEntityBareJidIfPossible())
|
|
||||||
.blockingGet();
|
|
||||||
} catch (NoSuchElementException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private final UUID accountId;
|
||||||
protected void writeSecretKeysOf(BareJid owner, PGPSecretKeyRingCollection secretKeys)
|
|
||||||
throws IOException {
|
|
||||||
repository.storeSecretKeysOf(owner.asEntityBareJidIfPossible(), secretKeys)
|
|
||||||
.subscribeOn(schedulers.getIoScheduler()).subscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Map<OpenPgpV4Fingerprint, Date> readKeyFetchDates(BareJid owner)
|
|
||||||
throws IOException {
|
|
||||||
return new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public KeyStore(UUID accountId, OpenPgpRepository repository, SchedulersFacade schedulers) {
|
||||||
protected void writeKeyFetchDates(BareJid owner, Map<OpenPgpV4Fingerprint, Date> dates)
|
this.accountId = accountId;
|
||||||
throws IOException {
|
this.repository = repository;
|
||||||
|
this.schedulers = schedulers;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
@Override
|
||||||
}, new AbstractOpenPgpMetadataStore() {
|
protected PGPPublicKeyRingCollection readPublicKeysOf(BareJid owner)
|
||||||
@Override
|
throws IOException, PGPException {
|
||||||
protected Map<OpenPgpV4Fingerprint, Date> readAnnouncedFingerprintsOf(BareJid contact)
|
try {
|
||||||
throws IOException {
|
return repository.loadPublicKeysOf(accountId, owner.asEntityBareJidIfPossible())
|
||||||
|
.blockingGet();
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeAnnouncedFingerprintsOf(BareJid contact, Map<OpenPgpV4Fingerprint, Date> metadata)
|
protected void writePublicKeysOf(BareJid owner, PGPPublicKeyRingCollection publicKeys)
|
||||||
throws IOException {
|
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
|
@Override
|
||||||
protected void writeTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint, Trust trust)
|
protected void writeSecretKeysOf(BareJid owner, PGPSecretKeyRingCollection secretKeys)
|
||||||
throws IOException {
|
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<OpenPgpV4Fingerprint, Date> readKeyFetchDates(BareJid owner)
|
||||||
this.repository = repository;
|
throws IOException {
|
||||||
this.schedulers = schedulers;
|
return repository.loadPublicKeyFetchDates(accountId, owner.asEntityBareJidIfPossible())
|
||||||
|
.blockingGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeKeyFetchDates(BareJid owner, Map<OpenPgpV4Fingerprint, Date> 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<OpenPgpV4Fingerprint, Date> readAnnouncedFingerprintsOf(BareJid contact)
|
||||||
|
throws IOException {
|
||||||
|
return repository.loadAnnouncedFingerprints(accountId, contact.asEntityBareJidIfPossible())
|
||||||
|
.blockingGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeAnnouncedFingerprintsOf(BareJid contact, Map<OpenPgpV4Fingerprint, Date> 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue