refactore trust enum to be FingerprintStatus class with trust and active

This commit is contained in:
Daniel Gultsch 2016-11-14 22:27:41 +01:00
parent 6da8b50d95
commit 05fc15be3d
11 changed files with 330 additions and 292 deletions

View File

@ -256,18 +256,18 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return axolotlStore.getIdentityKeyPair().getPublicKey().getFingerprint().replaceAll("\\s", ""); return axolotlStore.getIdentityKeyPair().getPublicKey().getFingerprint().replaceAll("\\s", "");
} }
public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust) { public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status) {
return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toPreppedString(), trust); return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toPreppedString(), status);
} }
public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Jid jid) { public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status, Jid jid) {
return axolotlStore.getContactKeysWithTrust(jid.toBareJid().toPreppedString(), trust); return axolotlStore.getContactKeysWithTrust(jid.toBareJid().toPreppedString(), status);
} }
public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, List<Jid> jids) { public Set<IdentityKey> getKeysWithTrust(FingerprintStatus status, List<Jid> jids) {
Set<IdentityKey> keys = new HashSet<>(); Set<IdentityKey> keys = new HashSet<>();
for(Jid jid : jids) { for(Jid jid : jids) {
keys.addAll(axolotlStore.getContactKeysWithTrust(jid.toPreppedString(), trust)); keys.addAll(axolotlStore.getContactKeysWithTrust(jid.toPreppedString(), status));
} }
return keys; return keys;
} }
@ -355,19 +355,6 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return this.deviceIds.get(account.getJid().toBareJid()); return this.deviceIds.get(account.getJid().toBareJid());
} }
private void setTrustOnSessions(final Jid jid, @NonNull final Set<Integer> deviceIds,
final XmppAxolotlSession.Trust from,
final XmppAxolotlSession.Trust to) {
for (Integer deviceId : deviceIds) {
AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
XmppAxolotlSession session = sessions.get(address);
if (session != null && session.getFingerprint() != null
&& session.getTrust() == from) {
session.setTrust(to);
}
}
}
public void registerDevices(final Jid jid, @NonNull final Set<Integer> deviceIds) { public void registerDevices(final Jid jid, @NonNull final Set<Integer> deviceIds) {
if (jid.toBareJid().equals(account.getJid().toBareJid())) { if (jid.toBareJid().equals(account.getJid().toBareJid())) {
if (!deviceIds.isEmpty()) { if (!deviceIds.isEmpty()) {
@ -389,23 +376,25 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
} }
Set<Integer> expiredDevices = new HashSet<>(axolotlStore.getSubDeviceSessions(jid.toBareJid().toPreppedString())); Set<Integer> expiredDevices = new HashSet<>(axolotlStore.getSubDeviceSessions(jid.toBareJid().toPreppedString()));
expiredDevices.removeAll(deviceIds); expiredDevices.removeAll(deviceIds);
setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.TRUSTED, for (Integer deviceId : expiredDevices) {
XmppAxolotlSession.Trust.INACTIVE_TRUSTED); AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.TRUSTED_X509, XmppAxolotlSession session = sessions.get(address);
XmppAxolotlSession.Trust.INACTIVE_TRUSTED_X509); if (session != null && session.getFingerprint() != null) {
setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.UNDECIDED, if (session.getTrust().isActive()) {
XmppAxolotlSession.Trust.INACTIVE_UNDECIDED); session.setTrust(session.getTrust().toInactive());
setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.UNTRUSTED, }
XmppAxolotlSession.Trust.INACTIVE_UNTRUSTED); }
}
Set<Integer> newDevices = new HashSet<>(deviceIds); Set<Integer> newDevices = new HashSet<>(deviceIds);
setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_TRUSTED, for (Integer deviceId : newDevices) {
XmppAxolotlSession.Trust.TRUSTED); AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toPreppedString(), deviceId);
setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_TRUSTED_X509, XmppAxolotlSession session = sessions.get(address);
XmppAxolotlSession.Trust.TRUSTED_X509); if (session != null && session.getFingerprint() != null) {
setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_UNDECIDED, if (!session.getTrust().isActive()) {
XmppAxolotlSession.Trust.UNDECIDED); session.setTrust(session.getTrust().toActive());
setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_UNTRUSTED, }
XmppAxolotlSession.Trust.UNTRUSTED); }
}
this.deviceIds.put(jid, deviceIds); this.deviceIds.put(jid, deviceIds);
mXmppConnectionService.keyStatusUpdated(null); mXmppConnectionService.keyStatusUpdated(null);
} }
@ -428,7 +417,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
} }
public void purgeKey(final String fingerprint) { public void purgeKey(final String fingerprint) {
axolotlStore.setFingerprintTrust(fingerprint.replaceAll("\\s", ""), XmppAxolotlSession.Trust.COMPROMISED); axolotlStore.setFingerprintTrust(fingerprint.replaceAll("\\s", ""), FingerprintStatus.createCompromised());
} }
public void publishOwnDeviceIdIfNeeded() { public void publishOwnDeviceIdIfNeeded() {
@ -692,16 +681,16 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return jids; return jids;
} }
public XmppAxolotlSession.Trust getFingerprintTrust(String fingerprint) { public FingerprintStatus getFingerprintTrust(String fingerprint) {
return axolotlStore.getFingerprintTrust(fingerprint); return axolotlStore.getFingerprintStatus(fingerprint);
} }
public X509Certificate getFingerprintCertificate(String fingerprint) { public X509Certificate getFingerprintCertificate(String fingerprint) {
return axolotlStore.getFingerprintCertificate(fingerprint); return axolotlStore.getFingerprintCertificate(fingerprint);
} }
public void setFingerprintTrust(String fingerprint, XmppAxolotlSession.Trust trust) { public void setFingerprintTrust(String fingerprint, FingerprintStatus status) {
axolotlStore.setFingerprintTrust(fingerprint, trust); axolotlStore.setFingerprintTrust(fingerprint, status);
} }
private void verifySessionWithPEP(final XmppAxolotlSession session) { private void verifySessionWithPEP(final XmppAxolotlSession session) {
@ -724,7 +713,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
mXmppConnectionService.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(verification.first, "RSA"); mXmppConnectionService.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(verification.first, "RSA");
String fingerprint = session.getFingerprint(); String fingerprint = session.getFingerprint();
Log.d(Config.LOGTAG, "verified session with x.509 signature. fingerprint was: "+fingerprint); Log.d(Config.LOGTAG, "verified session with x.509 signature. fingerprint was: "+fingerprint);
setFingerprintTrust(fingerprint, XmppAxolotlSession.Trust.TRUSTED_X509); setFingerprintTrust(fingerprint, FingerprintStatus.createActiveVerified(true));
axolotlStore.setFingerprintCertificate(fingerprint, verification.first[0]); axolotlStore.setFingerprintCertificate(fingerprint, verification.first[0]);
fetchStatusMap.put(address, FetchStatus.SUCCESS_VERIFIED); fetchStatusMap.put(address, FetchStatus.SUCCESS_VERIFIED);
Bundle information = CryptoHelper.extractCertificateInformation(verification.first[0]); Bundle information = CryptoHelper.extractCertificateInformation(verification.first[0]);
@ -921,8 +910,8 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
sessions.addAll(findOwnSessions()); sessions.addAll(findOwnSessions());
boolean verified = false; boolean verified = false;
for(XmppAxolotlSession session : sessions) { for(XmppAxolotlSession session : sessions) {
if (session.getTrust().trusted()) { if (session.getTrust().isTrustedAndActive()) {
if (session.getTrust() == XmppAxolotlSession.Trust.TRUSTED_X509) { if (session.getTrust().getTrust() == FingerprintStatus.Trust.VERIFIED_X509) {
verified = true; verified = true;
} else { } else {
return false; return false;

View File

@ -0,0 +1,122 @@
package eu.siacs.conversations.crypto.axolotl;
import android.content.ContentValues;
import android.database.Cursor;
public class FingerprintStatus {
private Trust trust = Trust.UNTRUSTED;
private boolean active = false;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FingerprintStatus that = (FingerprintStatus) o;
return active == that.active && trust == that.trust;
}
@Override
public int hashCode() {
int result = trust.hashCode();
result = 31 * result + (active ? 1 : 0);
return result;
}
private FingerprintStatus() {
}
public ContentValues toContentValues() {
final ContentValues contentValues = new ContentValues();
contentValues.put(SQLiteAxolotlStore.TRUST,trust.toString());
contentValues.put(SQLiteAxolotlStore.ACTIVE,active ? 1 : 0);
return contentValues;
}
public static FingerprintStatus fromCursor(Cursor cursor) {
final FingerprintStatus status = new FingerprintStatus();
try {
status.trust = Trust.valueOf(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.TRUST)));
} catch(IllegalArgumentException e) {
status.trust = Trust.UNTRUSTED;
}
status.active = cursor.getInt(cursor.getColumnIndex(SQLiteAxolotlStore.ACTIVE)) > 0;
return status;
}
public static FingerprintStatus createActiveUndecided() {
final FingerprintStatus status = new FingerprintStatus();
status.trust = Trust.UNDECIDED;
status.active = true;
return status;
}
public static FingerprintStatus createActiveVerified(boolean x509) {
final FingerprintStatus status = new FingerprintStatus();
status.trust = x509 ? Trust.VERIFIED_X509 : Trust.VERIFIED;
status.active = true;
return status;
}
public static FingerprintStatus createActive(boolean trusted) {
final FingerprintStatus status = new FingerprintStatus();
status.trust = trusted ? Trust.TRUSTED : Trust.UNTRUSTED;
status.active = true;
return status;
}
public boolean isTrustedAndActive() {
return active && isTrusted();
}
public boolean isTrusted() {
return trust == Trust.TRUSTED || trust == Trust.VERIFIED || trust == Trust.VERIFIED_X509;
}
public boolean isCompromised() {
return trust == Trust.COMPROMISED;
}
public boolean isActive() {
return active;
}
public FingerprintStatus toActive() {
FingerprintStatus status = new FingerprintStatus();
status.trust = trust;
status.active = true;
return status;
}
public FingerprintStatus toInactive() {
FingerprintStatus status = new FingerprintStatus();
status.trust = trust;
status.active = false;
return status;
}
public Trust getTrust() {
return trust;
}
public static FingerprintStatus createCompromised() {
FingerprintStatus status = new FingerprintStatus();
status.active = false;
status.trust = Trust.COMPROMISED;
return status;
}
public enum Trust {
COMPROMISED,
UNDECIDED,
UNTRUSTED,
TRUSTED,
VERIFIED,
VERIFIED_X509
}
}

View File

@ -35,7 +35,9 @@ public class SQLiteAxolotlStore implements AxolotlStore {
public static final String KEY = "key"; public static final String KEY = "key";
public static final String FINGERPRINT = "fingerprint"; public static final String FINGERPRINT = "fingerprint";
public static final String NAME = "name"; public static final String NAME = "name";
public static final String TRUSTED = "trusted"; public static final String TRUSTED = "trusted"; //no longer used
public static final String TRUST = "trust";
public static final String ACTIVE = "active";
public static final String OWN = "ownkey"; public static final String OWN = "ownkey";
public static final String CERTIFICATE = "certificate"; public static final String CERTIFICATE = "certificate";
@ -51,11 +53,11 @@ public class SQLiteAxolotlStore implements AxolotlStore {
private int localRegistrationId; private int localRegistrationId;
private int currentPreKeyId = 0; private int currentPreKeyId = 0;
private final LruCache<String, XmppAxolotlSession.Trust> trustCache = private final LruCache<String, FingerprintStatus> trustCache =
new LruCache<String, XmppAxolotlSession.Trust>(NUM_TRUSTS_TO_CACHE) { new LruCache<String, FingerprintStatus>(NUM_TRUSTS_TO_CACHE) {
@Override @Override
protected XmppAxolotlSession.Trust create(String fingerprint) { protected FingerprintStatus create(String fingerprint) {
return mXmppConnectionService.databaseBackend.isIdentityKeyTrusted(account, fingerprint); return mXmppConnectionService.databaseBackend.getFingerprintStatus(account, fingerprint);
} }
}; };
@ -208,12 +210,12 @@ public class SQLiteAxolotlStore implements AxolotlStore {
return true; return true;
} }
public XmppAxolotlSession.Trust getFingerprintTrust(String fingerprint) { public FingerprintStatus getFingerprintStatus(String fingerprint) {
return (fingerprint == null)? null : trustCache.get(fingerprint); return (fingerprint == null)? null : trustCache.get(fingerprint);
} }
public void setFingerprintTrust(String fingerprint, XmppAxolotlSession.Trust trust) { public void setFingerprintTrust(String fingerprint, FingerprintStatus status) {
mXmppConnectionService.databaseBackend.setIdentityKeyTrust(account, fingerprint, trust); mXmppConnectionService.databaseBackend.setIdentityKeyTrust(account, fingerprint, status);
trustCache.remove(fingerprint); trustCache.remove(fingerprint);
} }
@ -225,8 +227,8 @@ public class SQLiteAxolotlStore implements AxolotlStore {
return mXmppConnectionService.databaseBackend.getIdentityKeyCertifcate(account, fingerprint); return mXmppConnectionService.databaseBackend.getIdentityKeyCertifcate(account, fingerprint);
} }
public Set<IdentityKey> getContactKeysWithTrust(String bareJid, XmppAxolotlSession.Trust trust) { public Set<IdentityKey> getContactKeysWithTrust(String bareJid, FingerprintStatus status) {
return mXmppConnectionService.databaseBackend.loadIdentityKeys(account, bareJid, trust); return mXmppConnectionService.databaseBackend.loadIdentityKeys(account, bareJid, status);
} }
public long getContactNumTrustedKeys(String bareJid) { public long getContactNumTrustedKeys(String bareJid) {

View File

@ -19,9 +19,6 @@ import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
import org.whispersystems.libaxolotl.protocol.WhisperMessage; import org.whispersystems.libaxolotl.protocol.WhisperMessage;
import java.util.HashMap;
import java.util.Map;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
@ -34,76 +31,6 @@ public class XmppAxolotlSession {
private Integer preKeyId = null; private Integer preKeyId = null;
private boolean fresh = true; private boolean fresh = true;
public enum Trust {
UNDECIDED(0),
TRUSTED(1),
UNTRUSTED(2),
COMPROMISED(3),
INACTIVE_TRUSTED(4),
INACTIVE_UNDECIDED(5),
INACTIVE_UNTRUSTED(6),
TRUSTED_X509(7),
INACTIVE_TRUSTED_X509(8);
private static final Map<Integer, Trust> trustsByValue = new HashMap<>();
static {
for (Trust trust : Trust.values()) {
trustsByValue.put(trust.getCode(), trust);
}
}
private final int code;
Trust(int code) {
this.code = code;
}
public int getCode() {
return this.code;
}
public String toString() {
switch (this) {
case UNDECIDED:
return "Trust undecided " + getCode();
case TRUSTED:
return "Trusted " + getCode();
case COMPROMISED:
return "Compromised " + getCode();
case INACTIVE_TRUSTED:
return "Inactive (Trusted)" + getCode();
case INACTIVE_UNDECIDED:
return "Inactive (Undecided)" + getCode();
case INACTIVE_UNTRUSTED:
return "Inactive (Untrusted)" + getCode();
case TRUSTED_X509:
return "Trusted (X509) " + getCode();
case INACTIVE_TRUSTED_X509:
return "Inactive (Trusted (X509)) " + getCode();
case UNTRUSTED:
default:
return "Untrusted " + getCode();
}
}
public static Trust fromBoolean(Boolean trusted) {
return trusted ? TRUSTED : UNTRUSTED;
}
public static Trust fromCode(int code) {
return trustsByValue.get(code);
}
public boolean trusted() {
return this == TRUSTED_X509 || this == TRUSTED;
}
public boolean trustedInactive() {
return this == INACTIVE_TRUSTED_X509 || this == INACTIVE_TRUSTED;
}
}
public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress, IdentityKey identityKey) { public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress, IdentityKey identityKey) {
this(account, store, remoteAddress); this(account, store, remoteAddress);
this.identityKey = identityKey; this.identityKey = identityKey;
@ -145,75 +72,60 @@ public class XmppAxolotlSession {
this.fresh = false; this.fresh = false;
} }
protected void setTrust(Trust trust) { protected void setTrust(FingerprintStatus status) {
sqLiteAxolotlStore.setFingerprintTrust(getFingerprint(), trust); sqLiteAxolotlStore.setFingerprintTrust(getFingerprint(), status);
} }
protected Trust getTrust() { protected FingerprintStatus getTrust() {
Trust trust = sqLiteAxolotlStore.getFingerprintTrust(getFingerprint()); FingerprintStatus status = sqLiteAxolotlStore.getFingerprintStatus(getFingerprint());
return (trust == null) ? Trust.UNDECIDED : trust; return (status == null) ? FingerprintStatus.createActiveUndecided() : status;
} }
@Nullable @Nullable
public byte[] processReceiving(byte[] encryptedKey) { public byte[] processReceiving(byte[] encryptedKey) {
byte[] plaintext = null; byte[] plaintext = null;
Trust trust = getTrust(); FingerprintStatus status = getTrust();
switch (trust) { if (!status.isCompromised()) {
case INACTIVE_TRUSTED: try {
case UNDECIDED:
case UNTRUSTED:
case TRUSTED:
case INACTIVE_TRUSTED_X509:
case TRUSTED_X509:
try { try {
try { PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey);
PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey); if (!message.getPreKeyId().isPresent()) {
if (!message.getPreKeyId().isPresent()) { Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage did not contain a PreKeyId");
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage did not contain a PreKeyId"); return null;
break;
}
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
IdentityKey msgIdentityKey = message.getIdentityKey();
if (this.identityKey != null && !this.identityKey.equals(msgIdentityKey)) {
Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Had session with fingerprint " + this.getFingerprint() + ", received message with fingerprint " + msgIdentityKey.getFingerprint());
} else {
this.identityKey = msgIdentityKey;
plaintext = cipher.decrypt(message);
preKeyId = message.getPreKeyId().get();
}
} catch (InvalidMessageException | InvalidVersionException e) {
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "WhisperMessage received");
WhisperMessage message = new WhisperMessage(encryptedKey);
plaintext = cipher.decrypt(message);
} catch (InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e) {
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
} }
} catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e) { Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
IdentityKey msgIdentityKey = message.getIdentityKey();
if (this.identityKey != null && !this.identityKey.equals(msgIdentityKey)) {
Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Had session with fingerprint " + this.getFingerprint() + ", received message with fingerprint " + msgIdentityKey.getFingerprint());
} else {
this.identityKey = msgIdentityKey;
plaintext = cipher.decrypt(message);
preKeyId = message.getPreKeyId().get();
}
} catch (InvalidMessageException | InvalidVersionException e) {
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "WhisperMessage received");
WhisperMessage message = new WhisperMessage(encryptedKey);
plaintext = cipher.decrypt(message);
} catch (InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e) {
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage()); Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
} }
} catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e) {
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
}
if (plaintext != null) { if (plaintext != null) {
if (trust == Trust.INACTIVE_TRUSTED) { if (!status.isActive()) {
setTrust(Trust.TRUSTED); setTrust(status.toActive());
} else if (trust == Trust.INACTIVE_TRUSTED_X509) {
setTrust(Trust.TRUSTED_X509);
}
} }
}
break;
case COMPROMISED:
default:
// ignore
break;
} }
return plaintext; return plaintext;
} }
@Nullable @Nullable
public byte[] processSending(@NonNull byte[] outgoingMessage) { public byte[] processSending(@NonNull byte[] outgoingMessage) {
Trust trust = getTrust(); FingerprintStatus status = getTrust();
if (trust.trusted()) { if (status.isTrustedAndActive()) {
CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage); CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
return ciphertextMessage.serialize(); return ciphertextMessage.serialize();
} else { } else {

View File

@ -8,6 +8,7 @@ import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.GeoHelper;
@ -811,8 +812,8 @@ public class Message extends AbstractEntity {
} }
public boolean isTrusted() { public boolean isTrusted() {
XmppAxolotlSession.Trust t = conversation.getAccount().getAxolotlService().getFingerprintTrust(axolotlFingerprint); FingerprintStatus s = conversation.getAccount().getAxolotlService().getFingerprintTrust(axolotlFingerprint);
return t != null && t.trusted(); return s != null && s.isTrustedAndActive();
} }
private int getPreviousEncryption() { private int getPreviousEncryption() {

View File

@ -27,17 +27,19 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import org.json.JSONException; import org.json.JSONException;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
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.SQLiteAxolotlStore; import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
@ -45,7 +47,6 @@ import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.PresenceTemplate; import eu.siacs.conversations.entities.PresenceTemplate;
import eu.siacs.conversations.entities.Roster; import eu.siacs.conversations.entities.Roster;
import eu.siacs.conversations.entities.ServiceDiscoveryResult; import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.generator.AbstractGenerator;
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;
@ -54,7 +55,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null; private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history"; private static final String DATABASE_NAME = "history";
private static final int DATABASE_VERSION = 30; private static final int DATABASE_VERSION = 31;
private static String CREATE_CONTATCS_STATEMENT = "create table " private static String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@ -129,7 +130,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ SQLiteAxolotlStore.OWN + " INTEGER, " + SQLiteAxolotlStore.OWN + " INTEGER, "
+ SQLiteAxolotlStore.FINGERPRINT + " TEXT, " + SQLiteAxolotlStore.FINGERPRINT + " TEXT, "
+ SQLiteAxolotlStore.CERTIFICATE + " BLOB, " + SQLiteAxolotlStore.CERTIFICATE + " BLOB, "
+ SQLiteAxolotlStore.TRUSTED + " INTEGER, " + SQLiteAxolotlStore.TRUST + " TEXT, "
+ SQLiteAxolotlStore.ACTIVE + " NUMBER, "
+ SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY(" + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
+ SQLiteAxolotlStore.ACCOUNT + SQLiteAxolotlStore.ACCOUNT
+ ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, " + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
@ -297,7 +299,16 @@ public class DatabaseBackend extends SQLiteOpenHelper {
deleteSession(db, account, ownAddress); deleteSession(db, account, ownAddress);
IdentityKeyPair identityKeyPair = loadOwnIdentityKeyPair(db, account); IdentityKeyPair identityKeyPair = loadOwnIdentityKeyPair(db, account);
if (identityKeyPair != null) { if (identityKeyPair != null) {
setIdentityKeyTrust(db, account, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), XmppAxolotlSession.Trust.TRUSTED); String[] selectionArgs = {
account.getUuid(),
identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", "")
};
ContentValues values = new ContentValues();
values.put(SQLiteAxolotlStore.TRUSTED, 2);
db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values,
SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ SQLiteAxolotlStore.FINGERPRINT + " = ? ",
selectionArgs);
} else { } else {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not load own identity key pair"); Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not load own identity key pair");
} }
@ -346,6 +357,33 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 30 && newVersion >= 30) { if (oldVersion < 30 && newVersion >= 30) {
db.execSQL(CREATE_START_TIMES_TABLE); db.execSQL(CREATE_START_TIMES_TABLE);
} }
if (oldVersion < 31 && newVersion >= 31) {
db.execSQL("ALTER TABLE "+ SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN "+SQLiteAxolotlStore.TRUST + " TEXT");
db.execSQL("ALTER TABLE "+ SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN "+SQLiteAxolotlStore.ACTIVE + " NUMBER");
HashMap<Integer,ContentValues> migration = new HashMap<>();
migration.put(0,createFingerprintStatusContentValues(FingerprintStatus.Trust.UNDECIDED,true));
migration.put(1,createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, true));
migration.put(2,createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, true));
migration.put(3,createFingerprintStatusContentValues(FingerprintStatus.Trust.COMPROMISED, false));
migration.put(4,createFingerprintStatusContentValues(FingerprintStatus.Trust.TRUSTED, false));
migration.put(5,createFingerprintStatusContentValues(FingerprintStatus.Trust.UNDECIDED, false));
migration.put(6,createFingerprintStatusContentValues(FingerprintStatus.Trust.UNTRUSTED, false));
migration.put(7,createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED_X509, true));
migration.put(8,createFingerprintStatusContentValues(FingerprintStatus.Trust.VERIFIED_X509, false));
for(Map.Entry<Integer,ContentValues> entry : migration.entrySet()) {
String whereClause = SQLiteAxolotlStore.TRUSTED+"=?";
String[] where = {String.valueOf(entry.getKey())};
db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME,entry.getValue(),whereClause,where);
}
}
}
private static ContentValues createFingerprintStatusContentValues(FingerprintStatus.Trust trust, boolean active) {
ContentValues values = new ContentValues();
values.put(SQLiteAxolotlStore.TRUST,trust.toString());
values.put(SQLiteAxolotlStore.ACTIVE,active ? 1 : 0);
return values;
} }
private void canonicalizeJids(SQLiteDatabase db) { private void canonicalizeJids(SQLiteDatabase db) {
@ -1006,7 +1044,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
} }
private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, Boolean own, String fingerprint) { private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, Boolean own, String fingerprint) {
String[] columns = {SQLiteAxolotlStore.TRUSTED, String[] columns = {SQLiteAxolotlStore.TRUST,
SQLiteAxolotlStore.ACTIVE,
SQLiteAxolotlStore.KEY}; SQLiteAxolotlStore.KEY};
ArrayList<String> selectionArgs = new ArrayList<>(4); ArrayList<String> selectionArgs = new ArrayList<>(4);
selectionArgs.add(account.getUuid()); selectionArgs.add(account.getUuid());
@ -1058,14 +1097,12 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return loadIdentityKeys(account, name, null); return loadIdentityKeys(account, name, null);
} }
public Set<IdentityKey> loadIdentityKeys(Account account, String name, XmppAxolotlSession.Trust trust) { public Set<IdentityKey> loadIdentityKeys(Account account, String name, FingerprintStatus status) {
Set<IdentityKey> identityKeys = new HashSet<>(); Set<IdentityKey> identityKeys = new HashSet<>();
Cursor cursor = getIdentityKeyCursor(account, name, false); Cursor cursor = getIdentityKeyCursor(account, name, false);
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
if (trust != null && if (status != null && !FingerprintStatus.fromCursor(cursor).equals(status)) {
cursor.getInt(cursor.getColumnIndex(SQLiteAxolotlStore.TRUSTED))
!= trust.getCode()) {
continue; continue;
} }
try { try {
@ -1084,22 +1121,24 @@ public class DatabaseBackend extends SQLiteOpenHelper {
String[] args = { String[] args = {
account.getUuid(), account.getUuid(),
name, name,
String.valueOf(XmppAxolotlSession.Trust.TRUSTED.getCode()), FingerprintStatus.Trust.TRUSTED.toString(),
String.valueOf(XmppAxolotlSession.Trust.TRUSTED_X509.getCode()) FingerprintStatus.Trust.VERIFIED.toString(),
FingerprintStatus.Trust.VERIFIED_X509.toString()
}; };
return DatabaseUtils.queryNumEntries(db, SQLiteAxolotlStore.IDENTITIES_TABLENAME, return DatabaseUtils.queryNumEntries(db, SQLiteAxolotlStore.IDENTITIES_TABLENAME,
SQLiteAxolotlStore.ACCOUNT + " = ?" SQLiteAxolotlStore.ACCOUNT + " = ?"
+ " AND " + SQLiteAxolotlStore.NAME + " = ?" + " AND " + SQLiteAxolotlStore.NAME + " = ?"
+ " AND (" + SQLiteAxolotlStore.TRUSTED + " = ? OR " + SQLiteAxolotlStore.TRUSTED + " = ?)", + " AND (" + SQLiteAxolotlStore.TRUST + " = ? OR " + SQLiteAxolotlStore.TRUST + " = ? OR " +SQLiteAxolotlStore.TRUST +" = ?)"
+ " AND " +SQLiteAxolotlStore.ACTIVE + " > 0",
args args
); );
} }
private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized) { private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized) {
storeIdentityKey(account, name, own, fingerprint, base64Serialized, XmppAxolotlSession.Trust.UNDECIDED); storeIdentityKey(account, name, own, fingerprint, base64Serialized, FingerprintStatus.createActiveUndecided());
} }
private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized, XmppAxolotlSession.Trust trusted) { 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();
values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid()); values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
@ -1107,35 +1146,34 @@ public class DatabaseBackend extends SQLiteOpenHelper {
values.put(SQLiteAxolotlStore.OWN, own ? 1 : 0); values.put(SQLiteAxolotlStore.OWN, own ? 1 : 0);
values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint); values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint);
values.put(SQLiteAxolotlStore.KEY, base64Serialized); values.put(SQLiteAxolotlStore.KEY, base64Serialized);
values.put(SQLiteAxolotlStore.TRUSTED, trusted.getCode()); values.putAll(status.toContentValues());
db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values); db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values);
} }
public XmppAxolotlSession.Trust isIdentityKeyTrusted(Account account, String fingerprint) { public FingerprintStatus getFingerprintStatus(Account account, String fingerprint) {
Cursor cursor = getIdentityKeyCursor(account, fingerprint); Cursor cursor = getIdentityKeyCursor(account, fingerprint);
XmppAxolotlSession.Trust trust = null; final FingerprintStatus status;
if (cursor.getCount() > 0) { if (cursor.getCount() > 0) {
cursor.moveToFirst(); cursor.moveToFirst();
int trustValue = cursor.getInt(cursor.getColumnIndex(SQLiteAxolotlStore.TRUSTED)); status = FingerprintStatus.fromCursor(cursor);
trust = XmppAxolotlSession.Trust.fromCode(trustValue); } else {
status = null;
} }
cursor.close(); cursor.close();
return trust; return status;
} }
public boolean setIdentityKeyTrust(Account account, String fingerprint, XmppAxolotlSession.Trust trust) { public boolean setIdentityKeyTrust(Account account, String fingerprint, FingerprintStatus fingerprintStatus) {
SQLiteDatabase db = this.getWritableDatabase(); SQLiteDatabase db = this.getWritableDatabase();
return setIdentityKeyTrust(db, account, fingerprint, trust); return setIdentityKeyTrust(db, account, fingerprint, fingerprintStatus);
} }
private boolean setIdentityKeyTrust(SQLiteDatabase db, Account account, String fingerprint, XmppAxolotlSession.Trust trust) { private boolean setIdentityKeyTrust(SQLiteDatabase db, Account account, String fingerprint, FingerprintStatus status) {
String[] selectionArgs = { String[] selectionArgs = {
account.getUuid(), account.getUuid(),
fingerprint fingerprint
}; };
ContentValues values = new ContentValues(); int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, status.toContentValues(),
values.put(SQLiteAxolotlStore.TRUSTED, trust.getCode());
int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values,
SQLiteAxolotlStore.ACCOUNT + " = ? AND " SQLiteAxolotlStore.ACCOUNT + " = ? AND "
+ SQLiteAxolotlStore.FINGERPRINT + " = ? ", + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
selectionArgs); selectionArgs);
@ -1194,7 +1232,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
} }
public void storeOwnIdentityKeyPair(Account account, IdentityKeyPair identityKeyPair) { public void storeOwnIdentityKeyPair(Account account, IdentityKeyPair identityKeyPair) {
storeIdentityKey(account, account.getJid().toBareJid().toPreppedString(), true, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKeyPair.serialize(), Base64.DEFAULT), XmppAxolotlSession.Trust.TRUSTED); storeIdentityKey(account, account.getJid().toBareJid().toPreppedString(), true, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKeyPair.serialize(), Base64.DEFAULT), FingerprintStatus.createActiveVerified(false));
} }

View File

@ -39,6 +39,7 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
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.XmppAxolotlSession; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
@ -509,8 +510,8 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
} }
private void onOmemoKeyClicked(Account account, String fingerprint) { private void onOmemoKeyClicked(Account account, String fingerprint) {
final XmppAxolotlSession.Trust trust = account.getAxolotlService().getFingerprintTrust(fingerprint); FingerprintStatus status = account.getAxolotlService().getFingerprintTrust(fingerprint);
if (Config.X509_VERIFICATION && trust != null && trust == XmppAxolotlSession.Trust.TRUSTED_X509) { if (Config.X509_VERIFICATION && status != null && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509) {
X509Certificate x509Certificate = account.getAxolotlService().getFingerprintCertificate(fingerprint); X509Certificate x509Certificate = account.getAxolotlService().getFingerprintCertificate(fingerprint);
if (x509Certificate != null) { if (x509Certificate != null) {
showCertificateInformationDialog(CryptoHelper.extractCertificateInformation(x509Certificate)); showCertificateInformationDialog(CryptoHelper.extractCertificateInformation(x509Certificate));

View File

@ -49,6 +49,7 @@ import de.timroes.android.listview.EnhancedListView;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
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.XmppAxolotlSession; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Blockable; import eu.siacs.conversations.entities.Blockable;
@ -1677,8 +1678,8 @@ public class ConversationActivity extends XmppActivity
AxolotlService axolotlService = mSelectedConversation.getAccount().getAxolotlService(); AxolotlService axolotlService = mSelectedConversation.getAccount().getAxolotlService();
final List<Jid> targets = axolotlService.getCryptoTargets(mSelectedConversation); final List<Jid> targets = axolotlService.getCryptoTargets(mSelectedConversation);
boolean hasUnaccepted = !mSelectedConversation.getAcceptedCryptoTargets().containsAll(targets); boolean hasUnaccepted = !mSelectedConversation.getAcceptedCryptoTargets().containsAll(targets);
boolean hasUndecidedOwn = !axolotlService.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED).isEmpty(); boolean hasUndecidedOwn = !axolotlService.getKeysWithTrust(FingerprintStatus.createActiveUndecided()).isEmpty();
boolean hasUndecidedContacts = !axolotlService.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, targets).isEmpty(); boolean hasUndecidedContacts = !axolotlService.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), targets).isEmpty();
boolean hasPendingKeys = !axolotlService.findDevicesWithoutSession(mSelectedConversation).isEmpty(); boolean hasPendingKeys = !axolotlService.findDevicesWithoutSession(mSelectedConversation).isEmpty();
boolean hasNoTrustedKeys = axolotlService.anyTargetHasNoTrustedKeys(targets); boolean hasNoTrustedKeys = axolotlService.anyTargetHasNoTrustedKeys(targets);
if(hasUndecidedOwn || hasUndecidedContacts || hasPendingKeys || hasNoTrustedKeys || hasUnaccepted) { if(hasUndecidedOwn || hasUndecidedContacts || hasPendingKeys || hasNoTrustedKeys || hasUnaccepted) {

View File

@ -20,6 +20,7 @@ import java.util.Set;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
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.XmppAxolotlSession; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
@ -108,7 +109,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
for(final String fingerprint : ownKeysToTrust.keySet()) { for(final String fingerprint : ownKeysToTrust.keySet()) {
hasOwnKeys = true; hasOwnKeys = true;
addFingerprintRowWithListeners(ownKeys, mAccount, fingerprint, false, addFingerprintRowWithListeners(ownKeys, mAccount, fingerprint, false,
XmppAxolotlSession.Trust.fromBoolean(ownKeysToTrust.get(fingerprint)), false, FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)), false,
new CompoundButton.OnCheckedChangeListener() { new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
@ -133,7 +134,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
final Map<String, Boolean> fingerprints = entry.getValue(); final Map<String, Boolean> fingerprints = entry.getValue();
for (final String fingerprint : fingerprints.keySet()) { for (final String fingerprint : fingerprints.keySet()) {
addFingerprintRowWithListeners(keysContainer, mAccount, fingerprint, false, addFingerprintRowWithListeners(keysContainer, mAccount, fingerprint, false,
XmppAxolotlSession.Trust.fromBoolean(fingerprints.get(fingerprint)), false, FingerprintStatus.createActive(fingerprints.get(fingerprint)), false,
new CompoundButton.OnCheckedChangeListener() { new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
@ -184,7 +185,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets(); List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets();
ownKeysToTrust.clear(); ownKeysToTrust.clear();
AxolotlService service = this.mAccount.getAxolotlService(); AxolotlService service = this.mAccount.getAxolotlService();
Set<IdentityKey> ownKeysSet = service.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED); Set<IdentityKey> ownKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided());
for(final IdentityKey identityKey : ownKeysSet) { for(final IdentityKey identityKey : ownKeysSet) {
if(!ownKeysToTrust.containsKey(identityKey)) { if(!ownKeysToTrust.containsKey(identityKey)) {
ownKeysToTrust.put(identityKey.getFingerprint().replaceAll("\\s", ""), false); ownKeysToTrust.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
@ -193,9 +194,9 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
synchronized (this.foreignKeysToTrust) { synchronized (this.foreignKeysToTrust) {
foreignKeysToTrust.clear(); foreignKeysToTrust.clear();
for (Jid jid : contactJids) { for (Jid jid : contactJids) {
Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, jid); Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), jid);
if (hasNoOtherTrustedKeys(jid) && ownKeysSet.size() == 0) { if (hasNoOtherTrustedKeys(jid) && ownKeysSet.size() == 0) {
foreignKeysSet.addAll(service.getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED, jid)); foreignKeysSet.addAll(service.getKeysWithTrust(FingerprintStatus.createActive(false), jid));
} }
Map<String, Boolean> foreignFingerprints = new HashMap<>(); Map<String, Boolean> foreignFingerprints = new HashMap<>();
for (final IdentityKey identityKey : foreignKeysSet) { for (final IdentityKey identityKey : foreignKeysSet) {
@ -280,7 +281,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
for(final String fingerprint :ownKeysToTrust.keySet()) { for(final String fingerprint :ownKeysToTrust.keySet()) {
mAccount.getAxolotlService().setFingerprintTrust( mAccount.getAxolotlService().setFingerprintTrust(
fingerprint, fingerprint,
XmppAxolotlSession.Trust.fromBoolean(ownKeysToTrust.get(fingerprint))); FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)));
} }
List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets(); List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets();
synchronized (this.foreignKeysToTrust) { synchronized (this.foreignKeysToTrust) {
@ -293,7 +294,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
for (final String fingerprint : value.keySet()) { for (final String fingerprint : value.keySet()) {
mAccount.getAxolotlService().setFingerprintTrust( mAccount.getAxolotlService().setFingerprintTrust(
fingerprint, fingerprint,
XmppAxolotlSession.Trust.fromBoolean(value.get(fingerprint))); FingerprintStatus.createActive(value.get(fingerprint)));
} }
} }
} }

View File

@ -78,6 +78,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
@ -780,25 +781,21 @@ public abstract class XmppActivity extends Activity {
} }
protected boolean addFingerprintRow(LinearLayout keys, final Account account, final String fingerprint, boolean highlight, View.OnClickListener onKeyClickedListener) { protected boolean addFingerprintRow(LinearLayout keys, final Account account, final String fingerprint, boolean highlight, View.OnClickListener onKeyClickedListener) {
final XmppAxolotlSession.Trust trust = account.getAxolotlService() final FingerprintStatus status = account.getAxolotlService().getFingerprintTrust(fingerprint);
.getFingerprintTrust(fingerprint); if (status == null) {
if (trust == null) {
return false; return false;
} }
return addFingerprintRowWithListeners(keys, account, fingerprint, highlight, trust, true, return addFingerprintRowWithListeners(keys, account, fingerprint, highlight, status, true,
new CompoundButton.OnCheckedChangeListener() { new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
account.getAxolotlService().setFingerprintTrust(fingerprint, account.getAxolotlService().setFingerprintTrust(fingerprint,FingerprintStatus.createActive(isChecked));
(isChecked) ? XmppAxolotlSession.Trust.TRUSTED :
XmppAxolotlSession.Trust.UNTRUSTED);
} }
}, },
new View.OnClickListener() { new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
account.getAxolotlService().setFingerprintTrust(fingerprint, account.getAxolotlService().setFingerprintTrust(fingerprint,FingerprintStatus.createActive(true));
XmppAxolotlSession.Trust.UNTRUSTED);
v.setEnabled(true); v.setEnabled(true);
} }
}, },
@ -810,13 +807,13 @@ public abstract class XmppActivity extends Activity {
protected boolean addFingerprintRowWithListeners(LinearLayout keys, final Account account, protected boolean addFingerprintRowWithListeners(LinearLayout keys, final Account account,
final String fingerprint, final String fingerprint,
boolean highlight, boolean highlight,
XmppAxolotlSession.Trust trust, FingerprintStatus status,
boolean showTag, boolean showTag,
CompoundButton.OnCheckedChangeListener CompoundButton.OnCheckedChangeListener
onCheckedChangeListener, onCheckedChangeListener,
View.OnClickListener onClickListener, View.OnClickListener onClickListener,
View.OnClickListener onKeyClickedListener) { View.OnClickListener onKeyClickedListener) {
if (trust == XmppAxolotlSession.Trust.COMPROMISED) { if (status.isCompromised()) {
return false; return false;
} }
View view = getLayoutInflater().inflate(R.layout.contact_key, keys, false); View view = getLayoutInflater().inflate(R.layout.contact_key, keys, false);
@ -826,8 +823,6 @@ public abstract class XmppActivity extends Activity {
keyType.setOnClickListener(onKeyClickedListener); keyType.setOnClickListener(onKeyClickedListener);
Switch trustToggle = (Switch) view.findViewById(R.id.tgl_trust); Switch trustToggle = (Switch) view.findViewById(R.id.tgl_trust);
trustToggle.setVisibility(View.VISIBLE); trustToggle.setVisibility(View.VISIBLE);
trustToggle.setOnCheckedChangeListener(onCheckedChangeListener);
trustToggle.setOnClickListener(onClickListener);
final View.OnLongClickListener purge = new View.OnLongClickListener() { final View.OnLongClickListener purge = new View.OnLongClickListener() {
@Override @Override
public boolean onLongClick(View v) { public boolean onLongClick(View v) {
@ -835,50 +830,46 @@ public abstract class XmppActivity extends Activity {
return true; return true;
} }
}; };
boolean active = true;
view.setOnLongClickListener(purge); view.setOnLongClickListener(purge);
key.setOnLongClickListener(purge); key.setOnLongClickListener(purge);
keyType.setOnLongClickListener(purge); keyType.setOnLongClickListener(purge);
boolean x509 = Config.X509_VERIFICATION boolean x509 = Config.X509_VERIFICATION && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509;
&& (trust == XmppAxolotlSession.Trust.TRUSTED_X509 || trust == XmppAxolotlSession.Trust.INACTIVE_TRUSTED_X509); final View.OnClickListener toast;
switch (trust) { if (status.isActive()) {
case UNTRUSTED: key.setTextColor(getPrimaryTextColor());
case TRUSTED: keyType.setTextColor(getSecondaryTextColor());
case TRUSTED_X509: trustToggle.setOnCheckedChangeListener(onCheckedChangeListener);
trustToggle.setChecked(trust.trusted(), false); if (status.getTrust() == FingerprintStatus.Trust.UNDECIDED) {
trustToggle.setEnabled(!Config.X509_VERIFICATION || trust != XmppAxolotlSession.Trust.TRUSTED_X509); trustToggle.setOnClickListener(onClickListener);
if (Config.X509_VERIFICATION && trust == XmppAxolotlSession.Trust.TRUSTED_X509) { trustToggle.setEnabled(false);
trustToggle.setOnClickListener(null); } else {
trustToggle.setOnClickListener(null);
trustToggle.setChecked(status.isTrusted(), false);
trustToggle.setEnabled(true);
}
toast = new View.OnClickListener() {
@Override
public void onClick(View v) {
hideToast();
} }
key.setTextColor(getPrimaryTextColor()); };
keyType.setTextColor(getSecondaryTextColor()); } else {
break; key.setTextColor(getTertiaryTextColor());
case UNDECIDED: keyType.setTextColor(getTertiaryTextColor());
trustToggle.setChecked(false, false); trustToggle.setOnClickListener(null);
trustToggle.setEnabled(false); trustToggle.setEnabled(false);
key.setTextColor(getPrimaryTextColor()); trustToggle.setChecked(status.isTrusted(), false);
keyType.setTextColor(getSecondaryTextColor()); toast = new View.OnClickListener() {
break; @Override
case INACTIVE_UNTRUSTED: public void onClick(View v) {
case INACTIVE_UNDECIDED: replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
trustToggle.setOnClickListener(null); }
trustToggle.setChecked(false, false); };
trustToggle.setEnabled(false); trustToggle.setOnClickListener(toast);
key.setTextColor(getTertiaryTextColor());
keyType.setTextColor(getTertiaryTextColor());
active = false;
break;
case INACTIVE_TRUSTED:
case INACTIVE_TRUSTED_X509:
trustToggle.setOnClickListener(null);
trustToggle.setChecked(true, false);
trustToggle.setEnabled(false);
key.setTextColor(getTertiaryTextColor());
keyType.setTextColor(getTertiaryTextColor());
active = false;
break;
} }
view.setOnClickListener(toast);
key.setOnClickListener(toast);
keyType.setOnClickListener(toast);
if (showTag) { if (showTag) {
keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint)); keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
} else { } else {
@ -893,27 +884,6 @@ public abstract class XmppActivity extends Activity {
key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2))); key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)));
final View.OnClickListener toast;
if (!active) {
toast = new View.OnClickListener() {
@Override
public void onClick(View v) {
replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
}
};
trustToggle.setOnClickListener(toast);
} else {
toast = new View.OnClickListener() {
@Override
public void onClick(View v) {
hideToast();
}
};
}
view.setOnClickListener(toast);
key.setOnClickListener(toast);
keyType.setOnClickListener(toast);
keys.addView(view); keys.addView(view);
return true; return true;
} }

View File

@ -44,6 +44,7 @@ import java.util.regex.Pattern;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
@ -203,11 +204,11 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
viewHolder.indicator.setImageResource(darkBackground ? R.drawable.ic_lock_white_18dp : R.drawable.ic_lock_black_18dp); viewHolder.indicator.setImageResource(darkBackground ? R.drawable.ic_lock_white_18dp : R.drawable.ic_lock_black_18dp);
viewHolder.indicator.setVisibility(View.VISIBLE); viewHolder.indicator.setVisibility(View.VISIBLE);
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
XmppAxolotlSession.Trust trust = message.getConversation() FingerprintStatus status = message.getConversation()
.getAccount().getAxolotlService().getFingerprintTrust( .getAccount().getAxolotlService().getFingerprintTrust(
message.getFingerprint()); message.getFingerprint());
if(trust == null || (!trust.trusted() && !trust.trustedInactive())) { if(status == null || (!status.isTrustedAndActive())) {
viewHolder.indicator.setColorFilter(activity.getWarningTextColor()); viewHolder.indicator.setColorFilter(activity.getWarningTextColor());
viewHolder.indicator.setAlpha(1.0f); viewHolder.indicator.setAlpha(1.0f);
} else { } else {