Mercury-IM/data/src/main/java/org/mercury_im/messenger/data/repository/RxIkeyRepository.java

147 lines
6.2 KiB
Java

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();
}
}