OX key experiments

This commit is contained in:
Paul Schaub 2020-06-26 16:00:47 +02:00
parent d8076ddcac
commit 89695c617f
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
8 changed files with 189 additions and 12 deletions

View File

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

View File

@ -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() {
@ -156,7 +171,7 @@ public class ContactDetailViewModel extends ViewModel {
InterruptedException, SmackException.NoResponseException {
if (!newName.trim().isEmpty()) {
RosterEntry entry = roster.getEntry(JidCreate.entityBareFromOrThrowUnchecked(getContactAddress().getValue()));
entry.setName(newName);
entry.setName(newName);
}
}
@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

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

View File

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

View File

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

View File

@ -82,7 +82,7 @@ public class MercuryConnectionManager {
this.cryptoManager = cryptoManager;
this.schedulers = schedulers;
EntityCapsManager.setPersistentCache(entityCapsStore);
//EntityCapsManager.setPersistentCache(entityCapsStore);
start();
}