Commit 16ac76fe authored by agrajaghh's avatar agrajaghh Committed by Bastien Le Querrec

use ConfirmIdentityDialog instead of ReceiveKeyDialog

also switch to AppCompatDialog

Upstream commit: https://github.com/signalapp/Signal-Android/commit/72bd6d5844590f7087f36fafaf582acfa63a5f3f
parent 9d2351cc
Pipeline #162 passed with stages
in 45 minutes and 10 seconds
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView style="@style/Registration.Description"
android:id="@+id/description_text"
android:layout_width="fill_parent"
android:layout_marginBottom="16dip"
android:layout_marginTop="16dip"/>
</FrameLayout>
......@@ -325,19 +325,14 @@
<string name="PassphrasePromptActivity_watermark_content_description">Silence icon</string>
<string name="PassphrasePromptActivity_invalid_passphrase_exclamation">Invalid passphrase!</string>
<!-- ReceiveKeyActivity -->
<string name="ReceiveKeyActivity_the_signature_on_this_key_exchange_is_different">The
signature on this key exchange is different than what you\'ve previously received from this
contact. This could either mean that someone is trying to intercept your communication, or
that this contact simply re-installed Silence and now has a new identity key.
<!-- ConfirmIdentityDialog -->
<string name="ConfirmIdentityDialog_the_signature_on_this_key_exchange_is_different">The
identifying key material for %1$s has changed. This could either mean that someone is trying to
intercept your communication, or that %2$s simply re-installed Silence and now has a new
identity key.
</string>
<string name="ReceiveKeyActivity_the_signature_on_this_key_exchange_is_trusted_but">The
signature on this key exchange is trusted, but you have the \'automatically complete key
exchanges\' setting disabled.
</string>
<string name="ReceiveKeyActivity_processing">Processing</string>
<string name="ReceiveKeyActivity_processing_key_exchange">Processing key exchange…</string>
<string name="ConfirmIdentityDialog_you_may_wish_to_verify_this_contact">You should verify the identity of this contact.</string>
<string name="ConfirmIdentityDialog_you_may_wish_to_verify_this_contact">You may wish to verify this contact.</string>
<string name="ConfirmIdentityDialog_accept">Accept</string>
<!-- RecipientPreferencesActivity -->
<string name="RecipientPreferenceActivity_block_this_contact_question">Block this contact?</string>
......@@ -603,9 +598,6 @@
<string name="recipient_preferences__color">Color</string>
<string name="recipient_preferences__color_for_this_contact">Color for this contact</string>
<!-- receive_key_dialog -->
<string name="receive_key_activity__complete">Complete</string>
<!-- recipients_panel -->
<string name="recipients_panel__to"><small>Enter a name or number</small></string>
<string name="recipients_panel__add_member">Add member</string>
......
package org.smssecure.smssecure;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.os.AsyncTask;
import android.support.v7.app.AlertDialog;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.View;
import android.widget.TextView;
import org.smssecure.smssecure.crypto.IdentityKeyParcelable;
import org.smssecure.smssecure.crypto.MasterSecret;
import org.smssecure.smssecure.database.DatabaseFactory;
import org.smssecure.smssecure.database.IdentityDatabase;
import org.smssecure.smssecure.database.MmsAddressDatabase;
import org.smssecure.smssecure.database.MmsDatabase;
import org.smssecure.smssecure.database.MmsSmsDatabase;
import org.smssecure.smssecure.database.SmsDatabase;
import org.smssecure.smssecure.database.documents.IdentityKeyMismatch;
import org.smssecure.smssecure.database.model.MessageRecord;
import org.smssecure.smssecure.jobs.SmsDecryptJob;
import org.smssecure.smssecure.recipients.Recipient;
import org.smssecure.smssecure.recipients.RecipientFactory;
import org.smssecure.smssecure.recipients.Recipients;
import org.smssecure.smssecure.sms.MessageSender;
import org.smssecure.smssecure.util.Base64;
import java.io.IOException;
public class ConfirmIdentityDialog extends AlertDialog {
private static final String TAG = ConfirmIdentityDialog.class.getSimpleName();
private OnClickListener callback;
public ConfirmIdentityDialog(Context context,
MasterSecret masterSecret,
MessageRecord messageRecord,
IdentityKeyMismatch mismatch)
{
super(context);
Recipient recipient = RecipientFactory.getRecipientForId(context, mismatch.getRecipientId(), false);
String name = recipient.toShortString();
String introduction = String.format(context.getString(R.string.ConfirmIdentityDialog_the_signature_on_this_key_exchange_is_different), name, name);
SpannableString spannableString = new SpannableString(introduction + " " +
context.getString(R.string.ConfirmIdentityDialog_you_may_wish_to_verify_this_contact));
spannableString.setSpan(new VerifySpan(context, mismatch),
introduction.length()+1, spannableString.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
setTitle(name);
setMessage(spannableString);
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(masterSecret, messageRecord, mismatch));
setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener());
}
@Override
public void show() {
super.show();
((TextView)this.findViewById(android.R.id.message))
.setMovementMethod(LinkMovementMethod.getInstance());
}
public void setCallback(OnClickListener callback) {
this.callback = callback;
}
private class AcceptListener implements OnClickListener {
private final MasterSecret masterSecret;
private final MessageRecord messageRecord;
private final IdentityKeyMismatch mismatch;
private AcceptListener(MasterSecret masterSecret, MessageRecord messageRecord, IdentityKeyMismatch mismatch) {
this.masterSecret = masterSecret;
this.messageRecord = messageRecord;
this.mismatch = mismatch;
}
@Override
public void onClick(DialogInterface dialog, int which) {
new AsyncTask<Void, Void, Void>()
{
@Override
protected Void doInBackground(Void... params) {
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(getContext());
identityDatabase.saveIdentity(masterSecret,
mismatch.getRecipientId(),
mismatch.getIdentityKey());
processMessageRecord(messageRecord);
return null;
}
private void processMessageRecord(MessageRecord messageRecord) {
Context context = getContext();
SmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getRecipientId(),
mismatch.getIdentityKey());
ApplicationContext.getInstance(context)
.getJobManager()
.add(new SmsDecryptJob(context, messageRecord.getId(), true, false));
}
}.execute();
if (callback != null) callback.onClick(null, 0);
}
}
private class CancelListener implements OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
if (callback != null) callback.onClick(null, 0);
}
}
private static class VerifySpan extends ClickableSpan {
private final Context context;
private final IdentityKeyMismatch mismatch;
private VerifySpan(Context context, IdentityKeyMismatch mismatch) {
this.context = context;
this.mismatch = mismatch;
}
@Override
public void onClick(View widget) {
Intent intent = new Intent(context, VerifyIdentityActivity.class);
intent.putExtra("recipient", mismatch.getRecipientId());
intent.putExtra("remote_identity", new IdentityKeyParcelable(mismatch.getIdentityKey()));
context.startActivity(intent);
}
}
}
......@@ -53,6 +53,7 @@ import org.smssecure.smssecure.database.DatabaseFactory;
import org.smssecure.smssecure.database.MmsDatabase;
import org.smssecure.smssecure.database.MmsSmsDatabase;
import org.smssecure.smssecure.database.SmsDatabase;
import org.smssecure.smssecure.database.documents.IdentityKeyMismatch;
import org.smssecure.smssecure.database.model.MediaMmsMessageRecord;
import org.smssecure.smssecure.database.model.MessageRecord;
import org.smssecure.smssecure.database.model.NotificationMmsMessageRecord;
......@@ -533,10 +534,23 @@ public class ConversationItem extends LinearLayout
contactPhoto.setVisibility(View.VISIBLE);
}
private IdentityKeyMismatch getKeyMismatch(final MessageRecord record) {
if (record.isIdentityMismatchFailure()) {
Log.w(TAG, "isIdentityMismatchFailure(): true");
for (final IdentityKeyMismatch mismatch : record.getIdentityKeyMismatches()) {
if (mismatch.getRecipientId() == record.getIndividualRecipient().getRecipientId()) {
return mismatch;
}
}
}
Log.w(TAG, "Returning null IdentityKeyMismatch...");
return null;
}
/// Event handlers
private void handleKeyExchangeClicked() {
new ReceiveKeyDialog(context, masterSecret, messageRecord).show();
new ConfirmIdentityDialog(context, masterSecret, messageRecord, getKeyMismatch(messageRecord)).show();
}
private void handleLegacyKeyExchangeClicked() {
......
......@@ -102,7 +102,7 @@ public class MessageRecipientListItem extends RelativeLayout
conflictButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
new ReceiveKeyDialog(getContext(), masterSecret, record).show();
new ConfirmIdentityDialog(getContext(), masterSecret, record, null).show();
}
});
} else if (networkFailure != null || record.isFailed()) {
......
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.smssecure.smssecure;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.View;
import android.widget.TextView;
import org.smssecure.smssecure.crypto.IdentityKeyParcelable;
import org.smssecure.smssecure.crypto.MasterSecret;
import org.smssecure.smssecure.crypto.storage.SilenceIdentityKeyStore;
import org.smssecure.smssecure.database.DatabaseFactory;
import org.smssecure.smssecure.database.EncryptingSmsDatabase;
import org.smssecure.smssecure.database.IdentityDatabase;
import org.smssecure.smssecure.database.model.MessageRecord;
import org.smssecure.smssecure.jobs.SmsDecryptJob;
import org.smssecure.smssecure.protocol.KeyExchangeMessage;
import org.smssecure.smssecure.recipients.Recipient;
import org.smssecure.smssecure.sms.IncomingIdentityUpdateMessage;
import org.smssecure.smssecure.sms.IncomingKeyExchangeMessage;
import org.smssecure.smssecure.sms.IncomingPreKeyBundleMessage;
import org.smssecure.smssecure.sms.IncomingTextMessage;
import org.smssecure.smssecure.util.Base64;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.InvalidVersionException;
import org.whispersystems.libsignal.LegacyMessageException;
import org.whispersystems.libsignal.protocol.PreKeySignalMessage;
import org.whispersystems.libsignal.state.IdentityKeyStore;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
/**
* Activity for displaying sent/received session keys.
*
* @author Moxie Marlinspike
*/
public class ReceiveKeyDialog extends AlertDialog {
private static final String TAG = ReceiveKeyDialog.class.getSimpleName();
private OnClickListener callback;
public ReceiveKeyDialog(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull MessageRecord messageRecord)
{
super(context);
try{
final IncomingKeyExchangeMessage message = getMessage(messageRecord);
final IdentityKey identityKey = getIdentityKey(message);
if (isTrusted(masterSecret, identityKey, messageRecord.getIndividualRecipient(), messageRecord.getSubscriptionId())){
setMessage(context.getString(R.string.ReceiveKeyActivity_the_signature_on_this_key_exchange_is_trusted_but));
} else {
setUntrustedText(messageRecord, identityKey);
}
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.receive_key_activity__complete), new AcceptListener(masterSecret, messageRecord, message, identityKey));
setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener());
} catch (InvalidKeyException | InvalidVersionException | InvalidMessageException | LegacyMessageException e) {
throw new AssertionError(e);
}
}
@Override
public void show() {
super.show();
((TextView)this.findViewById(android.R.id.message))
.setMovementMethod(LinkMovementMethod.getInstance());
}
public void setCallback(OnClickListener callback) {
this.callback = callback;
}
private void setUntrustedText(final MessageRecord messageRecord, final IdentityKey identityKey){
String introText = getContext().getString(R.string.ReceiveKeyActivity_the_signature_on_this_key_exchange_is_different);
SpannableString spannableString = new SpannableString(introText + " " +
getContext().getString(R.string.ConfirmIdentityDialog_you_may_wish_to_verify_this_contact));
spannableString.setSpan(new ClickableSpan() {
@Override
public void onClick(View widget) {
Intent intent = new Intent(getContext(), VerifyIdentityActivity.class);
intent.putExtra("recipient", messageRecord.getIndividualRecipient().getRecipientId());
intent.putExtra("remote_identity", new IdentityKeyParcelable(identityKey));
getContext().startActivity(intent);
}
}, introText.length() + 1,
spannableString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
setMessage(spannableString);
}
private boolean isTrusted(MasterSecret masterSecret, IdentityKey identityKey, Recipient recipient, int subscriptionId) {
IdentityKeyStore identityKeyStore = new SilenceIdentityKeyStore(getContext(), masterSecret, subscriptionId);
return identityKeyStore.isTrustedIdentity(new SignalProtocolAddress(recipient.getNumber(), 1), identityKey, IdentityKeyStore.Direction.RECEIVING);
}
private static IncomingKeyExchangeMessage getMessage(MessageRecord messageRecord)
throws InvalidKeyException, InvalidVersionException,
InvalidMessageException, LegacyMessageException
{
IncomingTextMessage message = new IncomingTextMessage(messageRecord.getIndividualRecipient().getNumber(),
messageRecord.getRecipientDeviceId(),
System.currentTimeMillis(),
messageRecord.getBody().getBody(),
messageRecord.getSubscriptionId());
if (messageRecord.isBundleKeyExchange()) {
return new IncomingPreKeyBundleMessage(message, message.getMessageBody());
} else if (messageRecord.isIdentityUpdate()) {
return new IncomingIdentityUpdateMessage(message, message.getMessageBody());
} else {
return new IncomingKeyExchangeMessage(message, message.getMessageBody());
}
}
private static IdentityKey getIdentityKey(IncomingKeyExchangeMessage message)
throws InvalidKeyException, InvalidVersionException,
InvalidMessageException, LegacyMessageException
{
try {
if (message.isIdentityUpdate()) {
return new IdentityKey(Base64.decodeWithoutPadding(message.getMessageBody()), 0);
} else if (message.isPreKeyBundle()) {
return new PreKeySignalMessage(Base64.decodeWithoutPadding(message.getMessageBody())).getIdentityKey();
} else {
return new KeyExchangeMessage(Base64.decodeWithoutPadding(message.getMessageBody())).getIdentityKey();
}
} catch (IOException e) {
throw new AssertionError(e);
}
}
private class CancelListener implements OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
if (callback != null) callback.onClick(null, 0);
}
}
private class AcceptListener implements OnClickListener {
private MasterSecret masterSecret;
private MessageRecord messageRecord;
private IncomingKeyExchangeMessage message;
private IdentityKey identityKey;
private AcceptListener(MasterSecret masterSecret,
MessageRecord messageRecord,
IncomingKeyExchangeMessage message,
IdentityKey identityKey)
{
this.masterSecret = masterSecret;
this.messageRecord = messageRecord;
this.message = message;
this.identityKey = identityKey;
}
@Override
public void onClick(DialogInterface dialog, int which) {
new AsyncTask<Void, Void, Void>(){
@Override
protected Void doInBackground(Void... params) {
Context context = getContext();
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
identityDatabase.saveIdentity(masterSecret,
messageRecord.getIndividualRecipient().getRecipientId(),
identityKey);
if (message.isIdentityUpdate()) {
smsDatabase.markAsProcessedKeyExchange(messageRecord.getId());
} else {
ApplicationContext.getInstance(getContext())
.getJobManager()
.add(new SmsDecryptJob(context, messageRecord.getId(), true, false));
}
return null;
}
}.execute();
if (callback != null) callback.onClick(null, 0);
}
}
}
......@@ -199,6 +199,11 @@ public class SmsDecryptJob extends MasterSecretJob {
database.markAsStaleKeyExchange(messageId);
} catch (UntrustedIdentityException e) {
Log.w(TAG, e);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), false);
long recipientId = recipients.getPrimaryRecipient().getRecipientId();
database.addMismatchedIdentity(messageId, recipientId, e.getUntrustedIdentity());
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment