parse omemo fingerprints from uris

This commit is contained in:
Daniel Gultsch 2016-11-17 20:09:42 +01:00
parent 3f3b360eee
commit 7e2e42cb11
11 changed files with 172 additions and 44 deletions

View file

@ -98,6 +98,10 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return false; return false;
} }
public void preVerifyFingerprint(Contact contact, String fingerprint) {
axolotlStore.preVerifyFingerprint(contact.getAccount(), contact.getJid().toBareJid().toPreppedString(), fingerprint);
}
private static class AxolotlAddressMap<T> { private static class AxolotlAddressMap<T> {
protected Map<String, Map<Integer, T>> map; protected Map<String, Map<Integer, T>> map;
protected final Object MAP_LOCK = new Object(); protected final Object MAP_LOCK = new Object();
@ -200,7 +204,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
public void put(AxolotlAddress address, XmppAxolotlSession value) { public void put(AxolotlAddress address, XmppAxolotlSession value) {
super.put(address, value); super.put(address, value);
value.setNotFresh(); value.setNotFresh();
xmppConnectionService.syncRosterToDisk(account); xmppConnectionService.syncRosterToDisk(account); //TODO why?
} }
public void put(XmppAxolotlSession session) { public void put(XmppAxolotlSession session) {
@ -417,7 +421,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
} }
public void purgeKey(final String fingerprint) { public void purgeKey(final String fingerprint) {
axolotlStore.setFingerprintTrust(fingerprint.replaceAll("\\s", ""), FingerprintStatus.createCompromised()); axolotlStore.setFingerprintStatus(fingerprint.replaceAll("\\s", ""), FingerprintStatus.createCompromised());
} }
public void publishOwnDeviceIdIfNeeded() { public void publishOwnDeviceIdIfNeeded() {
@ -690,7 +694,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
} }
public void setFingerprintTrust(String fingerprint, FingerprintStatus status) { public void setFingerprintTrust(String fingerprint, FingerprintStatus status) {
axolotlStore.setFingerprintTrust(fingerprint, status); axolotlStore.setFingerprintStatus(fingerprint, status);
} }
private void verifySessionWithPEP(final XmppAxolotlSession session) { private void verifySessionWithPEP(final XmppAxolotlSession session) {
@ -749,14 +753,15 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
private void finishBuildingSessionsFromPEP(final AxolotlAddress address) { private void finishBuildingSessionsFromPEP(final AxolotlAddress address) {
AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), 0); AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toPreppedString(), 0);
if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING) Map<Integer, FetchStatus> own = fetchStatusMap.getAll(ownAddress);
&& !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) { Map<Integer, FetchStatus> remote = fetchStatusMap.getAll(address);
if (!own.containsValue(FetchStatus.PENDING) && !remote.containsValue(FetchStatus.PENDING)) {
FetchStatus report = null; FetchStatus report = null;
if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.SUCCESS_VERIFIED) if (own.containsValue(FetchStatus.SUCCESS) || remote.containsValue(FetchStatus.SUCCESS)) {
| fetchStatusMap.getAll(address).containsValue(FetchStatus.SUCCESS_VERIFIED)) { report = FetchStatus.SUCCESS;
} else if (own.containsValue(FetchStatus.SUCCESS_VERIFIED) || remote.containsValue(FetchStatus.SUCCESS_VERIFIED)) {
report = FetchStatus.SUCCESS_VERIFIED; report = FetchStatus.SUCCESS_VERIFIED;
} else if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.ERROR) } else if (own.containsValue(FetchStatus.ERROR) || remote.containsValue(FetchStatus.ERROR)) {
|| fetchStatusMap.getAll(address).containsValue(FetchStatus.ERROR)) {
report = FetchStatus.ERROR; report = FetchStatus.ERROR;
} }
mXmppConnectionService.keyStatusUpdated(report); mXmppConnectionService.keyStatusUpdated(report);
@ -812,7 +817,9 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
if (Config.X509_VERIFICATION) { if (Config.X509_VERIFICATION) {
verifySessionWithPEP(session); verifySessionWithPEP(session);
} else { } else {
fetchStatusMap.put(address, FetchStatus.SUCCESS); FingerprintStatus status = getFingerprintTrust(bundle.getIdentityKey().getFingerprint().replaceAll("\\s",""));
boolean verified = status != null && status.isVerified();
fetchStatusMap.put(address, verified ? FetchStatus.SUCCESS_VERIFIED : FetchStatus.SUCCESS);
finishBuildingSessionsFromPEP(address); finishBuildingSessionsFromPEP(address);
} }
} catch (UntrustedIdentityException | InvalidKeyException e) { } catch (UntrustedIdentityException | InvalidKeyException e) {

View file

@ -114,6 +114,20 @@ public class FingerprintStatus {
return status; return status;
} }
public FingerprintStatus toVerified() {
FingerprintStatus status = new FingerprintStatus();
status.active = active;
status.trust = Trust.VERIFIED;
return status;
}
public static FingerprintStatus createInactiveVerified() {
final FingerprintStatus status = new FingerprintStatus();
status.trust = Trust.VERIFIED;
status.active = false;
return status;
}
public enum Trust { public enum Trust {
COMPROMISED, COMPROMISED,
UNDECIDED, UNDECIDED,

View file

@ -187,7 +187,15 @@ public class SQLiteAxolotlStore implements AxolotlStore {
@Override @Override
public void saveIdentity(String name, IdentityKey identityKey) { public void saveIdentity(String name, IdentityKey identityKey) {
if (!mXmppConnectionService.databaseBackend.loadIdentityKeys(account, name).contains(identityKey)) { if (!mXmppConnectionService.databaseBackend.loadIdentityKeys(account, name).contains(identityKey)) {
mXmppConnectionService.databaseBackend.storeIdentityKey(account, name, identityKey); String fingerprint = identityKey.getFingerprint().replaceAll("\\s", "");
FingerprintStatus status = getFingerprintStatus(fingerprint);
if (status == null) {
status = FingerprintStatus.createActiveUndecided(); //default for new keys
} else {
status = status.toActive();
}
mXmppConnectionService.databaseBackend.storeIdentityKey(account, name, identityKey, status);
trustCache.remove(fingerprint);
} }
} }
@ -214,7 +222,7 @@ public class SQLiteAxolotlStore implements AxolotlStore {
return (fingerprint == null)? null : trustCache.get(fingerprint); return (fingerprint == null)? null : trustCache.get(fingerprint);
} }
public void setFingerprintTrust(String fingerprint, FingerprintStatus status) { public void setFingerprintStatus(String fingerprint, FingerprintStatus status) {
mXmppConnectionService.databaseBackend.setIdentityKeyTrust(account, fingerprint, status); mXmppConnectionService.databaseBackend.setIdentityKeyTrust(account, fingerprint, status);
trustCache.remove(fingerprint); trustCache.remove(fingerprint);
} }
@ -430,4 +438,8 @@ public class SQLiteAxolotlStore implements AxolotlStore {
public void removeSignedPreKey(int signedPreKeyId) { public void removeSignedPreKey(int signedPreKeyId) {
mXmppConnectionService.databaseBackend.deleteSignedPreKey(account, signedPreKeyId); mXmppConnectionService.databaseBackend.deleteSignedPreKey(account, signedPreKeyId);
} }
public void preVerifyFingerprint(Account account, String name, String fingerprint) {
mXmppConnectionService.databaseBackend.storePreVerification(account,name,fingerprint,FingerprintStatus.createInactiveVerified());
}
} }

View file

@ -73,7 +73,7 @@ public class XmppAxolotlSession {
} }
protected void setTrust(FingerprintStatus status) { protected void setTrust(FingerprintStatus status) {
sqLiteAxolotlStore.setFingerprintTrust(getFingerprint(), status); sqLiteAxolotlStore.setFingerprintStatus(getFingerprint(), status);
} }
protected FingerprintStatus getTrust() { protected FingerprintStatus getTrust() {

View file

@ -1106,7 +1106,12 @@ public class DatabaseBackend extends SQLiteOpenHelper {
continue; continue;
} }
try { try {
identityKeys.add(new IdentityKey(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT), 0)); String key = cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY));
if (key != null) {
identityKeys.add(new IdentityKey(Base64.decode(key, Base64.DEFAULT), 0));
} else {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Missing key (possibly preverified) in database for account" + account.getJid().toBareJid() + ", address: " + name);
}
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name); Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
} }
@ -1134,10 +1139,6 @@ public class DatabaseBackend extends SQLiteOpenHelper {
); );
} }
private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized) {
storeIdentityKey(account, name, own, fingerprint, base64Serialized, FingerprintStatus.createActiveUndecided());
}
private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized, FingerprintStatus status) { private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized, FingerprintStatus status) {
SQLiteDatabase db = this.getWritableDatabase(); SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
@ -1147,6 +1148,22 @@ public class DatabaseBackend extends SQLiteOpenHelper {
values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint); values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint);
values.put(SQLiteAxolotlStore.KEY, base64Serialized); values.put(SQLiteAxolotlStore.KEY, base64Serialized);
values.putAll(status.toContentValues()); values.putAll(status.toContentValues());
String where = SQLiteAxolotlStore.ACCOUNT+"=? AND "+SQLiteAxolotlStore.NAME+"=? AND "+SQLiteAxolotlStore.FINGERPRINT+" =?";
String[] whereArgs = {account.getUuid(),name,fingerprint};
int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME,values,where,whereArgs);
if (rows == 0) {
db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values);
}
}
public void storePreVerification(Account account, String name, String fingerprint, FingerprintStatus status) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
values.put(SQLiteAxolotlStore.NAME, name);
values.put(SQLiteAxolotlStore.OWN, 0);
values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint);
values.putAll(status.toContentValues());
db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values); db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values);
} }
@ -1227,8 +1244,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
} }
} }
public void storeIdentityKey(Account account, String name, IdentityKey identityKey) { public void storeIdentityKey(Account account, String name, IdentityKey identityKey, FingerprintStatus status) {
storeIdentityKey(account, name, false, identityKey.getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT)); storeIdentityKey(account, name, false, identityKey.getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT), status);
} }
public void storeOwnIdentityKeyPair(Account account, IdentityKeyPair identityKeyPair) { public void storeOwnIdentityKeyPair(Account account, IdentityKeyPair identityKeyPair) {

View file

@ -65,6 +65,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpDecryptionService; import eu.siacs.conversations.crypto.PgpDecryptionService;
import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Blockable; import eu.siacs.conversations.entities.Blockable;
@ -102,6 +103,7 @@ import eu.siacs.conversations.utils.PhoneHelper;
import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor; import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor;
import eu.siacs.conversations.utils.SerialSingleThreadExecutor; import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
import eu.siacs.conversations.utils.Xmlns; import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnBindListener; import eu.siacs.conversations.xmpp.OnBindListener;
import eu.siacs.conversations.xmpp.OnContactStatusChanged; import eu.siacs.conversations.xmpp.OnContactStatusChanged;
@ -3608,6 +3610,29 @@ public class XmppConnectionService extends Service {
}); });
} }
public void verifyFingerprints(Contact contact, List<XmppUri.Fingerprint> fingerprints) {
boolean needsRosterWrite = false;
final AxolotlService axolotlService = contact.getAccount().getAxolotlService();
for(XmppUri.Fingerprint fp : fingerprints) {
if (fp.type == XmppUri.FingerprintType.OTR) {
needsRosterWrite |= contact.addOtrFingerprint(fp.fingerprint);
} else if (fp.type == XmppUri.FingerprintType.OMEMO) {
String fingerprint = "05"+fp.fingerprint.replaceAll("\\s","");
FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint);
if (fingerprintStatus != null) {
if (!fingerprintStatus.isVerified()) {
axolotlService.setFingerprintTrust(fingerprint,fingerprintStatus.toVerified());
}
} else {
axolotlService.preVerifyFingerprint(contact,fingerprint);
}
}
}
if (needsRosterWrite) {
syncRosterToDisk(contact.getAccount());
}
}
public interface OnMamPreferencesFetched { public interface OnMamPreferencesFetched {
void onPreferencesFetched(Element prefs); void onPreferencesFetched(Element prefs);
void onPreferencesFetchFailed(); void onPreferencesFetchFailed();

View file

@ -397,11 +397,11 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
protected void showCreateContactDialog(final String prefilledJid, final String fingerprint) { protected void showCreateContactDialog(final String prefilledJid, final Invite invite) {
EnterJidDialog dialog = new EnterJidDialog( EnterJidDialog dialog = new EnterJidDialog(
this, mKnownHosts, mActivatedAccounts, this, mKnownHosts, mActivatedAccounts,
getString(R.string.create_contact), getString(R.string.create), getString(R.string.create_contact), getString(R.string.create),
prefilledJid, null, fingerprint == null prefilledJid, null, !invite.hasFingerprints()
); );
dialog.setOnEnterJidDialogPositiveListener(new EnterJidDialog.OnEnterJidDialogPositiveListener() { dialog.setOnEnterJidDialogPositiveListener(new EnterJidDialog.OnEnterJidDialogPositiveListener() {
@ -420,7 +420,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
if (contact.showInRoster()) { if (contact.showInRoster()) {
throw new EnterJidDialog.JidError(getString(R.string.contact_already_exists)); throw new EnterJidDialog.JidError(getString(R.string.contact_already_exists));
} else { } else {
contact.addOtrFingerprint(fingerprint); //contact.addOtrFingerprint(fingerprint);
xmppConnectionService.createContact(contact); xmppConnectionService.createContact(contact);
switchToConversation(contact); switchToConversation(contact);
return true; return true;
@ -842,6 +842,10 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
} }
private boolean handleJid(Invite invite) { private boolean handleJid(Invite invite) {
Log.d(Config.LOGTAG,"handling invite for "+invite.getJid());
for(XmppUri.Fingerprint fp : invite.getFingerprints()) {
Log.d(Config.LOGTAG,fp.toString());
}
List<Contact> contacts = xmppConnectionService.findContacts(invite.getJid()); List<Contact> contacts = xmppConnectionService.findContacts(invite.getJid());
if (invite.isMuc()) { if (invite.isMuc()) {
Conversation muc = xmppConnectionService.findFirstMuc(invite.getJid()); Conversation muc = xmppConnectionService.findFirstMuc(invite.getJid());
@ -853,16 +857,19 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
return false; return false;
} }
} else if (contacts.size() == 0) { } else if (contacts.size() == 0) {
showCreateContactDialog(invite.getJid().toString(), invite.getFingerprint()); showCreateContactDialog(invite.getJid().toString(), invite);
return false; return false;
} else if (contacts.size() == 1) { } else if (contacts.size() == 1) {
Contact contact = contacts.get(0); Contact contact = contacts.get(0);
if (invite.getFingerprint() != null) { if (invite.hasFingerprints()) {
xmppConnectionService.verifyFingerprints(contact,invite.getFingerprints());
}
/*if (invite.getFingerprint() != null) {
if (contact.addOtrFingerprint(invite.getFingerprint())) { if (contact.addOtrFingerprint(invite.getFingerprint())) {
Log.d(Config.LOGTAG, "added new fingerprint"); Log.d(Config.LOGTAG, "added new fingerprint");
xmppConnectionService.syncRosterToDisk(contact.getAccount()); xmppConnectionService.syncRosterToDisk(contact.getAccount());
} }
} }*/
switchToConversation(contact); switchToConversation(contact);
return true; return true;
} else { } else {

View file

@ -18,6 +18,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.OmemoActivity; import eu.siacs.conversations.OmemoActivity;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@ -245,7 +246,9 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
Toast.makeText(TrustKeysActivity.this,R.string.error_fetching_omemo_key,Toast.LENGTH_SHORT).show(); Toast.makeText(TrustKeysActivity.this,R.string.error_fetching_omemo_key,Toast.LENGTH_SHORT).show();
break; break;
case SUCCESS_VERIFIED: case SUCCESS_VERIFIED:
Toast.makeText(TrustKeysActivity.this,R.string.verified_omemo_key_with_certificate,Toast.LENGTH_LONG).show(); Toast.makeText(TrustKeysActivity.this,
Config.X509_VERIFICATION ? R.string.verified_omemo_key_with_certificate : R.string.all_omemo_keys_have_been_verified,
Toast.LENGTH_LONG).show();
break; break;
} }
} }

View file

@ -173,11 +173,10 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
protected boolean verifyWithUri(XmppUri uri) { protected boolean verifyWithUri(XmppUri uri) {
Contact contact = mConversation.getContact(); Contact contact = mConversation.getContact();
if (this.mConversation.getContact().getJid().equals(uri.getJid()) && uri.getFingerprint() != null) { if (this.mConversation.getContact().getJid().equals(uri.getJid()) && uri.hasFingerprints()) {
contact.addOtrFingerprint(uri.getFingerprint()); xmppConnectionService.verifyFingerprints(contact,uri.getFingerprints());
Toast.makeText(this,R.string.verified,Toast.LENGTH_SHORT).show(); Toast.makeText(this,R.string.verified,Toast.LENGTH_SHORT).show();
updateView(); updateView();
xmppConnectionService.syncRosterToDisk(contact.getAccount());
return true; return true;
} else { } else {
Toast.makeText(this,R.string.could_not_verify_fingerprint,Toast.LENGTH_SHORT).show(); Toast.makeText(this,R.string.could_not_verify_fingerprint,Toast.LENGTH_SHORT).show();

View file

@ -4,7 +4,9 @@ import android.net.Uri;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
@ -13,7 +15,9 @@ public class XmppUri {
protected String jid; protected String jid;
protected boolean muc; protected boolean muc;
protected String fingerprint; protected List<Fingerprint> fingerprints = new ArrayList<>();
private static final String OMEMO_URI_PARAM = "omemo-sid-";
public XmppUri(String uri) { public XmppUri(String uri) {
try { try {
@ -56,7 +60,7 @@ public class XmppUri {
} else { } else {
jid = uri.getSchemeSpecificPart().split("\\?")[0]; jid = uri.getSchemeSpecificPart().split("\\?")[0];
} }
fingerprint = parseFingerprint(uri.getQuery()); this.fingerprints = parseFingerprints(uri.getQuery());
} else if ("imto".equalsIgnoreCase(scheme)) { } else if ("imto".equalsIgnoreCase(scheme)) {
// sample: imto://xmpp/foo@bar.com // sample: imto://xmpp/foo@bar.com
try { try {
@ -73,19 +77,29 @@ public class XmppUri {
} }
} }
protected String parseFingerprint(String query) { protected List<Fingerprint> parseFingerprints(String query) {
if (query == null) { List<Fingerprint> fingerprints = new ArrayList<>();
return null; String[] pairs = query == null ? new String[0] : query.split(";");
} else { for(String pair : pairs) {
final String NEEDLE = "otr-fingerprint="; String[] parts = pair.split("=",2);
int index = query.indexOf(NEEDLE); if (parts.length == 2) {
if (index >= 0 && query.length() >= (NEEDLE.length() + index + 40)) { String key = parts[0].toLowerCase(Locale.US);
return query.substring(index + NEEDLE.length(), index + NEEDLE.length() + 40); String value = parts[1];
} else { if ("otr-fingerprint".equals(key)) {
return null; fingerprints.add(new Fingerprint(FingerprintType.OTR,value));
}
if (key.startsWith(OMEMO_URI_PARAM)) {
try {
int id = Integer.parseInt(key.substring(OMEMO_URI_PARAM.length()));
fingerprints.add(new Fingerprint(FingerprintType.OMEMO,value,id));
} catch (Exception e) {
//ignoring invalid device id
} }
} }
} }
}
return fingerprints;
}
public Jid getJid() { public Jid getJid() {
try { try {
@ -95,7 +109,36 @@ public class XmppUri {
} }
} }
public String getFingerprint() { public List<Fingerprint> getFingerprints() {
return this.fingerprint; return this.fingerprints;
}
public boolean hasFingerprints() {
return fingerprints.size() > 0;
}
public enum FingerprintType {
OMEMO,
OTR
}
public static class Fingerprint {
public final FingerprintType type;
public final String fingerprint;
public final int deviceId;
public Fingerprint(FingerprintType type, String fingerprint) {
this(type, fingerprint, 0);
}
public Fingerprint(FingerprintType type, String fingerprint, int deviceId) {
this.type = type;
this.fingerprint = fingerprint;
this.deviceId = deviceId;
}
@Override
public String toString() {
return type.toString()+": "+fingerprint+(deviceId != 0 ? " "+String.valueOf(deviceId) : "");
}
} }
} }

View file

@ -702,4 +702,5 @@
<string name="error_unable_to_create_temporary_file">Unable to create temporary file</string> <string name="error_unable_to_create_temporary_file">Unable to create temporary file</string>
<string name="this_device_has_been_verified">This device has been verified</string> <string name="this_device_has_been_verified">This device has been verified</string>
<string name="copy_fingerprint">Copy fingerprint</string> <string name="copy_fingerprint">Copy fingerprint</string>
<string name="all_omemo_keys_have_been_verified">All OMEMO keys have been verified</string>
</resources> </resources>