UX for unencrypted fallback case

pull/1/head
Jake McGinty 10 years ago committed by Moxie Marlinspike
parent 40629a3bcf
commit 832763f695

@ -35,6 +35,27 @@ public class Session {
return hasV1Session(context, recipient) || hasV2Session(context, masterSecret, recipient);
}
public static boolean hasEncryptCapableSession(Context context,
MasterSecret masterSecret,
CanonicalRecipient recipient)
{
RecipientDevice device = new RecipientDevice(recipient.getRecipientId(),
RecipientDevice.DEFAULT_DEVICE_ID);
return hasEncryptCapableSession(context, masterSecret, recipient, device);
}
public static boolean hasEncryptCapableSession(Context context,
MasterSecret masterSecret,
CanonicalRecipient recipient,
RecipientDevice device)
{
return
hasV1Session(context, recipient) ||
(hasV2Session(context, masterSecret, recipient) &&
!SessionRecordV2.needsRefresh(context, masterSecret, device));
}
public static boolean hasRemoteIdentityKey(Context context,
MasterSecret masterSecret,
CanonicalRecipient recipient)

@ -64,7 +64,10 @@
<string name="ConversationItem_group_action_joined">%1$s have joined the group.</string>
<string name="ConversationItem_group_action_modify">%1$s has updated the group.</string>
<string name="ConversationItem_click_to_approve">Tap for SMS fallback</string>
<string name="ConversationItem_click_to_approve_unencrypted">Tap for insecure fallback</string>
<string name="ConversationItem_click_to_approve_dialog_title">Fallback to SMS?</string>
<string name="ConversationItem_click_to_approve_unencrypted_dialog_title">Fallback to unencrypted SMS?</string>
<string name="ConversationItem_click_to_approve_unencrypted_dialog_message">This message will <b>not</b> be encrypted because a secure session could not be established.\n\nSend insecure message?</string>
<!-- ConversationActivity -->
<string name="ConversationActivity_initiate_secure_session_question">Initiate Secure Session?</string>

