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.core.util.Optional; 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.Arrays; import java.util.List; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import io.reactivex.Completable; import io.reactivex.Observable; import io.reactivex.Single; import io.requery.Persistable; import io.requery.query.ResultDelegate; import io.requery.reactivex.ReactiveEntityStore; public class RxIkeyRepository implements IkeyRepository { private static final Logger LOGGER = Logger.getLogger(RxIkeyRepository.class.getName()); private final ReactiveEntityStore data; public RxIkeyRepository(ReactiveEntityStore data) { this.data = data; } @Override public Observable> loadSecretKey(UUID accountId) { return data.select(IkeySecretKeyModel.class) .where(IkeySecretKeyModel.ACCOUNT_ID.eq(accountId)) .get() .observableResult() .map(ResultDelegate::toList) .doOnNext(l -> LOGGER.log(Level.INFO, "new Result: " + Arrays.toString(l.toArray()))) .map(l -> l.isEmpty() ? new Optional<>() : new Optional<>(l.get(0).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())); return m; }) .flatMap(data::upsert) .ignoreElement(); } @Override public Single deleteSecretKey(UUID accountId) { return data.delete().from(IkeySecretKeyModel.class) .where(IkeySecretKeyModel.ACCOUNT_ID.eq(accountId)) .get() .single(); } @Override public Single> loadBackupPassphrase(UUID accountID) { return data.select(IkeySecretKeyModel.class) .where(IkeySecretKeyModel.ACCOUNT_ID.eq(accountID)) .get() .observable() .map(m -> new Optional<>(m.getBackupPassphrase())) .single(new Optional<>()); } @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(passphrase); return m; }) .flatMap(data::upsert) .ignoreElement(); } @Override public Observable loadRecord(UUID accountId, EntityBareJid jid) { return data.select(IkeyRecordModel.class) .where(IkeyRecordModel.ACCOUNT_ID.eq(accountId).and(IkeyRecordModel.JID.eq(jid))) .get().observable() .map(m -> { SuperordinateElement superordinateElement = new SuperordinateElement(m.getSuperordinate().getEncoded()); List 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(); } }