OX key experiments
This commit is contained in:
parent
d8076ddcac
commit
89695c617f
|
@ -23,10 +23,13 @@ import com.google.android.material.chip.Chip;
|
|||
import com.google.android.material.chip.ChipGroup;
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
|
||||
|
||||
import org.jivesoftware.smackx.colors.ConsistentColor;
|
||||
import org.mercury_im.messenger.android.util.OpenPgpFingerprintColorizer;
|
||||
import org.mercury_im.messenger.core.Messenger;
|
||||
import org.mercury_im.messenger.R;
|
||||
import org.mercury_im.messenger.android.ui.chat.ChatActivity;
|
||||
import org.mercury_im.messenger.android.util.ColorUtil;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -65,6 +68,9 @@ public class ContactDetailFragment extends Fragment {
|
|||
@BindView(R.id.button_add_to_group)
|
||||
Button button;
|
||||
|
||||
@BindView(R.id.fingerprint)
|
||||
TextView fingerprint;
|
||||
|
||||
private ContactDetailViewModel viewModel;
|
||||
|
||||
@Nullable
|
||||
|
@ -145,6 +151,7 @@ public class ContactDetailFragment extends Fragment {
|
|||
viewModel.getContactPresenceStatus().observe(this, presenceText -> contactPresence.setText(presenceText));
|
||||
viewModel.getContactAccountAddress().observe(this, address -> contactAccount.setText(address));
|
||||
viewModel.getContactGroups().observe(this, this::setRosterGroups);
|
||||
viewModel.getContactFingerprints().observe(this, this::setFingerprints);
|
||||
}
|
||||
|
||||
private void setRosterGroups(List<String> groups) {
|
||||
|
@ -164,4 +171,11 @@ public class ContactDetailFragment extends Fragment {
|
|||
contactGroups.addView(chip);
|
||||
}
|
||||
}
|
||||
|
||||
private void setFingerprints(List<OpenPgpV4Fingerprint> fingerprints) {
|
||||
if (fingerprints.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
fingerprint.setText(OpenPgpFingerprintColorizer.formatOpenPgpV4Fingerprint(fingerprints.get(0)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,13 @@ import org.jxmpp.jid.Jid;
|
|||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.mercury_im.messenger.android.MercuryImApplication;
|
||||
import org.mercury_im.messenger.core.Messenger;
|
||||
import org.mercury_im.messenger.core.SchedulersFacade;
|
||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
||||
import org.mercury_im.messenger.core.data.repository.PeerRepository;
|
||||
import org.mercury_im.messenger.entity.contact.Peer;
|
||||
import org.mercury_im.messenger.android.ui.avatar.AvatarDrawable;
|
||||
import org.mercury_im.messenger.core.util.CombinedPresenceListener;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -41,6 +44,12 @@ public class ContactDetailViewModel extends ViewModel {
|
|||
@Inject
|
||||
PeerRepository peerRepository;
|
||||
|
||||
@Inject
|
||||
OpenPgpRepository openPgpRepository;
|
||||
|
||||
@Inject
|
||||
SchedulersFacade schedulers;
|
||||
|
||||
@Inject
|
||||
Messenger messenger;
|
||||
|
||||
|
@ -52,6 +61,7 @@ public class ContactDetailViewModel extends ViewModel {
|
|||
private MutableLiveData<String> contactName = new MutableLiveData<>("Alice Wonderland");
|
||||
private MutableLiveData<String> contactAccountAddress = new MutableLiveData<>("mad@hatter.lit");
|
||||
private MutableLiveData<List<String>> contactGroups = new MutableLiveData<>(Collections.emptyList());
|
||||
private MutableLiveData<List<OpenPgpV4Fingerprint>> contactFingerprints = new MutableLiveData<>(Collections.singletonList(new OpenPgpV4Fingerprint("1357B01865B2503C18453D208CAC2A9678548E35")));
|
||||
|
||||
private Roster roster;
|
||||
|
||||
|
@ -69,8 +79,8 @@ public class ContactDetailViewModel extends ViewModel {
|
|||
contactAddress.setValue(peerAddress);
|
||||
contactAccountId.setValue(accountId);
|
||||
disposable.add(peerRepository.observePeerByAddress(accountId, peerAddress)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(schedulers.getIoScheduler())
|
||||
.observeOn(schedulers.getUiScheduler())
|
||||
.subscribe(peerOptional -> {
|
||||
if (!peerOptional.isPresent()) {
|
||||
return;
|
||||
|
@ -98,6 +108,11 @@ public class ContactDetailViewModel extends ViewModel {
|
|||
contactPresenceMode.postValue(presence.getMode());
|
||||
contactPresenceStatus.postValue(presence.getStatus());
|
||||
}
|
||||
|
||||
disposable.add(openPgpRepository.observeFingerprintsOf(accountId, peerAddress)
|
||||
.subscribeOn(schedulers.getIoScheduler())
|
||||
.observeOn(schedulers.getUiScheduler())
|
||||
.subscribe(list -> contactFingerprints.setValue(list)));
|
||||
}
|
||||
|
||||
public LiveData<String> getContactAddress() {
|
||||
|
@ -183,4 +198,8 @@ public class ContactDetailViewModel extends ViewModel {
|
|||
return Completable.fromAction(() -> roster.getGroup(group).removeEntry(roster.getEntry(JidCreate.entityBareFromOrThrowUnchecked(getContactAddress().getValue()))));
|
||||
|
||||
}
|
||||
|
||||
public LiveData<List<OpenPgpV4Fingerprint>> getContactFingerprints() {
|
||||
return contactFingerprints;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
package org.mercury_im.messenger.android.util;
|
||||
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
|
||||
import org.jivesoftware.smackx.colors.ConsistentColor;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
public class OpenPgpFingerprintColorizer {
|
||||
|
||||
/**
|
||||
* Split an OpenPGP fingerprint into 10 blocks of length 4.
|
||||
*
|
||||
* @param fingerprint fingerprint
|
||||
* @return blocks
|
||||
*/
|
||||
public static String[] getFingerprintBlocks(OpenPgpV4Fingerprint fingerprint) {
|
||||
String[] blocks = new String[10];
|
||||
for (int i = 0; i < 10; i++) {
|
||||
blocks[i] = fingerprint.subSequence(i*4, (i+1)*4).toString();
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate consistent colors for 10 blocks of length 4 of the fingerprint.
|
||||
*
|
||||
* @param blocks Array of 10 OpenPGP fingerprint blocks of length 4
|
||||
* @param consistentColorSettings settings for color generation
|
||||
* @return array of generated colors
|
||||
*/
|
||||
public static int[] getColorsForFingerprintBlocks(String[] blocks,
|
||||
ConsistentColor.ConsistentColorSettings consistentColorSettings) {
|
||||
int[] colors = new int[10];
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (consistentColorSettings != null) {
|
||||
colors[i] = ColorUtil.consistentColor(blocks[i], consistentColorSettings);
|
||||
} else {
|
||||
colors[i] = ColorUtil.consistentColor(blocks[i]);
|
||||
}
|
||||
}
|
||||
return colors;
|
||||
}
|
||||
|
||||
public static Spannable formatOpenPgpV4Fingerprint(OpenPgpV4Fingerprint fingerprint) {
|
||||
return formatOpenPgpV4Fingerprint(fingerprint, null);
|
||||
}
|
||||
|
||||
public static Spannable formatOpenPgpV4Fingerprint(OpenPgpV4Fingerprint fingerprint,
|
||||
ConsistentColor.ConsistentColorSettings settings) {
|
||||
String[] blocks = getFingerprintBlocks(fingerprint);
|
||||
int[] colors = getColorsForFingerprintBlocks(blocks, settings);
|
||||
|
||||
StringBuilder formattedFingerprint = new StringBuilder();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
formattedFingerprint.append(blocks[i]);
|
||||
if (i == 4) {
|
||||
formattedFingerprint.append('\n');
|
||||
continue;
|
||||
}
|
||||
if (i != 9) {
|
||||
formattedFingerprint.append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
Spannable spannable = new SpannableString(formattedFingerprint.toString());
|
||||
for (int i = 0; i < 10; i++) {
|
||||
spannable.setSpan(new ForegroundColorSpan(colors[i]), i*5, (i+1)*5-1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
|
||||
}
|
||||
return spannable;
|
||||
}
|
||||
}
|
|
@ -153,6 +153,35 @@
|
|||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:cardElevation="4dp"
|
||||
app:cardCornerRadius="6dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/fingerprint"
|
||||
tools:text="1357 B018 65B2 503C 1845\n3D20 8CAC 2A96 7854 8E35"
|
||||
android:textSize="20dp"
|
||||
android:typeface="monospace"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package org.mercury_im.messenger.data.repository;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
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;
|
||||
|
@ -13,17 +15,23 @@ import org.mercury_im.messenger.data.model.OpenPgpSecretKeyRing;
|
|||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.requery.Persistable;
|
||||
import io.requery.query.ResultDelegate;
|
||||
|
@ -31,6 +39,8 @@ import io.requery.reactivex.ReactiveEntityStore;
|
|||
|
||||
public class RxOpenPgpRepository implements OpenPgpRepository {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(RxOpenPgpRepository.class.getName());
|
||||
|
||||
private final ReactiveEntityStore<Persistable> data;
|
||||
|
||||
@Inject
|
||||
|
@ -46,7 +56,8 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
|||
keyRing.setOwner(owner);
|
||||
keyRing.setBytes(keys.getEncoded());
|
||||
return keyRing;
|
||||
}).flatMap(data::upsert).ignoreElement();
|
||||
}).flatMap(data::upsert).ignoreElement()
|
||||
.doOnComplete(() -> LOGGER.log(Level.INFO, "Successfully stored public keys of " + owner + " for account " + accountId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -64,7 +75,8 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
|||
return data.delete(OpenPgpPublicKeyRing.class)
|
||||
.where(OpenPgpPublicKeyRing.ACCOUNT_ID.eq(accountId))
|
||||
.and(OpenPgpPublicKeyRing.OWNER.eq(owner))
|
||||
.get().single();
|
||||
.get().single()
|
||||
.doOnSuccess(count -> LOGGER.log(Level.INFO, "Successfully deleted " + count + " public keys of " + owner + " for account " + accountId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,7 +87,8 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
|||
keyRing.setOwner(owner);
|
||||
keyRing.setBytes(keys.getEncoded());
|
||||
return keyRing;
|
||||
}).flatMap(data::upsert).ignoreElement();
|
||||
}).flatMap(data::upsert).ignoreElement()
|
||||
.doOnComplete(() -> LOGGER.log(Level.INFO, "Successfully stored secret keys of " + owner + " for account " + accountId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -93,7 +106,8 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
|||
return data.delete(OpenPgpSecretKeyRing.class)
|
||||
.where(OpenPgpSecretKeyRing.ACCOUNT_ID.eq(accountId))
|
||||
.and(OpenPgpSecretKeyRing.OWNER.eq(owner))
|
||||
.get().single();
|
||||
.get().single()
|
||||
.doOnSuccess(count -> LOGGER.log(Level.INFO, "Successfully deleted " + count + " secret keys of " + owner + " for account " + accountId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -121,7 +135,9 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
|||
entity.setModificationDate(entry.getValue());
|
||||
entities.add(entity);
|
||||
}
|
||||
return data.upsert(entities).ignoreElement();
|
||||
return data.upsert(entities).ignoreElement()
|
||||
.doOnComplete(() -> LOGGER.log(Level.INFO, "Successfully stored announced fingerprints of " +
|
||||
owner + " for account " + accountId + ": " + Arrays.toString(metadata.keySet().toArray())));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -165,6 +181,26 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
|||
return data.upsert(entity).ignoreElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<List<OpenPgpV4Fingerprint>> observeFingerprintsOf(UUID accountId, String peerAddress) {
|
||||
return data.select(OpenPgpPublicKeyRing.class)
|
||||
//.where(OpenPgpPublicKeyRing.ACCOUNT_ID.eq(accountId))
|
||||
.where(OpenPgpPublicKeyRing.OWNER.eq(JidCreate.entityBareFromOrThrowUnchecked(peerAddress)))
|
||||
.get().observableResult()
|
||||
.map(result -> {
|
||||
OpenPgpPublicKeyRing model = new ResultDelegate<>(result).firstOrNull();
|
||||
if (model == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<OpenPgpV4Fingerprint> fingerprints = new ArrayList<>();
|
||||
PGPPublicKeyRingCollection keys = PGPainless.readKeyRing().publicKeyRingCollection(model.getBytes());
|
||||
for (PGPPublicKeyRing key : keys) {
|
||||
fingerprints.add(new OpenPgpV4Fingerprint(key));
|
||||
}
|
||||
return fingerprints;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<Map<OpenPgpV4Fingerprint, Date>> loadPublicKeyFetchDates(UUID accountId, EntityBareJid owner) {
|
||||
return data.select(OpenPgpKeyFetchDate.class)
|
||||
|
@ -180,6 +216,7 @@ public class RxOpenPgpRepository implements OpenPgpRepository {
|
|||
map.put(date.getFingerprint(), date.getFetchDate());
|
||||
}
|
||||
return map;
|
||||
});
|
||||
})
|
||||
.doOnError(e -> Logger.getLogger(RxOpenPgpRepository.class.getName()).log(Level.SEVERE, "Error observing public keys", e));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,9 +81,10 @@ public class MercuryOpenPgpManager {
|
|||
}
|
||||
oxManager.announceSupportAndPublish();
|
||||
OXInstantMessagingManager oximManager = OXInstantMessagingManager.getInstanceFor(connection.getConnection());
|
||||
oximManager.addOxMessageListener(new MercuryMessageStore(connection.getAccount(), repositories.getPeerRepository(), repositories.getDirectChatRepository(), repositories.getMessageRepository(), schedulers));
|
||||
oximManager.addOxMessageListener(new MercuryMessageStore(connection.getAccount(),
|
||||
repositories.getPeerRepository(), repositories.getDirectChatRepository(),
|
||||
repositories.getMessageRepository(), schedulers));
|
||||
oximManager.announceSupportForOxInstantMessaging();
|
||||
oxManager.stopMetadataListener();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
|
@ -7,10 +7,12 @@ import org.jxmpp.jid.EntityBareJid;
|
|||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
public interface OpenPgpRepository {
|
||||
|
@ -38,4 +40,6 @@ public interface OpenPgpRepository {
|
|||
Single<OpenPgpTrustStore.Trust> loadTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint);
|
||||
|
||||
Completable storeTrust(UUID accountId, EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, OpenPgpTrustStore.Trust trust);
|
||||
|
||||
Observable<List<OpenPgpV4Fingerprint>> observeFingerprintsOf(UUID accountId, String peerAddress);
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ public class MercuryConnectionManager {
|
|||
this.cryptoManager = cryptoManager;
|
||||
this.schedulers = schedulers;
|
||||
|
||||
EntityCapsManager.setPersistentCache(entityCapsStore);
|
||||
//EntityCapsManager.setPersistentCache(entityCapsStore);
|
||||
start();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue