Switch to a more heavily TOFU model for identity keys.

1) There is no longer a concept of "verified" or "unverified."
   Only "what we saw last time" and "different from last time."

2) Let's eliminate "verify session," since we're all about
   identity keys now.

3) Mark manually processed key exchanges as processed.
This commit is contained in:
Moxie Marlinspike
2013-05-23 16:36:24 -07:00
parent ef7977128b
commit 24fc93e9ae
33 changed files with 497 additions and 1019 deletions

View File

@@ -16,14 +16,19 @@
*/
package org.thoughtcrime.securesms;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.thoughtcrime.securesms.crypto.IdentityKey;
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
import org.thoughtcrime.securesms.crypto.InvalidVersionException;
import org.thoughtcrime.securesms.crypto.KeyExchangeMessage;
@@ -42,36 +47,32 @@ import org.thoughtcrime.securesms.util.MemoryCleaner;
public class ReceiveKeyActivity extends PassphraseRequiredSherlockActivity {
private TextView descriptionText;
private TextView signatureText;
private Button confirmButton;
private Button cancelButton;
private Button verifySessionButton;
private Button verifyIdentityButton;
private Recipient recipient;
private long threadId;
private long messageId;
private MasterSecret masterSecret;
private KeyExchangeMessage keyExchangeMessage;
private KeyExchangeProcessor keyExchangeProcessor;
private boolean sent;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.receive_key_activity);
initializeResources();
try {
initializeKey();
initializeText();
} catch (InvalidKeyException ike) {
Log.w("ReceiveKeyActivity", ike);
initializeCorruptedKeyText();
} catch (InvalidVersionException ive) {
initializeBadVersionText();
Log.w("ReceiveKeyActivity", ive);
}
initializeListeners();
}
@@ -83,64 +84,20 @@ public class ReceiveKeyActivity extends PassphraseRequiredSherlockActivity {
}
private void initializeText() {
if (keyExchangeProcessor.hasCompletedSession()) initializeTextForExistingSession();
else initializeTextForNewSession();
SpannableString spannableString = new SpannableString(descriptionText.getText() + " " +
getString(R.string.ReceiveKeyActivity_you_may_wish_to_verify_this_contact));
spannableString.setSpan(new ClickableSpan() {
@Override
public void onClick(View widget) {
Intent intent = new Intent(ReceiveKeyActivity.this, VerifyIdentityActivity.class);
intent.putExtra("recipient", recipient);
intent.putExtra("master_secret", masterSecret);
startActivity(intent);
}
}, descriptionText.getText().length()+1, spannableString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
initializeSignatureText();
}
private void initializeCorruptedKeyText() {
descriptionText.setText(R.string.ReceiveKeyActivity_error_you_have_received_a_corrupted_public_key);
confirmButton.setVisibility(View.GONE);
}
private void initializeBadVersionText() {
descriptionText.setText(R.string.ReceiveKeyActivity_error_you_have_received_a_public_key_from_an_unsupported_version_of_the_protocol);
confirmButton.setVisibility(View.GONE);
}
private void initializeSignatureText() {
if (!keyExchangeMessage.hasIdentityKey()) {
signatureText.setText(R.string.ReceiveKeyActivity_this_key_exchange_message_does_not_include_an_identity_signature);
return;
}
IdentityKey identityKey = keyExchangeMessage.getIdentityKey();
String identityName = DatabaseFactory.getIdentityDatabase(this).getNameForIdentity(masterSecret, identityKey);
if (identityName == null) {
signatureText.setText(R.string.ReceiveKeyActivity_this_key_exchange_message_includes_an_identity_signature_but_you_do_not_yet_trust_it);
} else {
signatureText.setText(String.format(getString(R.string.ReceiveKeyActivity_this_key_exchange_message_includes_an_identity_signature_which_you_trust_for_s), identityName));
}
}
private void initializeTextForExistingSession() {
if (keyExchangeProcessor.isRemoteKeyExchangeForExistingSession(keyExchangeMessage)) {
descriptionText.setText(String.format(getString(R.string.ReceiveKeyActivity_this_is_the_key_that_you_sent_to_start_your_current_encrypted_session_with_s), recipient.toShortString()));
this.confirmButton.setVisibility(View.GONE);
this.verifySessionButton.setVisibility(View.VISIBLE);
this.verifyIdentityButton.setVisibility(View.VISIBLE);
} else if (keyExchangeProcessor.isLocalKeyExchangeForExistingSession(keyExchangeMessage)) {
descriptionText.setText(String.format(getString(R.string.ReceiveKeyActivity_this_is_the_key_that_you_received_to_start_your_current_encrypted_session_with_s), recipient.toShortString()));
this.confirmButton.setVisibility(View.GONE);
this.verifySessionButton.setVisibility(View.VISIBLE);
this.verifyIdentityButton.setVisibility(View.VISIBLE);
} else {
descriptionText.setText(String.format(getString(R.string.ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_warning_you_already_have_an_encrypted_session), recipient.toShortString()));
this.confirmButton.setVisibility(View.VISIBLE);
this.verifyIdentityButton.setVisibility(View.GONE);
this.verifySessionButton.setVisibility(View.GONE);
}
}
private void initializeTextForNewSession() {
if (keyExchangeProcessor.hasInitiatedSession() && !this.sent)
descriptionText.setText(String.format(getString(R.string.ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_you_have_previously_initiated), recipient.toShortString()));
else if (keyExchangeProcessor.hasInitiatedSession() && this.sent)
descriptionText.setText(String.format(getString(R.string.ReceiveKeyActivity_you_have_initiated_a_key_exchange_message_with_s_but_have_not_yet_received_a_reply), recipient.toShortString()));
else if (!keyExchangeProcessor.hasInitiatedSession() && !this.sent)
descriptionText.setText(String.format(getString(R.string.ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_you_have_no_existing_session), recipient.toShortString()));
descriptionText.setText(spannableString);
descriptionText.setMovementMethod(LinkMovementMethod.getInstance());
}
private void initializeKey() throws InvalidKeyException, InvalidVersionException {
@@ -150,52 +107,46 @@ public class ReceiveKeyActivity extends PassphraseRequiredSherlockActivity {
private void initializeResources() {
this.descriptionText = (TextView) findViewById(R.id.description_text);
this.signatureText = (TextView) findViewById(R.id.signature_text);
this.confirmButton = (Button) findViewById(R.id.ok_button);
this.cancelButton = (Button) findViewById(R.id.cancel_button);
this.verifyIdentityButton = (Button) findViewById(R.id.verify_identity_button);
this.verifySessionButton = (Button) findViewById(R.id.verify_session_button);
this.recipient = getIntent().getParcelableExtra("recipient");
this.threadId = getIntent().getLongExtra("thread_id", -1);
this.messageId = getIntent().getLongExtra("message_id", -1);
this.masterSecret = (MasterSecret)getIntent().getParcelableExtra("master_secret");
this.sent = getIntent().getBooleanExtra("sent", false);
this.keyExchangeProcessor = new KeyExchangeProcessor(this, masterSecret, recipient);
}
private void initializeListeners() {
this.confirmButton.setOnClickListener(new OkListener());
this.cancelButton.setOnClickListener(new CancelListener());
this.verifyIdentityButton.setOnClickListener(new VerifyIdentityListener());
this.verifySessionButton.setOnClickListener(new VerifySessionListener());
}
private class VerifyIdentityListener implements View.OnClickListener {
@Override
public void onClick(View v) {
Intent intent = new Intent(ReceiveKeyActivity.this, VerifyIdentityActivity.class);
intent.putExtra("recipient", recipient);
intent.putExtra("master_secret", masterSecret);
startActivity(intent);
finish();
}
}
private class VerifySessionListener implements View.OnClickListener {
@Override
public void onClick(View v) {
Intent intent = new Intent(ReceiveKeyActivity.this, VerifyKeysActivity.class);
intent.putExtra("recipient", recipient);
intent.putExtra("master_secret", masterSecret);
startActivity(intent);
finish();
}
}
private class OkListener implements View.OnClickListener {
@Override
public void onClick(View v) {
keyExchangeProcessor.processKeyExchangeMessage(keyExchangeMessage, threadId);
finish();
new AsyncTask<Void, Void, Void> () {
private ProgressDialog dialog;
@Override
protected void onPreExecute() {
dialog = ProgressDialog.show(ReceiveKeyActivity.this, "Processing",
"Processing key exchange...", true);
}
@Override
protected Void doInBackground(Void... params) {
keyExchangeProcessor.processKeyExchangeMessage(keyExchangeMessage, threadId);
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
.markAsProcessedKeyExchange(messageId);
return null;
}
@Override
protected void onPostExecute(Void result) {
dialog.dismiss();
finish();
}
}.execute();
}
}
@@ -205,5 +156,4 @@ public class ReceiveKeyActivity extends PassphraseRequiredSherlockActivity {
ReceiveKeyActivity.this.finish();
}
}
}