@ -675,7 +675,7 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
R.attr.conversation_send_secure_button};
TypedArray drawables = obtainStyledAttributes(attributes);
if ((getRecipients() != null && getRecipients().isGroupRecipient()) ||
if (isPushDestination() || (getRecipients() != null && getRecipients().isGroupRecipient()) ||
(isSingleConversation() && Session.hasSession(this, masterSecret, getRecipients().getPrimaryRecipient())))
{
sendButton.setImageDrawable(drawables.getDrawable(1));
@ -1074,7 +1074,7 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
private void sendMessage(boolean forcePlaintext) {
try {
Recipients recipients = getRecipients();
Recipients recipients = getRecipients();
if (recipients == null)
throw new RecipientFormattingException("Badly formatted");

@ -25,8 +25,6 @@ import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.media.MediaScannerConnection;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
@ -59,8 +57,6 @@ import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.Emoji;
import org.thoughtcrime.securesms.util.Dialogs;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.directory.Directory;
import org.whispersystems.textsecure.directory.NotInDirectoryException;
import org.whispersystems.textsecure.storage.Session;
import org.whispersystems.textsecure.util.FutureTaskListener;
import org.whispersystems.textsecure.util.ListenableFutureTask;
@ -230,7 +226,7 @@ public class ConversationItem extends LinearLayout {
if (messageRecord.isPending() && pushDestination && !messageRecord.isForcedSms()) {
background = SENT_PUSH_PENDING;
triangleBackground = SENT_PUSH_PENDING_TRIANGLE;
} else if (messageRecord.isPending() || messageRecord.isPendingFallbackApproval()) {
} else if (messageRecord.isPending() || messageRecord.isPendingSmsFallback()) {
background = SENT_SMS_PENDING;
triangleBackground = SENT_SMS_PENDING_TRIANGLE;
} else if (messageRecord.isPush()) {
@ -265,8 +261,8 @@ public class ConversationItem extends LinearLayout {
private void setStatusIcons(MessageRecord messageRecord) {
failedImage.setVisibility(messageRecord.isFailed() ? View.VISIBLE : View.GONE);
if (messageRecord.isOutgoing()) {
pendingIndicator.setVisibility(messageRecord.isPendingFallbackApproval() ? View.VISIBLE : View.GONE);
indicatorText.setVisibility(messageRecord.isPendingFallbackApproval() ? View.VISIBLE : View.GONE);
pendingIndicator.setVisibility(messageRecord.isPendingSmsFallback() ? View.VISIBLE : View.GONE);
indicatorText.setVisibility(messageRecord.isPendingSmsFallback() ? View.VISIBLE : View.GONE);
}
secureImage.setVisibility(messageRecord.isSecure() ? View.VISIBLE : View.GONE);
keyImage.setVisibility(messageRecord.isKeyExchange() ? View.VISIBLE : View.GONE);
@ -278,9 +274,10 @@ public class ConversationItem extends LinearLayout {
if (messageRecord.isFailed()) {
dateText.setText(R.string.ConversationItem_error_sending_message);
} else if (messageRecord.isPendingFallbackApproval() && indicatorText != null) {
} else if (messageRecord.isPendingSmsFallback() && indicatorText != null) {
dateText.setText("");
indicatorText.setText(R.string.ConversationItem_click_to_approve);
if (messageRecord.isPendingSecureSmsFallback()) indicatorText.setText(R.string.ConversationItem_click_to_approve);
else indicatorText.setText(R.string.ConversationItem_click_to_approve_unencrypted);
} else if (messageRecord.isPending()) {
dateText.setText(" ··· ");
} else {
@ -294,14 +291,15 @@ public class ConversationItem extends LinearLayout {
private void setMinimumWidth() {
if (indicatorText != null && indicatorText.getVisibility() == View.VISIBLE && indicatorText.getText() != null) {
conversationParent.setMinimumWidth(indicatorText.getText().length() * 20);
final float density = getResources().getDisplayMetrics().density;
conversationParent.setMinimumWidth(indicatorText.getText().length() * (int)(6.5 * density));
} else {
conversationParent.setMinimumWidth(0);
}
}
private void setEvents(MessageRecord messageRecord) {
setClickable(messageRecord.isPendingFallbackApproval() ||
setClickable(messageRecord.isPendingSmsFallback() ||
(messageRecord.isKeyExchange() &&
!messageRecord.isCorruptedKeyExchange() &&
!messageRecord.isOutgoing()));
@ -640,23 +638,40 @@ public class ConversationItem extends LinearLayout {
!messageRecord.isProcessedKeyExchange() &&
!messageRecord.isStaleKeyExchange())
handleKeyExchangeClicked();
else if (messageRecord.isPendingFallbackApproval())
else if (messageRecord.isPendingSmsFallback())
handleMessageApproval();
}
}
private void handleMessageApproval() {
final int title;
final int message;
if (messageRecord.isPendingSecureSmsFallback()) {
title = R.string.ConversationItem_click_to_approve_dialog_title;
message = -1;
} else {
title = R.string.ConversationItem_click_to_approve_unencrypted_dialog_title;
message = R.string.ConversationItem_click_to_approve_unencrypted_dialog_message;
}
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.ConversationItem_click_to_approve_dialog_title);
builder.setTitle(title);
if (message > -1) builder.setMessage(message);
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if (messageRecord.isMms()) {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
if (messageRecord.isPendingInsecureSmsFallback()) {
database.markAsInsecure(messageRecord.getId());
}
database.markAsOutbox(messageRecord.getId());
database.markAsForcedSms(messageRecord.getId());
} else {
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
if (messageRecord.isPendingInsecureSmsFallback()) {
database.markAsInsecure(messageRecord.getId());
}
database.markAsOutbox(messageRecord.getId());
database.markAsForcedSms(messageRecord.getId());
}
@ -669,8 +684,11 @@ public class ConversationItem extends LinearLayout {
builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if (messageRecord.isMms()) DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageRecord.getId());
else DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageRecord.getId());
if (messageRecord.isMms()) {
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageRecord.getId());
} else {
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageRecord.getId());
}
}
});
builder.show();

@ -285,8 +285,13 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
updateMailboxBitmask(id, 0, Types.MESSAGE_FORCE_SMS_BIT);
}
public void markAsPendingApproval(long messageId) {
updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_PENDING_FALLBACK_APPROVAL);
public void markAsPendingSecureSmsFallback(long messageId) {
updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_PENDING_SECURE_SMS_FALLBACK);
notifyConversationListeners(getThreadIdForMessage(messageId));
}
public void markAsPendingInsecureSmsFallback(long messageId) {
updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_PENDING_INSECURE_SMS_FALLBACK);
notifyConversationListeners(getThreadIdForMessage(messageId));
}
@ -338,6 +343,10 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
updateMailboxBitmask(messageId, 0, Types.SECURE_MESSAGE_BIT);
}
public void markAsInsecure(long messageId) {
updateMailboxBitmask(messageId, Types.SECURE_MESSAGE_BIT, 0);
}
public void markAsPush(long messageId) {
updateMailboxBitmask(messageId, 0, Types.PUSH_MESSAGE_BIT);
}

@ -15,18 +15,20 @@ public interface MmsSmsColumns {
protected static final long TOTAL_MASK = 0xFFFFFFFF;
// Base Types
protected static final long BASE_TYPE_MASK = 0x1F;
protected static final long BASE_TYPE_MASK = 0x1F;
protected static final long BASE_INBOX_TYPE = 20;
protected static final long BASE_OUTBOX_TYPE = 21;
protected static final long BASE_SENDING_TYPE = 22;
protected static final long BASE_SENT_TYPE = 23;
protected static final long BASE_SENT_FAILED_TYPE = 24;
protected static final long BASE_PENDING_FALLBACK_APPROVAL = 25;
protected static final long BASE_INBOX_TYPE = 20;
protected static final long BASE_OUTBOX_TYPE = 21;
protected static final long BASE_SENDING_TYPE = 22;
protected static final long BASE_SENT_TYPE = 23;
protected static final long BASE_SENT_FAILED_TYPE = 24;
protected static final long BASE_PENDING_SECURE_SMS_FALLBACK = 25;
protected static final long BASE_PENDING_INSECURE_SMS_FALLBACK = 26;
protected static final long[] OUTGOING_MESSAGE_TYPES = {BASE_OUTBOX_TYPE, BASE_SENT_TYPE,
BASE_SENDING_TYPE, BASE_SENT_FAILED_TYPE,
BASE_PENDING_FALLBACK_APPROVAL};
BASE_PENDING_SECURE_SMS_FALLBACK,
BASE_PENDING_INSECURE_SMS_FALLBACK};
// Message attributes
protected static final long MESSAGE_ATTRIBUTE_MASK = 0xE0;
@ -82,8 +84,17 @@ public interface MmsSmsColumns {
(type & BASE_TYPE_MASK) == BASE_SENDING_TYPE;
}
public static boolean isPendingApprovalType(long type) {
return (type & BASE_TYPE_MASK) == BASE_PENDING_FALLBACK_APPROVAL;
public static boolean isPendingSmsFallbackType(long type) {
return (type & BASE_TYPE_MASK) == BASE_PENDING_INSECURE_SMS_FALLBACK ||
(type & BASE_TYPE_MASK) == BASE_PENDING_SECURE_SMS_FALLBACK;
}
public static boolean isPendingSecureSmsFallbackType(long type) {
return (type & BASE_TYPE_MASK) == BASE_PENDING_SECURE_SMS_FALLBACK;
}
public static boolean isPendingInsecureSmsFallbackType(long type) {
return (type & BASE_TYPE_MASK) == BASE_PENDING_INSECURE_SMS_FALLBACK;
}
public static boolean isInboxType(long type) {

@ -174,6 +174,10 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
updateTypeBitmask(id, 0, Types.SECURE_MESSAGE_BIT);
}
public void markAsInsecure(long id) {
updateTypeBitmask(id, Types.SECURE_MESSAGE_BIT, 0);
}
public void markAsPush(long id) {
updateTypeBitmask(id, 0, Types.PUSH_MESSAGE_BIT);
}
@ -202,8 +206,12 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_OUTBOX_TYPE);
}
public void markAsPendingApproval(long id) {
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_PENDING_FALLBACK_APPROVAL);
public void markAsPendingSecureSmsFallback(long id) {
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_PENDING_SECURE_SMS_FALLBACK);
}
public void markAsPendingInsecureSmsFallback(long id) {
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_PENDING_INSECURE_SMS_FALLBACK);
}
public void markAsSending(long id) {

@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.textsecure.util.Util;
/**
* The base class for message record models that are displayed in
@ -125,8 +124,16 @@ public abstract class MessageRecord extends DisplayRecord {
return SmsDatabase.Types.isProcessedKeyExchange(type);
}
public boolean isPendingFallbackApproval() {
return SmsDatabase.Types.isPendingApprovalType(type);
public boolean isPendingSmsFallback() {
return SmsDatabase.Types.isPendingSmsFallbackType(type);
}
public boolean isPendingSecureSmsFallback() {
return SmsDatabase.Types.isPendingSecureSmsFallbackType(type);
}
public boolean isPendingInsecureSmsFallback() {
return SmsDatabase.Types.isPendingInsecureSmsFallbackType(type);
}
public boolean isBundleKeyExchange() {

@ -31,11 +31,12 @@ import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.SendReceiveService.ToastHandler;
import org.thoughtcrime.securesms.sms.IncomingIdentityUpdateMessage;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.SecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.transport.UniversalTransport;
import org.thoughtcrime.securesms.transport.UntrustedIdentityException;
import org.thoughtcrime.securesms.transport.UserInterventionRequiredException;
import org.whispersystems.textsecure.crypto.MasterSecret;
import ws.com.google.android.mms.MmsException;
@ -84,16 +85,18 @@ public class MmsSender {
result.getResponseStatus());
systemStateListener.unregisterForConnectivityChange();
} catch (UserInterventionRequiredException uire) {
Log.w("MmsSender", uire);
database.markAsPendingApproval(message.getDatabaseMessageId());
Recipients recipients = threads.getRecipientsForThreadId(threadId);
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
} catch (InsecureFallbackApprovalException ifae) {
Log.w("MmsSender", ifae);
database.markAsPendingInsecureSmsFallback(message.getDatabaseMessageId());
notifyMessageDeliveryFailed(context, threads, threadId);
} catch (SecureFallbackApprovalException sfae) {
Log.w("MmsSender", sfae);
database.markAsPendingSecureSmsFallback(message.getDatabaseMessageId());
notifyMessageDeliveryFailed(context, threads, threadId);
} catch (UndeliverableMessageException e) {
Log.w("MmsSender", e);
database.markAsSentFailed(message.getDatabaseMessageId());
Recipients recipients = threads.getRecipientsForThreadId(threadId);
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
notifyMessageDeliveryFailed(context, threads, threadId);
} catch (UntrustedIdentityException uie) {
IncomingIdentityUpdateMessage identityUpdateMessage = IncomingIdentityUpdateMessage.createFor(message.getTo()[0].getString(), uie.getIdentityKey());
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, identityUpdateMessage);
@ -117,6 +120,11 @@ public class MmsSender {
}
}
private static void notifyMessageDeliveryFailed(Context context, ThreadDatabase threads, long threadId) {
Recipients recipients = threads.getRecipientsForThreadId(threadId);
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
}
private void scheduleQuickRetryAlarm() {
((AlarmManager)context.getSystemService(Context.ALARM_SERVICE))
.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (30 * 1000),

@ -36,12 +36,12 @@ import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.SendReceiveService.ToastHandler;
import org.thoughtcrime.securesms.sms.IncomingIdentityUpdateMessage;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.SecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.transport.UniversalTransport;
import org.thoughtcrime.securesms.transport.UntrustedIdentityException;
import org.thoughtcrime.securesms.transport.UserInterventionRequiredException;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.storage.Session;
@ -86,9 +86,13 @@ public class SmsSender {
database.markAsSending(record.getId());
transport.deliver(record);
} catch (UserInterventionRequiredException uire) {
Log.w("SmsSender", uire);
DatabaseFactory.getSmsDatabase(context).markAsPendingApproval(record.getId());
} catch (InsecureFallbackApprovalException ifae) {
Log.w("SmsSender", ifae);
DatabaseFactory.getSmsDatabase(context).markAsPendingInsecureSmsFallback(record.getId());
MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipients(), record.getThreadId());
} catch (SecureFallbackApprovalException sfae) {
Log.w("SmsSender", sfae);
DatabaseFactory.getSmsDatabase(context).markAsPendingSecureSmsFallback(record.getId());
MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipients(), record.getThreadId());
} catch (UntrustedIdentityException e) {
Log.w("SmsSender", e);

@ -0,0 +1,7 @@
package org.thoughtcrime.securesms.transport;
public class InsecureFallbackApprovalException extends Exception {
public InsecureFallbackApprovalException(String detailMessage) {
super(detailMessage);
}
}

@ -36,6 +36,7 @@ import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.SessionCipher;
import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.storage.Session;
import org.whispersystems.textsecure.util.Hex;
import java.io.IOException;
@ -62,7 +63,9 @@ public class MmsTransport {
this.radio = MmsRadio.getInstance(context);
}
public MmsSendResult deliver(SendReq message) throws UndeliverableMessageException {
public MmsSendResult deliver(SendReq message) throws UndeliverableMessageException,
InsecureFallbackApprovalException
{
if (TextSecurePreferences.isPushRegistered(context) &&
!TextSecurePreferences.isSmsFallbackEnabled(context))
{
@ -109,7 +112,7 @@ public class MmsTransport {
}
private MmsSendResult sendMms(SendReq message, boolean usingMmsRadio, boolean useProxy)
throws IOException, UndeliverableMessageException
throws IOException, UndeliverableMessageException, InsecureFallbackApprovalException
{
String number = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number();
boolean upgradedSecure = false;
@ -141,7 +144,7 @@ public class MmsTransport {
}
}
private SendReq getEncryptedMessage(SendReq pdu) {
private SendReq getEncryptedMessage(SendReq pdu) throws InsecureFallbackApprovalException {
EncodedStringValue[] encodedRecipient = pdu.getTo();
String recipient = encodedRecipient[0].getString();
byte[] pduBytes = new PduComposer(context, pdu).make();
@ -162,11 +165,16 @@ public class MmsTransport {
return encryptedPdu;
}
private byte[] getEncryptedPdu(MasterSecret masterSecret, String recipientString, byte[] pduBytes) {
private byte[] getEncryptedPdu(MasterSecret masterSecret, String recipientString, byte[] pduBytes) throws InsecureFallbackApprovalException {
try {
TextTransport transportDetails = new TextTransport();
Recipient recipient = RecipientFactory.getRecipientsFromString(context, recipientString, false).getPrimaryRecipient();
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), RecipientDevice.DEFAULT_DEVICE_ID);
if (!Session.hasEncryptCapableSession(context, masterSecret, recipient, recipientDevice)) {
throw new InsecureFallbackApprovalException("No session exists for this secure message.");
}
SessionCipher sessionCipher = SessionCipher.createFor(context, masterSecret, recipientDevice);
CiphertextMessage ciphertextMessage = sessionCipher.encrypt(pduBytes);

@ -0,0 +1,7 @@
package org.thoughtcrime.securesms.transport;
public class SecureFallbackApprovalException extends Exception {
public SecureFallbackApprovalException(String detailMessage) {
super(detailMessage);
}
}

@ -33,6 +33,7 @@ import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.SessionCipher;
import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.storage.Session;
import java.util.ArrayList;
@ -46,7 +47,9 @@ public class SmsTransport extends BaseTransport {
this.masterSecret = masterSecret;
}
public void deliver(SmsMessageRecord message) throws UndeliverableMessageException {
public void deliver(SmsMessageRecord message) throws UndeliverableMessageException,
InsecureFallbackApprovalException
{
if (!TextSecurePreferences.isSmsNonDataOutEnabled(context) && !TextSecurePreferences.isSmsFallbackEnabled(context)) {
throw new UndeliverableMessageException("SMS Transport is not enabled!");
}
@ -58,7 +61,9 @@ public class SmsTransport extends BaseTransport {
}
}
private void deliverSecureMessage(SmsMessageRecord message) throws UndeliverableMessageException {
private void deliverSecureMessage(SmsMessageRecord message) throws UndeliverableMessageException,
InsecureFallbackApprovalException
{
MultipartSmsMessageHandler multipartMessageHandler = new MultipartSmsMessageHandler();
OutgoingTextMessage transportMessage = OutgoingTextMessage.from(message);
@ -161,9 +166,16 @@ public class SmsTransport extends BaseTransport {
private OutgoingTextMessage getAsymmetricEncrypt(MasterSecret masterSecret,
OutgoingTextMessage message)
throws InsecureFallbackApprovalException
{
Recipient recipient = message.getRecipients().getPrimaryRecipient();
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), RecipientDevice.DEFAULT_DEVICE_ID);
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(),
RecipientDevice.DEFAULT_DEVICE_ID);
if (!Session.hasEncryptCapableSession(context, masterSecret, recipient, recipientDevice)) {
throw new InsecureFallbackApprovalException("No session exists for this secure message.");
}
String body = message.getMessageBody();
SmsTransportDetails transportDetails = new SmsTransportDetails();
SessionCipher sessionCipher = SessionCipher.createFor(context, masterSecret, recipientDevice);

@ -24,6 +24,7 @@ import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.mms.MmsSendResult;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingIdentityUpdateMessage;
@ -36,6 +37,7 @@ import org.whispersystems.textsecure.directory.NotInDirectoryException;
import org.whispersystems.textsecure.push.ContactTokenDetails;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.push.UnregisteredUserException;
import org.whispersystems.textsecure.storage.Session;
import org.whispersystems.textsecure.util.DirectoryUtil;
import org.whispersystems.textsecure.util.InvalidNumberException;
@ -60,7 +62,8 @@ public class UniversalTransport {
}
public void deliver(SmsMessageRecord message)
throws UndeliverableMessageException, UntrustedIdentityException, RetryLaterException, UserInterventionRequiredException
throws UndeliverableMessageException, UntrustedIdentityException, RetryLaterException,
SecureFallbackApprovalException, InsecureFallbackApprovalException
{
if (!TextSecurePreferences.isPushRegistered(context)) {
smsTransport.deliver(message);
@ -99,7 +102,8 @@ public class UniversalTransport {
}
public MmsSendResult deliver(SendReq mediaMessage, long threadId)
throws UndeliverableMessageException, RetryLaterException, UntrustedIdentityException, UserInterventionRequiredException
throws UndeliverableMessageException, RetryLaterException, UntrustedIdentityException,
SecureFallbackApprovalException, InsecureFallbackApprovalException
{
if (Util.isEmpty(mediaMessage.getTo())) {
return mmsTransport.deliver(mediaMessage);
@ -155,28 +159,42 @@ public class UniversalTransport {
}
private MmsSendResult fallbackOrAskApproval(SendReq mediaMessage, String destination)
throws UserInterventionRequiredException, UndeliverableMessageException
throws SecureFallbackApprovalException, UndeliverableMessageException, InsecureFallbackApprovalException
{
boolean isSmsFallbackApprovalRequired = isSmsFallbackApprovalRequired(destination);
if (!isSmsFallbackApprovalRequired) {
Log.i("UniversalTransport", "Falling back to MMS without user intervention");
return mmsTransport.deliver(mediaMessage);
} else {
Log.i("UniversalTransport", "Marking message as pending user approval per their settings");
throw new UserInterventionRequiredException("Pending user approval for fallback to SMS");
try {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, destination, false).getPrimaryRecipient();
boolean isSmsFallbackApprovalRequired = isSmsFallbackApprovalRequired(destination);
if (!isSmsFallbackApprovalRequired) {
Log.w("UniversalTransport", "Falling back to MMS");
return mmsTransport.deliver(mediaMessage);
} else if (!Session.hasEncryptCapableSession(context, masterSecret, recipient)) {
Log.w("UniversalTransport", "Marking message as pending insecure SMS fallback");
throw new InsecureFallbackApprovalException("Pending user approval for fallback to insecure SMS");
} else {
Log.w("UniversalTransport", "Marking message as pending secure SMS fallback");
throw new SecureFallbackApprovalException("Pending user approval for fallback secure to SMS");
}
} catch (RecipientFormattingException rfe) {
throw new UndeliverableMessageException(rfe);
}
}
private void fallbackOrAskApproval(SmsMessageRecord smsMessage, String destination)
throws UserInterventionRequiredException, UndeliverableMessageException
throws SecureFallbackApprovalException, UndeliverableMessageException, InsecureFallbackApprovalException
{
boolean isSmsFallbackApprovalRequired = isSmsFallbackApprovalRequired(destination);
Recipient recipient = smsMessage.getIndividualRecipient();
boolean isSmsFallbackApprovalRequired = isSmsFallbackApprovalRequired(destination);
if (!isSmsFallbackApprovalRequired) {
Log.i("UniversalTransport", "Falling back to SMS without user intervention");
Log.w("UniversalTransport", "Falling back to SMS");
smsTransport.deliver(smsMessage);
} else if (!Session.hasEncryptCapableSession(context, masterSecret, recipient)) {
Log.w("UniversalTransport", "Marking message as pending insecure fallback.");
throw new InsecureFallbackApprovalException("Pending user approval for fallback to insecure SMS");
} else {
Log.i("UniversalTransport", "Marking message as pending user approval per their settings");
throw new UserInterventionRequiredException("Pending user approval for fallback to SMS");
Log.w("UniversalTransport", "Marking message as pending secure fallback.");
throw new SecureFallbackApprovalException("Pending user approval for fallback to secure SMS");
}
}
@ -218,7 +236,6 @@ public class UniversalTransport {
}
}
public boolean isMultipleRecipients(SendReq mediaMessage) {
int recipientCount = 0;
@ -267,9 +284,9 @@ public class UniversalTransport {
return directory.isActiveNumber(destination);
} catch (NotInDirectoryException e) {
try {
PushServiceSocket socket = PushServiceSocketFactory.create(context);
String contactToken = DirectoryUtil.getDirectoryServerToken(destination);
ContactTokenDetails registeredUser = socket.getContactTokenDetails(contactToken);
PushServiceSocket socket = PushServiceSocketFactory.create(context);
String contactToken = DirectoryUtil.getDirectoryServerToken(destination);
ContactTokenDetails registeredUser = socket.getContactTokenDetails(contactToken);
if (registeredUser == null) {
registeredUser = new ContactTokenDetails();

@ -1,7 +0,0 @@
package org.thoughtcrime.securesms.transport;
public class UserInterventionRequiredException extends Exception {
public UserInterventionRequiredException(String detailMessage) {
super(detailMessage);
}
}
Loading…
Cancel
Save