mirror of
https://codeberg.org/Mercury-IM/Mercury-IM
synced 2025-03-20 23:53:12 +01:00
Wip: Ikey storage backend
This commit is contained in:
parent
94d213b8dd
commit
733f9684c7
26 changed files with 784 additions and 23 deletions
|
@ -0,0 +1,48 @@
|
|||
package org.mercury_im.messenger.data.converter;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
import org.pgpainless.PGPainless;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.requery.Converter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
public class Base64PGPPublicKeyRingConverter implements Converter<PGPPublicKeyRing, String> {
|
||||
@Override
|
||||
public Class<PGPPublicKeyRing> getMappedType() {
|
||||
return PGPPublicKeyRing.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<String> getPersistedType() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Integer getPersistedSize() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public String convertToPersisted(PGPPublicKeyRing value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(Base64.encode(value.getEncoded()), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public PGPPublicKeyRing convertToMapped(Class<? extends PGPPublicKeyRing> type, @Nullable String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return PGPainless.readKeyRing().publicKeyRing(Base64.decode(value.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package org.mercury_im.messenger.data.converter;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
import org.pgpainless.PGPainless;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.requery.Converter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
public class Base64PGPSecretKeyRingConverter implements Converter<PGPSecretKeyRing, String> {
|
||||
@Override
|
||||
public Class<PGPSecretKeyRing> getMappedType() {
|
||||
return PGPSecretKeyRing.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<String> getPersistedType() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Integer getPersistedSize() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public String convertToPersisted(PGPSecretKeyRing value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(Base64.encode(value.getEncoded()), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public PGPSecretKeyRing convertToMapped(Class<? extends PGPSecretKeyRing> type, @Nullable String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return PGPainless.readKeyRing().secretKeyRing(Base64.decode(value.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package org.mercury_im.messenger.data.converter;
|
||||
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.requery.Converter;
|
||||
|
||||
public class OpenPGPSecretKeyBackupPassphraseConverter
|
||||
implements Converter<OpenPgpSecretKeyBackupPassphrase, String> {
|
||||
@Override
|
||||
public Class<OpenPgpSecretKeyBackupPassphrase> getMappedType() {
|
||||
return OpenPgpSecretKeyBackupPassphrase.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<String> getPersistedType() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Integer getPersistedSize() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToPersisted(OpenPgpSecretKeyBackupPassphrase value) {
|
||||
return value == null ? null : value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenPgpSecretKeyBackupPassphrase convertToMapped(Class<? extends OpenPgpSecretKeyBackupPassphrase> type, @Nullable String value) {
|
||||
return value == null ? null : new OpenPgpSecretKeyBackupPassphrase(value);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
package org.mercury_im.messenger.data.di;
|
||||
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
|
||||
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.EntityCapsRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.GroupChatRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.IkeyRecordRepository;
|
||||
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;
|
||||
|
@ -17,6 +19,7 @@ import org.mercury_im.messenger.data.repository.RxAccountRepository;
|
|||
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.RxIkeyRepository;
|
||||
import org.mercury_im.messenger.data.repository.RxMessageRepository;
|
||||
import org.mercury_im.messenger.data.repository.RxOpenPgpRepository;
|
||||
import org.mercury_im.messenger.data.repository.RxPeerRepository;
|
||||
|
@ -91,4 +94,10 @@ public class RepositoryModule {
|
|||
ReactiveEntityStore<Persistable> data, AccountRepository accountRepository) {
|
||||
return new RxOpenPgpRepository(data, accountRepository);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static IkeyRepository provideIkeyRepository(ReactiveEntityStore<Persistable> data) {
|
||||
return new RxIkeyRepository(data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package org.mercury_im.messenger.data.model;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
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.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.requery.CascadeAction;
|
||||
import io.requery.Column;
|
||||
import io.requery.Convert;
|
||||
import io.requery.Entity;
|
||||
import io.requery.Generated;
|
||||
import io.requery.Index;
|
||||
import io.requery.Key;
|
||||
import io.requery.OneToMany;
|
||||
import io.requery.Table;
|
||||
import io.requery.converter.UUIDConverter;
|
||||
|
||||
@Table(name = "ikey_record", uniqueIndexes = {"record_index"})
|
||||
@Entity
|
||||
public class AbstractIkeyRecordModel {
|
||||
|
||||
@Key
|
||||
@Generated
|
||||
UUID id;
|
||||
|
||||
@Column(name = "account", unique = true)
|
||||
@Index("record_index")
|
||||
@Convert(UUIDConverter.class)
|
||||
UUID accountId;
|
||||
|
||||
@Column(name = "jid", unique = true)
|
||||
@Index("record_index")
|
||||
@Convert(EntityBareJidConverter.class)
|
||||
EntityBareJid jid;
|
||||
|
||||
@Column(name = "\"timestamp\"")
|
||||
Date timestamp;
|
||||
|
||||
@OneToMany(mappedBy = "record", cascade = {CascadeAction.DELETE, CascadeAction.SAVE})
|
||||
@Column
|
||||
List<IkeySubordinateModel> subordinates;
|
||||
|
||||
@Column(name = "superordinate")
|
||||
PGPPublicKeyRing superordinate;
|
||||
|
||||
@Column(name = "fingerprint")
|
||||
@Convert(OpenPgpV4FingerprintConverter.class)
|
||||
OpenPgpV4Fingerprint fingerprint;
|
||||
|
||||
@Column(name = "trusted")
|
||||
boolean trusted;
|
||||
|
||||
@Column(name = "proof")
|
||||
String proof;
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package org.mercury_im.messenger.data.model;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.mercury_im.messenger.data.converter.Base64PGPSecretKeyRingConverter;
|
||||
import org.mercury_im.messenger.data.converter.OpenPGPSecretKeyBackupPassphraseConverter;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
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;
|
||||
|
||||
@Table(name = "ikey_sec_key")
|
||||
@Entity
|
||||
public class AbstractIkeySecretKeyModel {
|
||||
|
||||
@Key
|
||||
@Convert(UUIDConverter.class)
|
||||
@Column(name = "account")
|
||||
UUID accountId;
|
||||
|
||||
@Column(name = "fingerprint")
|
||||
OpenPgpV4Fingerprint fingerprint;
|
||||
|
||||
@Column(name = "backup_passphrase")
|
||||
@Convert(OpenPGPSecretKeyBackupPassphraseConverter.class)
|
||||
OpenPgpSecretKeyBackupPassphrase backupPassphrase;
|
||||
|
||||
@Column(name = "key")
|
||||
@Convert(Base64PGPSecretKeyRingConverter.class)
|
||||
PGPSecretKeyRing key;
|
||||
|
||||
@Column(name = "trusted")
|
||||
boolean trusted;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package org.mercury_im.messenger.data.model;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.requery.Column;
|
||||
import io.requery.Convert;
|
||||
import io.requery.Entity;
|
||||
import io.requery.Generated;
|
||||
import io.requery.Key;
|
||||
import io.requery.ManyToOne;
|
||||
import io.requery.Table;
|
||||
import io.requery.converter.URIConverter;
|
||||
|
||||
@Table(name = "ikey_subordinates")
|
||||
@Entity
|
||||
public class AbstractIkeySubordinateModel {
|
||||
|
||||
@Key
|
||||
@Generated
|
||||
UUID id;
|
||||
|
||||
@Column(name = "type")
|
||||
String type;
|
||||
|
||||
@Column(name = "uri")
|
||||
@Convert(URIConverter.class)
|
||||
URI uri;
|
||||
|
||||
@Column(name = "fpr")
|
||||
String fpr;
|
||||
|
||||
@ManyToOne
|
||||
AbstractIkeyRecordModel record;
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
package org.mercury_im.messenger.data.repository;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.jivesoftware.smackx.ikey.element.IkeyElement;
|
||||
import org.jivesoftware.smackx.ikey.element.ProofElement;
|
||||
import org.jivesoftware.smackx.ikey.element.SignedElement;
|
||||
import org.jivesoftware.smackx.ikey.element.SubordinateElement;
|
||||
import org.jivesoftware.smackx.ikey.element.SubordinateListElement;
|
||||
import org.jivesoftware.smackx.ikey.element.SuperordinateElement;
|
||||
import org.jivesoftware.smackx.ikey.mechanism.IkeyType;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.core.crypto.ikey.IkeyRepository;
|
||||
import org.mercury_im.messenger.data.model.IkeyRecordModel;
|
||||
import org.mercury_im.messenger.data.model.IkeySecretKeyModel;
|
||||
import org.mercury_im.messenger.data.model.IkeySubordinateModel;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Single;
|
||||
import io.requery.Persistable;
|
||||
import io.requery.reactivex.ReactiveEntityStore;
|
||||
|
||||
public class RxIkeyRepository implements IkeyRepository {
|
||||
|
||||
private final ReactiveEntityStore<Persistable> data;
|
||||
|
||||
public RxIkeyRepository(ReactiveEntityStore<Persistable> data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maybe<PGPSecretKeyRing> loadSecretKey(UUID accountId) {
|
||||
return data.select(IkeySecretKeyModel.class)
|
||||
.where(IkeySecretKeyModel.ACCOUNT_ID.eq(accountId))
|
||||
.get()
|
||||
.maybe()
|
||||
.map(IkeySecretKeyModel::getKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable storeSecretKey(UUID accountId, PGPSecretKeyRing secretKey) {
|
||||
|
||||
return data.select(IkeySecretKeyModel.class)
|
||||
.where(IkeySecretKeyModel.ACCOUNT_ID.eq(accountId))
|
||||
.get().observable()
|
||||
.single(new IkeySecretKeyModel())
|
||||
.map(m -> {
|
||||
m.setAccountId(accountId);
|
||||
m.setKey(secretKey);
|
||||
m.setFingerprint(new OpenPgpV4Fingerprint(secretKey.getPublicKey()));
|
||||
m.setBackupPassphrase(m.getBackupPassphrase());
|
||||
m.setTrusted(m.isTrusted());
|
||||
return m;
|
||||
})
|
||||
.flatMap(data::upsert)
|
||||
.ignoreElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<Integer> deleteSecretKey(UUID accountId) {
|
||||
return data.delete().from(IkeySecretKeyModel.class)
|
||||
.where(IkeySecretKeyModel.ACCOUNT_ID.eq(accountId))
|
||||
.get()
|
||||
.single();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<OpenPgpSecretKeyBackupPassphrase> loadBackupPassphrase(UUID accountID) {
|
||||
return data.select(IkeySecretKeyModel.class)
|
||||
.where(IkeySecretKeyModel.ACCOUNT_ID.eq(accountID))
|
||||
.get()
|
||||
.observable()
|
||||
.singleOrError()
|
||||
.map(IkeySecretKeyModel::getBackupPassphrase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable storeBackupPassphrase(UUID accountId, OpenPgpSecretKeyBackupPassphrase passphrase) {
|
||||
return data.select(IkeySecretKeyModel.class)
|
||||
.where(IkeySecretKeyModel.ACCOUNT_ID.eq(accountId))
|
||||
.get().observable()
|
||||
.single(new IkeySecretKeyModel())
|
||||
.map(m -> {
|
||||
m.setAccountId(accountId);
|
||||
m.setBackupPassphrase(m.getBackupPassphrase());
|
||||
return m;
|
||||
})
|
||||
.flatMap(data::upsert)
|
||||
.ignoreElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maybe<IkeyElement> loadRecord(UUID accountId, EntityBareJid jid) {
|
||||
return data.select(IkeyRecordModel.class)
|
||||
.where(IkeyRecordModel.ACCOUNT_ID.eq(accountId).and(IkeyRecordModel.JID.eq(jid)))
|
||||
.get().maybe()
|
||||
.map(m -> {
|
||||
SuperordinateElement superordinateElement = new SuperordinateElement(m.getSuperordinate().getEncoded());
|
||||
List<SubordinateElement> subList = new ArrayList<>();
|
||||
for (IkeySubordinateModel s : m.getSubordinates()) {
|
||||
subList.add(new SubordinateElement(s.getType(), s.getUri(), s.getFpr()));
|
||||
}
|
||||
SubordinateListElement subs = new SubordinateListElement(m.getJid(), m.getTimestamp(), subList);
|
||||
SignedElement signedElement = new SignedElement(subs.toBase64EncodedString());
|
||||
IkeyElement ikeyElement = new IkeyElement(IkeyType.OX, superordinateElement, signedElement, new ProofElement(m.getProof()));
|
||||
return ikeyElement;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable storeRecord(UUID accountId, EntityBareJid jid, IkeyElement record) {
|
||||
assert jid.equals(record.getSignedElement().getChildElement().getJid());
|
||||
return data.select(IkeyRecordModel.class)
|
||||
.where(IkeyRecordModel.ACCOUNT_ID.eq(accountId).and(IkeyRecordModel.JID.eq(jid)))
|
||||
.get().observable()
|
||||
.single(new IkeyRecordModel())
|
||||
.map(m -> {
|
||||
m.setAccountId(accountId);
|
||||
m.setJid(jid);
|
||||
m.setProof(record.getProof().getBase64Signature());
|
||||
m.setFingerprint(new OpenPgpV4Fingerprint(record.getSuperordinate().getPubKeyBytes()));
|
||||
m.setSuperordinate(PGPainless.readKeyRing().publicKeyRing(record.getSuperordinate().getPubKeyBytes()));
|
||||
m.setTimestamp(record.getSignedElement().getChildElement().getTimestamp());
|
||||
|
||||
for (SubordinateElement s : record.getSignedElement().getChildElement().getSubordinates()) {
|
||||
IkeySubordinateModel sm = new IkeySubordinateModel();
|
||||
sm.setRecord(m);
|
||||
sm.setFpr(s.getFingerprint());
|
||||
sm.setType(s.getType());
|
||||
sm.setUri(s.getUri());
|
||||
m.getSubordinates().add(sm);
|
||||
}
|
||||
|
||||
return m;
|
||||
})
|
||||
.flatMap(data::upsert)
|
||||
.ignoreElement();
|
||||
}
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
package org.mercury_im.messenger.data.mapping;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mercury_im.messenger.data.di.component.DaggerMappingTestComponent;
|
||||
import org.mercury_im.messenger.data.model.AccountModel;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
public class AccountMappingTest {
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package org.mercury_im.messenger.data.mapping;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mercury_im.messenger.data.di.component.DaggerMappingTestComponent;
|
||||
import org.mercury_im.messenger.data.model.EntityCapsModel;
|
||||
import org.mercury_im.messenger.entity.caps.EntityCapsRecord;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class EntityCapsMappingTest {
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package org.mercury_im.messenger.data.mapping;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mercury_im.messenger.data.converter.Base64PGPPublicKeyRingConverter;
|
||||
import org.mercury_im.messenger.data.converter.Base64PGPSecretKeyRingConverter;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.key.collection.PGPKeyRing;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class OpenPgpKeyConverterTest {
|
||||
|
||||
private static PGPKeyRing keyRing;
|
||||
|
||||
@BeforeAll
|
||||
public static void before() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException {
|
||||
keyRing = PGPainless.generateKeyRing().simpleEcKeyRing("xmpp:alice@wonderland.lit");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPublicKeyConverter() throws IOException {
|
||||
Base64PGPPublicKeyRingConverter converter = new Base64PGPPublicKeyRingConverter();
|
||||
String expected = Base64.toBase64String(keyRing.getPublicKeys().getEncoded());
|
||||
|
||||
String base64 = converter.convertToPersisted(keyRing.getPublicKeys());
|
||||
assertEquals(expected, base64);
|
||||
|
||||
PGPPublicKeyRing pub = converter.convertToMapped(PGPPublicKeyRing.class, base64);
|
||||
assertEquals(keyRing.getPublicKeys().getPublicKey().getKeyID(), pub.getPublicKey().getKeyID());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecretKeyConverter() throws IOException {
|
||||
Base64PGPSecretKeyRingConverter converter = new Base64PGPSecretKeyRingConverter();
|
||||
String expected = Base64.toBase64String(keyRing.getSecretKeys().getEncoded());
|
||||
|
||||
String base64 = converter.convertToPersisted(keyRing.getSecretKeys());
|
||||
assertEquals(expected, base64);
|
||||
|
||||
PGPSecretKeyRing sec = converter.convertToMapped(PGPSecretKeyRing.class, base64);
|
||||
assertEquals(keyRing.getSecretKeys().getPublicKey().getKeyID(), sec.getPublicKey().getKeyID());
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package org.mercury_im.messenger.data.mapping;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mercury_im.messenger.data.di.component.DaggerMappingTestComponent;
|
||||
import org.mercury_im.messenger.data.model.AccountModel;
|
||||
import org.mercury_im.messenger.data.model.PeerModel;
|
||||
|
@ -9,7 +9,7 @@ import org.mercury_im.messenger.entity.contact.SubscriptionDirection;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class PeerMappingTest {
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package org.mercury_im.messenger.data.repository;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mercury_im.messenger.data.di.component.DaggerInMemoryDatabaseComponent;
|
||||
import org.mercury_im.messenger.data.di.component.InMemoryDatabaseComponent;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
|
@ -16,6 +16,8 @@ import io.reactivex.disposables.CompositeDisposable;
|
|||
import io.requery.Persistable;
|
||||
import io.requery.reactivex.ReactiveEntityStore;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
public class AccountRepositoryTest {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(AccountRepositoryTest.class.getName());
|
||||
|
@ -119,13 +121,14 @@ public class AccountRepositoryTest {
|
|||
d.dispose();
|
||||
}
|
||||
|
||||
@Test(expected = NoSuchElementException.class)
|
||||
@Test
|
||||
public void updateMissingEntityFails() {
|
||||
Account missingAccount = new Account();
|
||||
missingAccount.setAddress("this@account.is.missing");
|
||||
missingAccount.setPassword("inTheDatabase");
|
||||
|
||||
accountRepository.updateAccount(missingAccount)
|
||||
.blockingGet();
|
||||
assertThrows(NoSuchElementException.class, () ->
|
||||
accountRepository.updateAccount(missingAccount)
|
||||
.blockingGet());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,12 +65,16 @@ public final class IkeyManager extends Manager {
|
|||
|
||||
public void startListeners() {
|
||||
PepManager.getInstanceFor(connection())
|
||||
.addPepEventListener(IkeyConstants.SUBORDINATES_NODE, IkeyElement.class, pepEventListener);
|
||||
.addPepEventListener(IkeyConstants.SUBORDINATES_NODE, IkeyElement.class, ikeyPepEventListener);
|
||||
}
|
||||
|
||||
public void stopListeners() {
|
||||
PepManager.getInstanceFor(connection())
|
||||
.removePepEventListener(pepEventListener);
|
||||
.removePepEventListener(ikeyPepEventListener);
|
||||
}
|
||||
|
||||
public void setStore(IkeyStore store) {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
public IkeyElement createIkeyElement(IkeySignatureCreationMechanism mechanism,
|
||||
|
@ -136,6 +140,15 @@ public final class IkeyManager extends Manager {
|
|||
store.storeIkeyRecord(from, element);
|
||||
}
|
||||
|
||||
public IkeyElement getIkeyElementOf(EntityBareJid from) throws IOException, InterruptedException, PubSubException.NotALeafNodeException, SmackException.NoResponseException, SmackException.NotConnectedException, XMPPException.XMPPErrorException, PubSubException.NotAPubSubNodeException {
|
||||
IkeyElement stored = store.loadIkeyRecord(from);
|
||||
if (stored == null) {
|
||||
stored = fetchIkeyElementOf(from);
|
||||
store.storeIkeyRecord(from, stored);
|
||||
}
|
||||
return stored;
|
||||
}
|
||||
|
||||
private boolean verifyIkeyElement(EntityBareJid from, IkeyElement element)
|
||||
throws IOException, UnsupportedSignatureAlgorithmException {
|
||||
IkeySignatureVerificationMechanism verificationMechanism = getSignatureVerificationMechanismFor(element);
|
||||
|
@ -174,7 +187,7 @@ public final class IkeyManager extends Manager {
|
|||
}
|
||||
|
||||
@SuppressWarnings("UnnecessaryAnonymousClass")
|
||||
private final PepEventListener<IkeyElement> pepEventListener = new PepEventListener<IkeyElement>() {
|
||||
private final PepEventListener<IkeyElement> ikeyPepEventListener = new PepEventListener<IkeyElement>() {
|
||||
@Override
|
||||
public void onPepEvent(EntityBareJid from, IkeyElement event, String id, Message carrierMessage) {
|
||||
try {
|
||||
|
|
|
@ -13,17 +13,24 @@ import java.net.URI;
|
|||
public class SubordinateElement implements NamedElement {
|
||||
|
||||
public static final String ELEMENT = "sub";
|
||||
public static final String ATTR_TYPE = "type";
|
||||
public static final String ATTR_SUB_URI = "uri";
|
||||
public static final String ATTR_SUB_FINGERPRINT = "fpr";
|
||||
|
||||
private final String type;
|
||||
private final URI subUri;
|
||||
private final String subFingerprint;
|
||||
|
||||
public SubordinateElement(URI subKeyItemUri, String subKeyFingerprint) {
|
||||
public SubordinateElement(String type, URI subKeyItemUri, String subKeyFingerprint) {
|
||||
this.type = type;
|
||||
this.subUri = Objects.requireNonNull(subKeyItemUri, "uri MUST NOT be null nor empty.");
|
||||
this.subFingerprint = StringUtils.requireNotNullNorEmpty(subKeyFingerprint, "fpr MUST NOT be null nor empty.");
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public URI getUri() {
|
||||
return subUri;
|
||||
}
|
||||
|
@ -40,6 +47,7 @@ public class SubordinateElement implements NamedElement {
|
|||
@Override
|
||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||
return new XmlStringBuilder(this)
|
||||
.attribute(ATTR_TYPE, getType())
|
||||
.attribute(ATTR_SUB_URI, getUri().toString())
|
||||
.attribute(ATTR_SUB_FINGERPRINT, getFingerprint())
|
||||
.closeEmptyElement();
|
||||
|
@ -49,6 +57,7 @@ public class SubordinateElement implements NamedElement {
|
|||
public int hashCode() {
|
||||
return HashCode.builder()
|
||||
.append(getElementName())
|
||||
.append(getType())
|
||||
.append(getFingerprint())
|
||||
.append(getUri())
|
||||
.build();
|
||||
|
@ -58,6 +67,7 @@ public class SubordinateElement implements NamedElement {
|
|||
public boolean equals(Object other) {
|
||||
return EqualsUtil.equals(this, other, (e, o) -> e
|
||||
.append(getElementName(), o.getElementName())
|
||||
.append(getType(), o.getType())
|
||||
.append(getFingerprint(), o.getFingerprint())
|
||||
.append(getUri(), o.getUri()));
|
||||
}
|
||||
|
|
|
@ -30,10 +30,11 @@ public class SubordinateListElementProvider extends ExtensionElementProvider<Sub
|
|||
switch (parser.nextTag()) {
|
||||
case START_ELEMENT:
|
||||
if (SubordinateElement.ELEMENT.equals(parser.getName())) {
|
||||
String type = ParserUtils.getRequiredAttribute(parser, SubordinateElement.ATTR_TYPE);
|
||||
String uriString = ParserUtils.getRequiredAttribute(parser, SubordinateElement.ATTR_SUB_URI);
|
||||
URI uri = URI.create(uriString);
|
||||
String fingerprint = ParserUtils.getRequiredAttribute(parser, SubordinateElement.ATTR_SUB_FINGERPRINT);
|
||||
subordinates.add(new SubordinateElement(uri, fingerprint));
|
||||
subordinates.add(new SubordinateElement(type, uri, fingerprint));
|
||||
}
|
||||
break;
|
||||
case END_ELEMENT:
|
||||
|
|
|
@ -18,6 +18,8 @@ import org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil;
|
|||
import org.jivesoftware.smackx.ox.util.SecretKeyBackupHelper;
|
||||
import org.jivesoftware.smackx.pep.PepManager;
|
||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||
import org.mercury_im.messenger.core.crypto.OpenPgpSecretKeyBackupPassphraseGenerator;
|
||||
import org.mercury_im.messenger.core.crypto.SecureRandomSecretKeyBackupPassphraseGenerator;
|
||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -55,6 +57,18 @@ public final class OxIkeyManager extends Manager {
|
|||
return OpenPgpPubSubUtil.fetchSecretKey(PepManager.getInstanceFor(connection()), SUPERORDINATE_NODE);
|
||||
}
|
||||
|
||||
public OpenPgpSecretKeyBackupPassphrase depositSecretIdentityKey(PGPSecretKeyRing secretKey)
|
||||
throws PGPException, InterruptedException, SmackException.NoResponseException,
|
||||
SmackException.NotConnectedException, SmackException.FeatureNotSupportedException,
|
||||
XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException, IOException {
|
||||
OpenPgpSecretKeyBackupPassphraseGenerator passphraseGenerator = new SecureRandomSecretKeyBackupPassphraseGenerator();
|
||||
OpenPgpSecretKeyBackupPassphrase passphrase = passphraseGenerator.generateBackupPassphrase();
|
||||
|
||||
depositSecretIdentityKey(secretKey, passphrase);
|
||||
|
||||
return passphrase;
|
||||
}
|
||||
|
||||
public void depositSecretIdentityKey(PGPSecretKeyRing secretKey, OpenPgpSecretKeyBackupPassphrase passphrase)
|
||||
throws InterruptedException, SmackException.NoResponseException,
|
||||
SmackException.NotConnectedException, SmackException.FeatureNotSupportedException,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package org.jivesoftware.smackx.ikey_ox;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
|
||||
public interface OxIkeyStore {
|
||||
|
||||
PGPSecretKeyRing loadSecretKey();
|
||||
|
||||
void storeSecretKey(PGPSecretKeyRing secretKey);
|
||||
|
||||
OpenPgpSecretKeyBackupPassphrase loadBackupPassphrase();
|
||||
|
||||
void storeBackupPassphrase(OpenPgpSecretKeyBackupPassphrase passphrase);
|
||||
}
|
|
@ -3,6 +3,7 @@ package org.jivesoftware.smackx.ikey_utils;
|
|||
import org.jivesoftware.smackx.ikey.element.SubordinateElement;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
||||
import org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil;
|
||||
import org.jivesoftware.smackx.pubsub.PubSubUri;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
|
@ -18,20 +19,20 @@ public class IkeySubordinateElementCreator {
|
|||
String node = OpenPgpPubSubUtil.PEP_NODE_PUBLIC_KEY(fingerprint);
|
||||
PubSubUri pubSubUri = new PubSubUri(pubSubService, node, itemId, null);
|
||||
URI uri = new URI(pubSubUri.toString());
|
||||
return new SubordinateElement(uri, fingerprint.toString());
|
||||
return new SubordinateElement(OpenPgpElement.NAMESPACE, uri, fingerprint.toString());
|
||||
}
|
||||
|
||||
public static SubordinateElement createOmemoSubordinateElement(BareJid pubSubService, OmemoDevice device, OmemoFingerprint fingerprint)
|
||||
throws URISyntaxException {
|
||||
PubSubUri pubSubUri = new PubSubUri(pubSubService, "urn:xmpp:omemo:1:bundles", Integer.toString(device.getDeviceId()), null);
|
||||
URI uri = new URI(pubSubUri.toString());
|
||||
return new SubordinateElement(uri, fingerprint.toString());
|
||||
return new SubordinateElement("urn:xmpp:omemo:1", uri, fingerprint.toString());
|
||||
}
|
||||
|
||||
public static SubordinateElement createSiacsOmemoSubordinateElement(BareJid pubSubService, OmemoDevice device, OmemoFingerprint fingerprint)
|
||||
throws URISyntaxException {
|
||||
PubSubUri pubSubUri = new PubSubUri(pubSubService, device.getBundleNodeName(), null, null);
|
||||
URI uri = new URI(pubSubUri.toString());
|
||||
return new SubordinateElement(uri, fingerprint.toString());
|
||||
return new SubordinateElement("eu.siacs.conversations.axolotl", uri, fingerprint.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package org.mercury_im.messenger.core.crypto.ikey;
|
||||
|
||||
import org.mercury_im.messenger.core.data.repository.IkeyKeyRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.IkeyRecordRepository;
|
||||
|
||||
public interface IkeyRepository extends IkeyKeyRepository, IkeyRecordRepository {
|
||||
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package org.mercury_im.messenger.core.crypto.ikey;
|
||||
|
||||
public class IkeySignatureProvider {
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package org.mercury_im.messenger.core.crypto.ikey;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.jivesoftware.smackx.ikey.element.IkeyElement;
|
||||
import org.jivesoftware.smackx.ikey.record.IkeyStore;
|
||||
import org.jivesoftware.smackx.ikey_ox.OxIkeyStore;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.core.SchedulersFacade;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Adapter that serves as a {@link IkeyStore} and {@link OxIkeyStore} implementation
|
||||
* wrapping a {@link IkeyRepository}.
|
||||
*/
|
||||
public class IkeyStoreAdapter implements IkeyStore, OxIkeyStore {
|
||||
|
||||
private final UUID accountId;
|
||||
private final IkeyRepository repository;
|
||||
private final SchedulersFacade schedulers;
|
||||
|
||||
public IkeyStoreAdapter(UUID accountId, IkeyRepository repository, SchedulersFacade schedulers) {
|
||||
this.accountId = accountId;
|
||||
this.repository = repository;
|
||||
this.schedulers = schedulers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IkeyElement loadIkeyRecord(EntityBareJid jid) throws IOException {
|
||||
return repository.loadRecord(accountId, jid)
|
||||
.compose(schedulers.executeUiSafeMaybe())
|
||||
.blockingGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeIkeyRecord(EntityBareJid jid, IkeyElement record) throws IOException {
|
||||
repository.storeRecord(accountId, jid, record)
|
||||
.compose(schedulers.executeUiSafeCompletable())
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PGPSecretKeyRing loadSecretKey() {
|
||||
return repository.loadSecretKey(accountId)
|
||||
.compose(schedulers.executeUiSafeMaybe())
|
||||
.blockingGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeSecretKey(PGPSecretKeyRing secretKey) {
|
||||
repository.storeSecretKey(accountId, secretKey)
|
||||
.compose(schedulers.executeUiSafeCompletable())
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenPgpSecretKeyBackupPassphrase loadBackupPassphrase() {
|
||||
return repository.loadBackupPassphrase(accountId)
|
||||
.compose(schedulers.executeUiSafeSingle())
|
||||
.blockingGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeBackupPassphrase(OpenPgpSecretKeyBackupPassphrase passphrase) {
|
||||
repository.storeBackupPassphrase(accountId, passphrase)
|
||||
.compose(schedulers.executeUiSafeCompletable())
|
||||
.subscribe();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package org.mercury_im.messenger.core.crypto.ikey;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smackx.ikey.IkeyManager;
|
||||
import org.jivesoftware.smackx.ikey_ox.OxIkeyManager;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||
import org.mercury_im.messenger.core.SchedulersFacade;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.key.collection.PGPKeyRing;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class MercuryIkeyManager {
|
||||
|
||||
private final IkeyRepository ikeyRepository;
|
||||
private final SchedulersFacade schedulers;
|
||||
|
||||
@Inject
|
||||
public MercuryIkeyManager(IkeyRepository ikeyRepository, SchedulersFacade schedulers) {
|
||||
this.ikeyRepository = ikeyRepository;
|
||||
this.schedulers = schedulers;
|
||||
}
|
||||
|
||||
public void initFor(MercuryConnection connection)
|
||||
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException,
|
||||
InterruptedException, SmackException.NoResponseException,
|
||||
SmackException.NotConnectedException, SmackException.FeatureNotSupportedException,
|
||||
XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException, IOException {
|
||||
IkeyManager ikeyManager = IkeyManager.getInstanceFor(connection.getConnection());
|
||||
|
||||
// bind repo to store
|
||||
IkeyStoreAdapter store = new IkeyStoreAdapter(connection.getAccountId(), ikeyRepository, schedulers);
|
||||
ikeyManager.setStore(store);
|
||||
|
||||
PGPSecretKeyRing ikey = store.loadSecretKey();
|
||||
if (ikey == null) {
|
||||
PGPKeyRing keyRing = PGPainless.generateKeyRing().simpleEcKeyRing("xmpp:" + connection.getAccount().getAddress());
|
||||
store.storeSecretKey(keyRing.getSecretKeys());
|
||||
}
|
||||
|
||||
OxIkeyManager oxIkeyManager = OxIkeyManager.getInstanceFor(connection.getConnection());
|
||||
OpenPgpSecretKeyBackupPassphrase passphrase = oxIkeyManager.depositSecretIdentityKey(ikey);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package org.mercury_im.messenger.core.data.repository;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpSecretKeyBackupPassphrase;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Single;
|
||||
|
||||
public interface IkeyKeyRepository {
|
||||
|
||||
default Maybe<PGPSecretKeyRing> loadSecretKey(Account account) {
|
||||
return loadSecretKey(account.getId());
|
||||
}
|
||||
|
||||
Maybe<PGPSecretKeyRing> loadSecretKey(UUID accountId);
|
||||
|
||||
default Completable storeSecretKey(Account account, PGPSecretKeyRing secretKey) {
|
||||
return storeSecretKey(account.getId(), secretKey);
|
||||
}
|
||||
|
||||
Completable storeSecretKey(UUID accountId, PGPSecretKeyRing secretKey);
|
||||
|
||||
default Single<Integer> deleteSecretKey(Account account) {
|
||||
return deleteSecretKey(account.getId());
|
||||
}
|
||||
|
||||
Single<Integer> deleteSecretKey(UUID accountId);
|
||||
|
||||
default Single<OpenPgpSecretKeyBackupPassphrase> loadBackupPassphrase(Account account) {
|
||||
return loadBackupPassphrase(account.getId());
|
||||
}
|
||||
|
||||
Single<OpenPgpSecretKeyBackupPassphrase> loadBackupPassphrase(UUID accountID);
|
||||
|
||||
default Completable storeBackupPassphrase(Account account, OpenPgpSecretKeyBackupPassphrase passphrase) {
|
||||
return storeBackupPassphrase(account.getId(), passphrase);
|
||||
}
|
||||
|
||||
Completable storeBackupPassphrase(UUID accountID, OpenPgpSecretKeyBackupPassphrase passphrase);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.mercury_im.messenger.core.data.repository;
|
||||
|
||||
import org.jivesoftware.smackx.ikey.element.IkeyElement;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Maybe;
|
||||
|
||||
public interface IkeyRecordRepository {
|
||||
|
||||
default Maybe<IkeyElement> loadRecord(Account account, EntityBareJid jid) {
|
||||
return loadRecord(account.getId(), jid);
|
||||
}
|
||||
|
||||
Maybe<IkeyElement> loadRecord(UUID accountId, EntityBareJid jid);
|
||||
|
||||
default Completable storeRecord(Account account, EntityBareJid jid, IkeyElement record) {
|
||||
return storeRecord(account.getId(), jid, record);
|
||||
}
|
||||
|
||||
Completable storeRecord(UUID accountId, EntityBareJid jid, IkeyElement record);
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package org.mercury_im.messenger.core.viewmodel.ikey;
|
||||
|
||||
import org.jivesoftware.smackx.ikey.IkeyManager;
|
||||
import org.jivesoftware.smackx.ikey_ox.OxIkeyManager;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
|
||||
import org.mercury_im.messenger.core.viewmodel.MercuryViewModel;
|
||||
import org.mercury_im.messenger.entity.Account;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class IkeySecretKeyBackupCreationViewModel implements MercuryViewModel {
|
||||
|
||||
private final MercuryConnectionManager connectionManager;
|
||||
|
||||
@Inject
|
||||
public IkeySecretKeyBackupCreationViewModel(MercuryConnectionManager connectionManager) {
|
||||
this.connectionManager = connectionManager;
|
||||
}
|
||||
|
||||
public void createIkeySecretKeyBackup(Account account) {
|
||||
MercuryConnection connection = connectionManager.getConnection(account);
|
||||
IkeyManager ikeyManager = IkeyManager.getInstanceFor(connection.getConnection());
|
||||
OxIkeyManager oxIkeyManager = OxIkeyManager.getInstanceFor(connection.getConnection());
|
||||
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue