Fix "Group Threads" so that messages are encrypted when possible.

1) Change the MessageSender logic so that individual SMS messages
are encrypted whenever there is a secure session, unless the UI
explicitly specifies otherwise.

2) Change the MMS logic so that messages to a recipient with a
secure session are all sent individually, instead of including
those recipients into the batch plaintext message.
This commit is contained in:
Moxie Marlinspike 2012-10-21 17:41:44 -07:00
parent 1bd260b981
commit c13a3a8181
3 changed files with 118 additions and 55 deletions

View File

@ -106,7 +106,7 @@ public class ConversationActivity extends SherlockFragmentActivity
private Recipients recipients;
private long threadId;
private boolean sendEncrypted;
private boolean isEncryptedConversation;
private CharacterCalculator characterCalculator = new CharacterCalculator();
@ -188,7 +188,7 @@ public class ConversationActivity extends SherlockFragmentActivity
MenuInflater inflater = this.getSupportMenuInflater();
menu.clear();
if (isSingleConversation() && sendEncrypted)
if (isSingleConversation() && isEncryptedConversation)
{
if (isAuthenticatedSession()) {
inflater.inflate(R.menu.conversation_secure_verified, menu);
@ -230,7 +230,7 @@ public class ConversationActivity extends SherlockFragmentActivity
@Override
public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
if (sendEncrypted) {
if (isEncryptedConversation) {
android.view.MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.conversation_button_context, menu);
}
@ -239,7 +239,7 @@ public class ConversationActivity extends SherlockFragmentActivity
@Override
public boolean onContextItemSelected(android.view.MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_context_send_unencrypted: sendMessage(false); return true;
case R.id.menu_context_send_unencrypted: sendMessage(true); return true;
}
return false;
@ -362,7 +362,7 @@ public class ConversationActivity extends SherlockFragmentActivity
if (isSingleConversation()) {
if (sendEncrypted) {
if (isEncryptedConversation) {
title = AuthenticityCalculator.getAuthenticatedName(this,
getRecipients().getPrimaryRecipient(),
masterSecret);
@ -400,12 +400,12 @@ public class ConversationActivity extends SherlockFragmentActivity
{
sendButton.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_menu_lock_holo_light, 0);
sendButton.setCompoundDrawablePadding(15);
this.sendEncrypted = true;
this.characterCalculator = new EncryptedCharacterCalculator();
this.isEncryptedConversation = true;
this.characterCalculator = new EncryptedCharacterCalculator();
} else {
sendButton.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
this.sendEncrypted = false;
this.characterCalculator = new CharacterCalculator();
this.isEncryptedConversation = false;
this.characterCalculator = new CharacterCalculator();
}
calculateCharactersRemaining();
@ -577,7 +577,7 @@ public class ConversationActivity extends SherlockFragmentActivity
if (rawText.length() < 1 && !attachmentManager.isAttachmentPresent())
throw new InvalidMessageException(getString(R.string.ConversationActivity_message_is_empty_exclamation));
if (!sendEncrypted && Tag.isTaggable(this, rawText))
if (!isEncryptedConversation && Tag.isTaggable(this, rawText))
rawText = Tag.getTaggedMessage(rawText);
return rawText;
@ -604,7 +604,7 @@ public class ConversationActivity extends SherlockFragmentActivity
}
}
private void sendMessage(boolean sendEncrypted) {
private void sendMessage(boolean forcePlaintext) {
try {
Recipients recipients = getRecipients();
@ -617,13 +617,13 @@ public class ConversationActivity extends SherlockFragmentActivity
if (attachmentManager.isAttachmentPresent()) {
allocatedThreadId = MessageSender.sendMms(ConversationActivity.this, masterSecret, recipients,
threadId, attachmentManager.getSlideDeck(), message,
sendEncrypted);
forcePlaintext);
} else if (recipients.isEmailRecipient()) {
allocatedThreadId = MessageSender.sendMms(ConversationActivity.this, masterSecret, recipients,
threadId, new SlideDeck(), message, sendEncrypted);
threadId, new SlideDeck(), message, forcePlaintext);
} else {
allocatedThreadId = MessageSender.send(ConversationActivity.this, masterSecret, recipients,
threadId, message, sendEncrypted);
threadId, message, forcePlaintext);
}
sendComplete(recipients, allocatedThreadId);
@ -679,7 +679,7 @@ public class ConversationActivity extends SherlockFragmentActivity
private class SendButtonListener implements OnClickListener, TextView.OnEditorActionListener {
public void onClick(View v) {
sendMessage(sendEncrypted);
sendMessage(false);
}
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {

View File

@ -16,13 +16,16 @@
*/
package org.thoughtcrime.securesms.recipients;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import org.thoughtcrime.securesms.crypto.KeyUtil;
import org.thoughtcrime.securesms.util.NumberUtil;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class Recipients implements Parcelable {
@ -44,6 +47,12 @@ public class Recipients implements Parcelable {
this.recipients = recipients;
}
public Recipients(final Recipient recipient) {
this.recipients = new LinkedList<Recipient>() {{
add(recipient);
}};
}
public Recipients(Parcel in) {
this.recipients = new ArrayList<Recipient>();
in.readTypedList(recipients, Recipient.CREATOR);
@ -68,6 +77,30 @@ public class Recipients implements Parcelable {
return false;
}
public Recipients getSecureSessionRecipients(Context context) {
List<Recipient> secureRecipients = new LinkedList<Recipient>();
for (Recipient recipient : recipients) {
if (KeyUtil.isSessionFor(context, recipient)) {
secureRecipients.add(recipient);
}
}
return new Recipients(secureRecipients);
}
public Recipients getInsecureSessionRecipients(Context context) {
List<Recipient> insecureRecipients = new LinkedList<Recipient>();
for (Recipient recipient : recipients) {
if (!KeyUtil.isSessionFor(context, recipient)) {
insecureRecipients.add(recipient);
}
}
return new Recipients(insecureRecipients);
}
public boolean isEmpty() {
return this.recipients.isEmpty();
}

View File

@ -16,32 +16,31 @@
*/
package org.thoughtcrime.securesms.sms;
import java.util.Iterator;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.KeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.mms.TextSlide;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.MmsSender;
import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.service.SmsSender;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.EncodedStringValue;
import ws.com.google.android.mms.pdu.PduBody;
import ws.com.google.android.mms.pdu.SendReq;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
public class MessageSender {
public static long sendMms(Context context, MasterSecret masterSecret, Recipients recipients,
long threadId, SlideDeck slideDeck, String message, boolean isSecure) throws MmsException
long threadId, SlideDeck slideDeck, String message, boolean forcePlaintext)
throws MmsException
{
if (threadId == -1)
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
@ -49,42 +48,55 @@ public class MessageSender {
if (message.trim().length() > 0)
slideDeck.addSlide(new TextSlide(context, message));
SendReq sendRequest = new SendReq();
String[] recipientsArray = recipients.toNumberStringArray();
EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(recipientsArray);
PduBody body = slideDeck.toPduBody();
SendReq sendRequest = new SendReq();
PduBody body = slideDeck.toPduBody();
sendRequest.setTo(encodedNumbers);
sendRequest.setDate(System.currentTimeMillis() / 1000L);
sendRequest.setBody(body);
sendRequest.setContentType(ContentType.MULTIPART_MIXED.getBytes());
long messageId = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret).insertMessageSent(sendRequest, threadId, isSecure);
Intent intent = new Intent(SendReceiveService.SEND_MMS_ACTION, null, context, SendReceiveService.class);
intent.putExtra("message_id", messageId);
context.startService(intent);
Recipients secureRecipients = recipients.getSecureSessionRecipients(context);
Recipients insecureRecipients = recipients.getInsecureSessionRecipients(context);
for (Recipient secureRecipient : secureRecipients.getRecipientsList()) {
sendMms(context, new Recipients(secureRecipient), masterSecret,
sendRequest, threadId, !forcePlaintext);
}
if (!insecureRecipients.isEmpty()) {
sendMms(context, insecureRecipients, masterSecret, sendRequest, threadId, false);
}
return threadId;
}
public static long send(Context context, MasterSecret masterSecret, Recipients recipients,
long threadId, String message, boolean isSecure)
long threadId, String message, boolean forcePlaintext)
{
if (threadId == -1)
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
Iterator<Recipient> i = recipients.getRecipientsList().iterator();
while (i.hasNext()) {
Recipient recipient = i.next();
for (Recipient recipient : recipients.getRecipientsList()) {
boolean isSecure = KeyUtil.isSessionFor(context, recipient) && !forcePlaintext;
long messageId;
if (!isSecure) messageId = DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageSent(masterSecret, PhoneNumberUtils.formatNumber(recipient.getNumber()), threadId, message, System.currentTimeMillis());
else messageId = DatabaseFactory.getEncryptingSmsDatabase(context).insertSecureMessageSent(masterSecret, PhoneNumberUtils.formatNumber(recipient.getNumber()), threadId, message, System.currentTimeMillis());
if (!isSecure) {
messageId = DatabaseFactory.getEncryptingSmsDatabase(context)
.insertMessageSent(masterSecret,
PhoneNumberUtils.formatNumber(recipient.getNumber()),
threadId, message, System.currentTimeMillis());
} else {
messageId = DatabaseFactory.getEncryptingSmsDatabase(context)
.insertSecureMessageSent(masterSecret,
PhoneNumberUtils.formatNumber(recipient.getNumber()),
threadId, message, System.currentTimeMillis());
}
Log.w("SMSSender", "Got message id for new message: " + messageId);
Intent intent = new Intent(SendReceiveService.SEND_SMS_ACTION, null, context, SendReceiveService.class);
Intent intent = new Intent(SendReceiveService.SEND_SMS_ACTION, null,
context, SendReceiveService.class);
intent.putExtra("message_id", messageId);
context.startService(intent);
}
@ -92,4 +104,22 @@ public class MessageSender {
return threadId;
}
private static void sendMms(Context context, Recipients recipients, MasterSecret masterSecret,
SendReq sendRequest, long threadId, boolean secure)
throws MmsException
{
String[] recipientsArray = recipients.toNumberStringArray();
EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(recipientsArray);
sendRequest.setTo(encodedNumbers);
long messageId = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret)
.insertMessageSent(sendRequest, threadId, secure);
Intent intent = new Intent(SendReceiveService.SEND_MMS_ACTION, null,
context, SendReceiveService.class);
intent.putExtra("message_id", messageId);
context.startService(intent);
}
}