Add INACTIVE state for removed keys

We introduce a new trust state: INACTIVE. This state is intended for
old keys that have been removed.

When a TRUSTED device is removed from the PEP devicelist, it's status
will be set to INACTIVE. INACTIVE keys are shown in the UI as greyed
out, non-interactible key rows. Messages are not encrypted for INACTIVE
devices.

When an INACTIVE device reappears in PEP, or a message is received from
an INACTIVE device, it is set back to trusted.
This commit is contained in:
Andreas Straub 2015-07-21 14:18:16 +02:00
parent 3c5c0c7d3b
commit 92b5081b5e
9 changed files with 102 additions and 40 deletions

View file

@ -102,7 +102,8 @@ public class AxolotlService {
UNDECIDED(0), UNDECIDED(0),
TRUSTED(1), TRUSTED(1),
UNTRUSTED(2), UNTRUSTED(2),
COMPROMISED(3); COMPROMISED(3),
INACTIVE(4);
private static final Map<Integer, Trust> trustsByValue = new HashMap<>(); private static final Map<Integer, Trust> trustsByValue = new HashMap<>();
@ -125,12 +126,16 @@ public class AxolotlService {
public String toString() { public String toString() {
switch(this){ switch(this){
case UNDECIDED: case UNDECIDED:
return "Trust undecided"; return "Trust undecided "+getCode();
case TRUSTED: case TRUSTED:
return "Trusted"; return "Trusted "+getCode();
case COMPROMISED:
return "Compromised "+getCode();
case INACTIVE:
return "Inactive "+getCode();
case UNTRUSTED: case UNTRUSTED:
default: default:
return "Untrusted"; return "Untrusted "+getCode();
} }
} }
@ -538,14 +543,20 @@ public class AxolotlService {
return fingerprint; return fingerprint;
} }
private SQLiteAxolotlStore.Trust getTrust() { protected void setTrust(SQLiteAxolotlStore.Trust trust) {
sqLiteAxolotlStore.setFingerprintTrust(fingerprint, trust);
}
protected SQLiteAxolotlStore.Trust getTrust() {
return sqLiteAxolotlStore.getFingerprintTrust(fingerprint); return sqLiteAxolotlStore.getFingerprintTrust(fingerprint);
} }
@Nullable @Nullable
public byte[] processReceiving(XmppAxolotlMessage.XmppAxolotlMessageHeader incomingHeader) { public byte[] processReceiving(XmppAxolotlMessage.XmppAxolotlMessageHeader incomingHeader) {
byte[] plaintext = null; byte[] plaintext = null;
switch (getTrust()) { SQLiteAxolotlStore.Trust trust = getTrust();
switch (trust) {
case INACTIVE:
case UNDECIDED: case UNDECIDED:
case UNTRUSTED: case UNTRUSTED:
case TRUSTED: case TRUSTED:
@ -574,6 +585,10 @@ public class AxolotlService {
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());
} }
if (plaintext != null && trust == SQLiteAxolotlStore.Trust.INACTIVE) {
setTrust(SQLiteAxolotlStore.Trust.TRUSTED);
}
break; break;
case COMPROMISED: case COMPROMISED:
@ -774,15 +789,32 @@ public class AxolotlService {
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 SQLiteAxolotlStore.Trust from,
final SQLiteAxolotlStore.Trust to) {
for(Integer deviceId:deviceIds) {
AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toString(), 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(deviceIds.contains(getOwnDeviceId())) { if(deviceIds.contains(getOwnDeviceId())) {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Skipping own Device ID:"+ jid + ":"+getOwnDeviceId());
deviceIds.remove(getOwnDeviceId()); deviceIds.remove(getOwnDeviceId());
} }
for(Integer i:deviceIds) { Set<Integer> expiredDevices = new HashSet<>(axolotlStore.getSubDeviceSessions(jid.toBareJid().toString()));
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Adding Device ID:"+ jid + ":"+i); expiredDevices.removeAll(deviceIds);
} setTrustOnSessions(jid, expiredDevices, SQLiteAxolotlStore.Trust.TRUSTED,
SQLiteAxolotlStore.Trust.INACTIVE);
Set<Integer> newDevices = new HashSet<>(deviceIds);
setTrustOnSessions(jid, newDevices, SQLiteAxolotlStore.Trust.INACTIVE,
SQLiteAxolotlStore.Trust.TRUSTED);
this.deviceIds.put(jid, deviceIds); this.deviceIds.put(jid, deviceIds);
mXmppConnectionService.keyStatusUpdated();
publishOwnDeviceIdIfNeeded(); publishOwnDeviceIdIfNeeded();
} }
@ -957,7 +989,7 @@ public class AxolotlService {
} }
}); });
} }
mXmppConnectionService.newKeysAvailable(); mXmppConnectionService.keyStatusUpdated();
} }
} }

View file

@ -86,7 +86,7 @@ import eu.siacs.conversations.xmpp.OnContactStatusChanged;
import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.OnMessageAcknowledged; import eu.siacs.conversations.xmpp.OnMessageAcknowledged;
import eu.siacs.conversations.xmpp.OnMessagePacketReceived; import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
import eu.siacs.conversations.xmpp.OnNewKeysAvailable; import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.OnPresencePacketReceived; import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
import eu.siacs.conversations.xmpp.OnStatusChanged; import eu.siacs.conversations.xmpp.OnStatusChanged;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist; import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
@ -309,8 +309,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
private int rosterChangedListenerCount = 0; private int rosterChangedListenerCount = 0;
private OnMucRosterUpdate mOnMucRosterUpdate = null; private OnMucRosterUpdate mOnMucRosterUpdate = null;
private int mucRosterChangedListenerCount = 0; private int mucRosterChangedListenerCount = 0;
private OnNewKeysAvailable mOnNewKeysAvailable = null; private OnKeyStatusUpdated mOnKeyStatusUpdated = null;
private int newKeysAvailableListenerCount = 0; private int keyStatusUpdatedListenerCount = 0;
private SecureRandom mRandom; private SecureRandom mRandom;
private OpenPgpServiceConnection pgpServiceConnection; private OpenPgpServiceConnection pgpServiceConnection;
private PgpEngine mPgpEngine = null; private PgpEngine mPgpEngine = null;
@ -1372,30 +1372,31 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
} }
public void setOnNewKeysAvailableListener(final OnNewKeysAvailable listener) { public void setOnKeyStatusUpdatedListener(final OnKeyStatusUpdated listener) {
synchronized (this) { synchronized (this) {
if (checkListeners()) { if (checkListeners()) {
switchToForeground(); switchToForeground();
} }
this.mOnNewKeysAvailable = listener; this.mOnKeyStatusUpdated = listener;
if (this.newKeysAvailableListenerCount < 2) { if (this.keyStatusUpdatedListenerCount < 2) {
this.newKeysAvailableListenerCount++; this.keyStatusUpdatedListenerCount++;
} }
} }
} }
public void removeOnNewKeysAvailableListener() { public void removeOnNewKeysAvailableListener() {
synchronized (this) { synchronized (this) {
this.newKeysAvailableListenerCount--; this.keyStatusUpdatedListenerCount--;
if (this.newKeysAvailableListenerCount <= 0) { if (this.keyStatusUpdatedListenerCount <= 0) {
this.newKeysAvailableListenerCount = 0; this.keyStatusUpdatedListenerCount = 0;
this.mOnNewKeysAvailable = null; this.mOnKeyStatusUpdated = null;
if (checkListeners()) { if (checkListeners()) {
switchToBackground(); switchToBackground();
} }
} }
} }
} }
public void setOnMucRosterUpdateListener(OnMucRosterUpdate listener) { public void setOnMucRosterUpdateListener(OnMucRosterUpdate listener) {
synchronized (this) { synchronized (this) {
if (checkListeners()) { if (checkListeners()) {
@ -1427,7 +1428,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
&& this.mOnRosterUpdate == null && this.mOnRosterUpdate == null
&& this.mOnUpdateBlocklist == null && this.mOnUpdateBlocklist == null
&& this.mOnShowErrorToast == null && this.mOnShowErrorToast == null
&& this.mOnNewKeysAvailable == null); && this.mOnKeyStatusUpdated == null);
} }
private void switchToForeground() { private void switchToForeground() {
@ -2316,9 +2317,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
} }
public void newKeysAvailable() { public void keyStatusUpdated() {
if(mOnNewKeysAvailable != null) { if(mOnKeyStatusUpdated != null) {
mOnNewKeysAvailable.onNewKeysAvailable(); mOnKeyStatusUpdated.onKeyStatusUpdated();
} }
} }

View file

@ -42,12 +42,13 @@ import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist; import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.XmppConnection;
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;
public class ContactDetailsActivity extends XmppActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist { public class ContactDetailsActivity extends XmppActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist, OnKeyStatusUpdated {
public static final String ACTION_VIEW_CONTACT = "view_contact"; public static final String ACTION_VIEW_CONTACT = "view_contact";
private Contact contact; private Contact contact;
@ -468,4 +469,9 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
populateView(); populateView();
} }
} }
@Override
public void onKeyStatusUpdated() {
refreshUi();
}
} }

View file

@ -35,12 +35,13 @@ import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.XmppConnection.Features; import eu.siacs.conversations.xmpp.XmppConnection.Features;
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;
import eu.siacs.conversations.xmpp.pep.Avatar; import eu.siacs.conversations.xmpp.pep.Avatar;
public class EditAccountActivity extends XmppActivity implements OnAccountUpdate{ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate, OnKeyStatusUpdated {
private AutoCompleteTextView mAccountJid; private AutoCompleteTextView mAccountJid;
private EditText mPassword; private EditText mPassword;
@ -618,4 +619,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}); });
builder.create().show(); builder.create().show();
} }
@Override
public void onKeyStatusUpdated() {
refreshUi();
}
} }

View file

@ -20,11 +20,11 @@ import eu.siacs.conversations.crypto.axolotl.AxolotlService.SQLiteAxolotlStore.T
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;
import eu.siacs.conversations.xmpp.OnNewKeysAvailable; import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
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;
public class TrustKeysActivity extends XmppActivity implements OnNewKeysAvailable { public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdated {
private Jid accountJid; private Jid accountJid;
private Jid contactJid; private Jid contactJid;
private boolean hasOtherTrustedKeys = false; private boolean hasOtherTrustedKeys = false;
@ -215,7 +215,7 @@ public class TrustKeysActivity extends XmppActivity implements OnNewKeysAvailabl
} }
@Override @Override
public void onNewKeysAvailable() { public void onKeyStatusUpdated() {
final Account account = xmppConnectionService.findAccountByJid(accountJid); final Account account = xmppConnectionService.findAccountByJid(accountJid);
hasPendingFetches = false; hasPendingFetches = false;
getFingerprints(account); getFingerprints(account);

View file

@ -83,7 +83,7 @@ import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinde
import eu.siacs.conversations.ui.widget.Switch; import eu.siacs.conversations.ui.widget.Switch;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.xmpp.OnNewKeysAvailable; import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist; import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
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;
@ -99,6 +99,7 @@ public abstract class XmppActivity extends Activity {
protected int mPrimaryTextColor; protected int mPrimaryTextColor;
protected int mSecondaryTextColor; protected int mSecondaryTextColor;
protected int mTertiaryTextColor;
protected int mPrimaryBackgroundColor; protected int mPrimaryBackgroundColor;
protected int mSecondaryBackgroundColor; protected int mSecondaryBackgroundColor;
protected int mColorRed; protected int mColorRed;
@ -294,8 +295,8 @@ public abstract class XmppActivity extends Activity {
if (this instanceof XmppConnectionService.OnShowErrorToast) { if (this instanceof XmppConnectionService.OnShowErrorToast) {
this.xmppConnectionService.setOnShowErrorToastListener((XmppConnectionService.OnShowErrorToast) this); this.xmppConnectionService.setOnShowErrorToastListener((XmppConnectionService.OnShowErrorToast) this);
} }
if (this instanceof OnNewKeysAvailable) { if (this instanceof OnKeyStatusUpdated) {
this.xmppConnectionService.setOnNewKeysAvailableListener((OnNewKeysAvailable) this); this.xmppConnectionService.setOnKeyStatusUpdatedListener((OnKeyStatusUpdated) this);
} }
} }
@ -318,7 +319,7 @@ public abstract class XmppActivity extends Activity {
if (this instanceof XmppConnectionService.OnShowErrorToast) { if (this instanceof XmppConnectionService.OnShowErrorToast) {
this.xmppConnectionService.removeOnShowErrorToastListener(); this.xmppConnectionService.removeOnShowErrorToastListener();
} }
if (this instanceof OnNewKeysAvailable) { if (this instanceof OnKeyStatusUpdated) {
this.xmppConnectionService.removeOnNewKeysAvailableListener(); this.xmppConnectionService.removeOnNewKeysAvailableListener();
} }
} }
@ -349,6 +350,7 @@ public abstract class XmppActivity extends Activity {
ExceptionHelper.init(getApplicationContext()); ExceptionHelper.init(getApplicationContext());
mPrimaryTextColor = getResources().getColor(R.color.black87); mPrimaryTextColor = getResources().getColor(R.color.black87);
mSecondaryTextColor = getResources().getColor(R.color.black54); mSecondaryTextColor = getResources().getColor(R.color.black54);
mTertiaryTextColor = getResources().getColor(R.color.black12);
mColorRed = getResources().getColor(R.color.red500); mColorRed = getResources().getColor(R.color.red500);
mColorOrange = getResources().getColor(R.color.orange500); mColorOrange = getResources().getColor(R.color.orange500);
mColorGreen = getResources().getColor(R.color.green500); mColorGreen = getResources().getColor(R.color.green500);
@ -668,10 +670,20 @@ public abstract class XmppActivity extends Activity {
case TRUSTED: case TRUSTED:
trustToggle.setChecked(trust == AxolotlService.SQLiteAxolotlStore.Trust.TRUSTED, false); trustToggle.setChecked(trust == AxolotlService.SQLiteAxolotlStore.Trust.TRUSTED, false);
trustToggle.setEnabled(true); trustToggle.setEnabled(true);
key.setTextColor(getPrimaryTextColor());
keyType.setTextColor(getSecondaryTextColor());
break; break;
case UNDECIDED: case UNDECIDED:
trustToggle.setChecked(false, false); trustToggle.setChecked(false, false);
trustToggle.setEnabled(false); trustToggle.setEnabled(false);
key.setTextColor(getPrimaryTextColor());
keyType.setTextColor(getSecondaryTextColor());
break;
case INACTIVE:
trustToggle.setChecked(true, false);
trustToggle.setEnabled(false);
key.setTextColor(getTertiaryTextColor());
keyType.setTextColor(getTertiaryTextColor());
break; break;
} }
@ -824,6 +836,10 @@ public abstract class XmppActivity extends Activity {
} }
}; };
public int getTertiaryTextColor() {
return this.mTertiaryTextColor;
}
public int getSecondaryTextColor() { public int getSecondaryTextColor() {
return this.mSecondaryTextColor; return this.mSecondaryTextColor;
} }

View file

@ -0,0 +1,5 @@
package eu.siacs.conversations.xmpp;
public interface OnKeyStatusUpdated {
public void onKeyStatusUpdated();
}

View file

@ -1,5 +0,0 @@
package eu.siacs.conversations.xmpp;
public interface OnNewKeysAvailable {
public void onNewKeysAvailable();
}

View file

@ -5,6 +5,7 @@
<color name="accent">#ff0091ea</color> <color name="accent">#ff0091ea</color>
<color name="black87">#de000000</color> <color name="black87">#de000000</color>
<color name="black54">#8a000000</color> <color name="black54">#8a000000</color>
<color name="black26">#42000000</color>
<color name="black12">#1f000000</color> <color name="black12">#1f000000</color>
<color name="white">#ffffffff</color> <color name="white">#ffffffff</color>
<color name="white70">#b2ffffff</color> <color name="white70">#b2ffffff</color>