From ab2d114bbc21a5c2d684f8760cb8e4cea54be5de Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Mon, 20 Jul 2015 22:18:24 +0200 Subject: Add purge axolotl key option Can now long-press a key to permanently purge it. --- .../crypto/axolotl/AxolotlService.java | 84 ++++++++++++++-------- .../crypto/axolotl/XmppAxolotlMessage.java | 7 +- .../conversations/ui/ContactDetailsActivity.java | 3 +- .../conversations/ui/EditAccountActivity.java | 3 +- .../eu/siacs/conversations/ui/XmppActivity.java | 37 ++++++++-- 5 files changed, 96 insertions(+), 38 deletions(-) (limited to 'src/main/java/eu') diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index 1fe455ff..6e28f111 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -100,7 +100,8 @@ public class AxolotlService { public enum Trust { UNDECIDED, // 0 TRUSTED, - UNTRUSTED; + UNTRUSTED, + COMPROMISED; public String toString() { switch(this){ @@ -514,41 +515,64 @@ public class AxolotlService { return fingerprint; } + private SQLiteAxolotlStore.Trust getTrust() { + return sqLiteAxolotlStore.getFingerprintTrust(fingerprint); + } + + @Nullable public byte[] processReceiving(XmppAxolotlMessage.XmppAxolotlMessageHeader incomingHeader) { byte[] plaintext = null; - try { - try { - PreKeyWhisperMessage message = new PreKeyWhisperMessage(incomingHeader.getContents()); - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account)+"PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId()); - String fingerprint = message.getIdentityKey().getFingerprint().replaceAll("\\s", ""); - if (this.fingerprint != null && !this.fingerprint.equals(fingerprint)) { - Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Had session with fingerprint "+ this.fingerprint+", received message with fingerprint "+fingerprint); - } else { - this.fingerprint = fingerprint; - plaintext = cipher.decrypt(message); - if (message.getPreKeyId().isPresent()) { - preKeyId = message.getPreKeyId().get(); + switch (getTrust()) { + case UNDECIDED: + case TRUSTED: + try { + try { + PreKeyWhisperMessage message = new PreKeyWhisperMessage(incomingHeader.getContents()); + Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account)+"PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId()); + String fingerprint = message.getIdentityKey().getFingerprint().replaceAll("\\s", ""); + if (this.fingerprint != null && !this.fingerprint.equals(fingerprint)) { + Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Had session with fingerprint "+ this.fingerprint+", received message with fingerprint "+fingerprint); + } else { + this.fingerprint = fingerprint; + plaintext = cipher.decrypt(message); + if (message.getPreKeyId().isPresent()) { + preKeyId = message.getPreKeyId().get(); + } + } + } catch (InvalidMessageException | InvalidVersionException e) { + Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account)+"WhisperMessage received"); + WhisperMessage message = new WhisperMessage(incomingHeader.getContents()); + 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.w(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Error decrypting axolotl header, "+e.getClass().getName()+": " + e.getMessage()); } - } catch (InvalidMessageException | InvalidVersionException e) { - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account)+"WhisperMessage received"); - WhisperMessage message = new WhisperMessage(incomingHeader.getContents()); - 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.w(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Error decrypting axolotl header, "+e.getClass().getName()+": " + e.getMessage()); + + break; + + case COMPROMISED: + case UNTRUSTED: + default: + // ignore + break; } return plaintext; } - public XmppAxolotlMessage.XmppAxolotlMessageHeader processSending(byte[] outgoingMessage) { - CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage); - XmppAxolotlMessage.XmppAxolotlMessageHeader header = - new XmppAxolotlMessage.XmppAxolotlMessageHeader(remoteAddress.getDeviceId(), - ciphertextMessage.serialize()); - return header; + @Nullable + public XmppAxolotlMessage.XmppAxolotlMessageHeader processSending(@NonNull byte[] outgoingMessage) { + SQLiteAxolotlStore.Trust trust = getTrust(); + if (trust == SQLiteAxolotlStore.Trust.TRUSTED) { + CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage); + XmppAxolotlMessage.XmppAxolotlMessageHeader header = + new XmppAxolotlMessage.XmppAxolotlMessageHeader(remoteAddress.getDeviceId(), + ciphertextMessage.serialize()); + return header; + } else { + return null; + } } } @@ -742,6 +766,10 @@ public class AxolotlService { }); } + public void purgeKey(IdentityKey identityKey) { + axolotlStore.setFingerprintTrust(identityKey.getFingerprint().replaceAll("\\s",""), SQLiteAxolotlStore.Trust.COMPROMISED); + } + public void publishOwnDeviceIdIfNeeded() { IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveDeviceIds(account.getJid().toBareJid()); mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java index 1378c94a..ec068ec7 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.crypto.axolotl; +import android.support.annotation.Nullable; import android.util.Base64; import java.security.InvalidAlgorithmParameterException; @@ -145,8 +146,10 @@ public class XmppAxolotlMessage { return headers; } - public void addHeader(XmppAxolotlMessageHeader header) { - headers.add(header); + public void addHeader(@Nullable XmppAxolotlMessageHeader header) { + if (header != null) { + headers.add(header); + } } public byte[] getInnerKey(){ diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index cc2ef27c..16e16cff 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -384,8 +384,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } for(final IdentityKey identityKey : xmppConnectionService.databaseBackend.loadIdentityKeys( contact.getAccount(), contact.getJid().toBareJid().toString())) { - hasKeys = true; - addFingerprintRow(keys, contact.getAccount(), identityKey); + hasKeys |= addFingerprintRow(keys, contact.getAccount(), identityKey); } if (contact.getPgpKeyId() != 0) { hasKeys = true; diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index e7b51329..c9e70082 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -566,8 +566,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate if(ownKey.equals(identityKey)) { continue; } - hasKeys = true; - addFingerprintRow(keys, mAccount, identityKey); + hasKeys |= addFingerprintRow(keys, mAccount, identityKey); } if (hasKeys) { keysCard.setVisibility(View.VISIBLE); diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index 69357224..19783627 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -601,11 +601,11 @@ public abstract class XmppActivity extends Activity { builder.create().show(); } - protected void addFingerprintRow(LinearLayout keys, final Account account, IdentityKey identityKey) { + protected boolean addFingerprintRow(LinearLayout keys, final Account account, IdentityKey identityKey) { final String fingerprint = identityKey.getFingerprint().replaceAll("\\s", ""); final AxolotlService.SQLiteAxolotlStore.Trust trust = account.getAxolotlService() .getFingerprintTrust(fingerprint); - addFingerprintRowWithListeners(keys, account, identityKey, trust, true, + return addFingerprintRowWithListeners(keys, account, identityKey, trust, true, new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { @@ -633,13 +633,16 @@ public abstract class XmppActivity extends Activity { ); } - protected void addFingerprintRowWithListeners(LinearLayout keys, final Account account, - IdentityKey identityKey, + protected boolean addFingerprintRowWithListeners(LinearLayout keys, final Account account, + final IdentityKey identityKey, AxolotlService.SQLiteAxolotlStore.Trust trust, boolean showTag, CompoundButton.OnCheckedChangeListener onCheckedChangeListener, View.OnClickListener onClickListener) { + if (trust == AxolotlService.SQLiteAxolotlStore.Trust.COMPROMISED) { + return false; + } View view = getLayoutInflater().inflate(R.layout.contact_key, keys, false); TextView key = (TextView) view.findViewById(R.id.key); TextView keyType = (TextView) view.findViewById(R.id.key_type); @@ -647,6 +650,13 @@ public abstract class XmppActivity extends Activity { trustToggle.setVisibility(View.VISIBLE); trustToggle.setOnCheckedChangeListener(onCheckedChangeListener); trustToggle.setOnClickListener(onClickListener); + view.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + showPurgeKeyDialog(account, identityKey); + return true; + } + }); switch (trust) { case UNTRUSTED: @@ -668,7 +678,26 @@ public abstract class XmppActivity extends Activity { key.setText(CryptoHelper.prettifyFingerprint(identityKey.getFingerprint())); keys.addView(view); + return true; + } + public void showPurgeKeyDialog(final Account account, final IdentityKey identityKey) { + Builder builder = new Builder(this); + builder.setTitle(getString(R.string.purge_key)); + builder.setIconAttribute(android.R.attr.alertDialogIcon); + builder.setMessage(getString(R.string.purge_key_desc_part1) + + "\n\n" + CryptoHelper.prettifyFingerprint(identityKey.getFingerprint()) + + "\n\n" + getString(R.string.purge_key_desc_part2)); + builder.setNegativeButton(getString(R.string.cancel), null); + builder.setPositiveButton(getString(R.string.accept), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + account.getAxolotlService().purgeKey(identityKey); + refreshUi(); + } + }); + builder.create().show(); } public void selectPresence(final Conversation conversation, -- cgit v1.2.3