mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 08:39:22 +01:00
Beginning of libtextsecure refactor.
1) Break out appropriate components. 2) Switch the incoming pipeline from SendReceiveService to the JobManager.
This commit is contained in:
@@ -30,48 +30,34 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.DecryptingQueue;
|
||||
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
|
||||
import org.thoughtcrime.securesms.crypto.TextSecureCipher;
|
||||
import org.thoughtcrime.securesms.crypto.TextSecureIdentityKeyStore;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.jobs.SmsDecryptJob;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.service.SendReceiveService;
|
||||
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingIdentityUpdateMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||
import org.thoughtcrime.securesms.sms.SmsTransportDetails;
|
||||
import org.thoughtcrime.securesms.util.MemoryCleaner;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libaxolotl.DuplicateMessageException;
|
||||
import org.whispersystems.libaxolotl.IdentityKey;
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.libaxolotl.InvalidMessageException;
|
||||
import org.whispersystems.libaxolotl.InvalidVersionException;
|
||||
import org.whispersystems.libaxolotl.LegacyMessageException;
|
||||
import org.whispersystems.libaxolotl.NoSessionException;
|
||||
import org.whispersystems.libaxolotl.StaleKeyExchangeException;
|
||||
import org.whispersystems.libaxolotl.UntrustedIdentityException;
|
||||
import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
|
||||
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
|
||||
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
|
||||
import org.whispersystems.libaxolotl.state.IdentityKeyStore;
|
||||
import org.whispersystems.textsecure.crypto.IdentityKeyParcelable;
|
||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||
import org.whispersystems.textsecure.crypto.TransportDetails;
|
||||
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
||||
import org.whispersystems.libaxolotl.InvalidKeyIdException;
|
||||
import org.whispersystems.textsecure.push.PushTransportDetails;
|
||||
import org.whispersystems.textsecure.storage.RecipientDevice;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureGroup;
|
||||
import org.whispersystems.textsecure.util.Base64;
|
||||
import org.whispersystems.textsecure.util.InvalidNumberException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.Type;
|
||||
|
||||
/**
|
||||
* Activity for displaying sent/received session keys.
|
||||
*
|
||||
@@ -87,13 +73,11 @@ public class ReceiveKeyActivity extends Activity {
|
||||
|
||||
private Recipient recipient;
|
||||
private int recipientDeviceId;
|
||||
private long threadId;
|
||||
private long messageId;
|
||||
|
||||
private MasterSecret masterSecret;
|
||||
private PreKeyWhisperMessage keyExchangeMessageBundle;
|
||||
private KeyExchangeMessage keyExchangeMessage;
|
||||
private IdentityKey identityUpdateMessage;
|
||||
private MasterSecret masterSecret;
|
||||
private IncomingKeyExchangeMessage message;
|
||||
private IdentityKey identityKey;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle state) {
|
||||
@@ -118,7 +102,7 @@ public class ReceiveKeyActivity extends Activity {
|
||||
}
|
||||
|
||||
private void initializeText() {
|
||||
if (isTrusted(keyExchangeMessage, keyExchangeMessageBundle, identityUpdateMessage)) {
|
||||
if (isTrusted(this.identityKey)) {
|
||||
initializeTrustedText();
|
||||
} else {
|
||||
initializeUntrustedText();
|
||||
@@ -135,16 +119,10 @@ public class ReceiveKeyActivity extends Activity {
|
||||
spannableString.setSpan(new ClickableSpan() {
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
IdentityKey remoteIdentity;
|
||||
|
||||
if (identityUpdateMessage != null) remoteIdentity = identityUpdateMessage;
|
||||
else if (keyExchangeMessageBundle != null) remoteIdentity = keyExchangeMessageBundle.getIdentityKey();
|
||||
else remoteIdentity = keyExchangeMessage.getIdentityKey();
|
||||
|
||||
Intent intent = new Intent(ReceiveKeyActivity.this, VerifyIdentityActivity.class);
|
||||
intent.putExtra("recipient", recipient);
|
||||
intent.putExtra("master_secret", masterSecret);
|
||||
intent.putExtra("remote_identity", new IdentityKeyParcelable(remoteIdentity));
|
||||
intent.putExtra("remote_identity", new IdentityKeyParcelable(identityKey));
|
||||
startActivity(intent);
|
||||
}
|
||||
}, getString(R.string.ReceiveKeyActivity_the_signature_on_this_key_exchange_is_different).length() +1,
|
||||
@@ -154,43 +132,32 @@ public class ReceiveKeyActivity extends Activity {
|
||||
descriptionText.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
private boolean isTrusted(KeyExchangeMessage message, PreKeyWhisperMessage messageBundle, IdentityKey identityUpdateMessage) {
|
||||
private boolean isTrusted(IdentityKey identityKey) {
|
||||
long recipientId = recipient.getRecipientId();
|
||||
IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(this, masterSecret);
|
||||
|
||||
if (message != null) return identityKeyStore.isTrustedIdentity(recipientId, message.getIdentityKey());
|
||||
else if (messageBundle != null) return identityKeyStore.isTrustedIdentity(recipientId, messageBundle.getIdentityKey());
|
||||
else if (identityUpdateMessage != null) return identityKeyStore.isTrustedIdentity(recipientId, identityUpdateMessage);
|
||||
|
||||
return false;
|
||||
return identityKeyStore.isTrustedIdentity(recipientId, identityKey);
|
||||
}
|
||||
|
||||
private void initializeKey()
|
||||
throws InvalidKeyException, InvalidVersionException,
|
||||
InvalidMessageException, LegacyMessageException
|
||||
InvalidMessageException, LegacyMessageException
|
||||
{
|
||||
try {
|
||||
String messageBody = getIntent().getStringExtra("body");
|
||||
IncomingTextMessage message = new IncomingTextMessage(recipient.getNumber(),
|
||||
recipientDeviceId,
|
||||
System.currentTimeMillis(),
|
||||
getIntent().getStringExtra("body"),
|
||||
Optional.<TextSecureGroup>absent());
|
||||
|
||||
if (getIntent().getBooleanExtra("is_bundle", false)) {
|
||||
boolean isPush = getIntent().getBooleanExtra("is_push", false);
|
||||
byte[] body;
|
||||
|
||||
if (isPush) {
|
||||
body = Base64.decode(messageBody.getBytes());
|
||||
} else {
|
||||
body = new SmsTransportDetails().getDecodedMessage(messageBody.getBytes());
|
||||
}
|
||||
|
||||
this.keyExchangeMessageBundle = new PreKeyWhisperMessage(body);
|
||||
} else if (getIntent().getBooleanExtra("is_identity_update", false)) {
|
||||
this.identityUpdateMessage = new IdentityKey(Base64.decodeWithoutPadding(messageBody), 0);
|
||||
} else {
|
||||
this.keyExchangeMessage = new KeyExchangeMessage(Base64.decodeWithoutPadding(messageBody));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
if (getIntent().getBooleanExtra("is_bundle", false)) {
|
||||
this.message = new IncomingPreKeyBundleMessage(message, message.getMessageBody());
|
||||
} else if (getIntent().getBooleanExtra("is_identity_update", false)) {
|
||||
this.message = new IncomingIdentityUpdateMessage(message, message.getMessageBody());
|
||||
} else {
|
||||
this.message = new IncomingKeyExchangeMessage(message, message.getMessageBody());
|
||||
}
|
||||
|
||||
this.identityKey = getIdentityKey(this.message);
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
@@ -199,7 +166,6 @@ public class ReceiveKeyActivity extends Activity {
|
||||
this.cancelButton = (Button) findViewById(R.id.cancel_button);
|
||||
this.recipient = getIntent().getParcelableExtra("recipient");
|
||||
this.recipientDeviceId = getIntent().getIntExtra("recipient_device_id", -1);
|
||||
this.threadId = getIntent().getLongExtra("thread_id", -1);
|
||||
this.messageId = getIntent().getLongExtra("message_id", -1);
|
||||
this.masterSecret = getIntent().getParcelableExtra("master_secret");
|
||||
}
|
||||
@@ -209,6 +175,26 @@ public class ReceiveKeyActivity extends Activity {
|
||||
this.cancelButton.setOnClickListener(new CancelListener());
|
||||
}
|
||||
|
||||
private 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()) {
|
||||
boolean isPush = getIntent().getBooleanExtra("is_push", false);
|
||||
|
||||
if (isPush) return new PreKeyWhisperMessage(Base64.decode(message.getMessageBody())).getIdentityKey();
|
||||
else return new PreKeyWhisperMessage(Base64.decodeWithoutPadding(message.getMessageBody())).getIdentityKey();
|
||||
} else {
|
||||
return new KeyExchangeMessage(Base64.decodeWithoutPadding(message.getMessageBody())).getIdentityKey();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private class OkListener implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -225,75 +211,20 @@ public class ReceiveKeyActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
if (keyExchangeMessage != null) {
|
||||
try {
|
||||
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(),
|
||||
recipientDeviceId);
|
||||
KeyExchangeProcessor processor = new KeyExchangeProcessor(ReceiveKeyActivity.this,
|
||||
masterSecret, recipientDevice);
|
||||
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(ReceiveKeyActivity.this);
|
||||
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this);
|
||||
Context context = ReceiveKeyActivity.this;
|
||||
|
||||
IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(ReceiveKeyActivity.this,
|
||||
masterSecret);
|
||||
identityKeyStore.saveIdentity(recipient.getRecipientId(), keyExchangeMessage.getIdentityKey());
|
||||
identityDatabase.saveIdentity(masterSecret, recipient.getRecipientId(), identityKey);
|
||||
|
||||
processor.processKeyExchangeMessage(keyExchangeMessage, threadId);
|
||||
|
||||
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
|
||||
.markAsProcessedKeyExchange(messageId);
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w("ReceiveKeyActivity", e);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
|
||||
.markAsCorruptKeyExchange(messageId);
|
||||
} catch (StaleKeyExchangeException e) {
|
||||
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
|
||||
.markAsStaleKeyExchange(messageId);
|
||||
} catch (UntrustedIdentityException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
} else if (keyExchangeMessageBundle != null) {
|
||||
try {
|
||||
Context context = ReceiveKeyActivity.this;
|
||||
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
|
||||
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), recipientDeviceId);
|
||||
|
||||
TransportDetails transportDetails = getIntent().getBooleanExtra("is_push", false) ?
|
||||
new PushTransportDetails(keyExchangeMessageBundle.getMessageVersion()) :
|
||||
new SmsTransportDetails();
|
||||
|
||||
TextSecureCipher cipher = new TextSecureCipher(ReceiveKeyActivity.this, masterSecret, recipientDevice, transportDetails);
|
||||
IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(ReceiveKeyActivity.this,
|
||||
masterSecret);
|
||||
|
||||
identityKeyStore.saveIdentity(recipient.getRecipientId(), keyExchangeMessageBundle.getIdentityKey());
|
||||
byte[] plaintext = cipher.decrypt(keyExchangeMessageBundle);
|
||||
|
||||
database.updateBundleMessageBody(masterSecret, messageId, "");
|
||||
database.updateMessageBody(masterSecret, messageId, new String(plaintext));
|
||||
|
||||
} catch (InvalidKeyIdException | InvalidKeyException | LegacyMessageException | NoSessionException e) {
|
||||
Log.w("ReceiveKeyActivity", e);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
|
||||
.markAsCorruptKeyExchange(messageId);
|
||||
} catch (InvalidMessageException e) {
|
||||
Log.w("ReceiveKeyActivity", e);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
|
||||
.markAsDecryptFailed(messageId);
|
||||
} catch (DuplicateMessageException e) {
|
||||
Log.w("ReceiveKeyActivity", e);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
|
||||
.markAsDecryptDuplicate(messageId);
|
||||
} catch (UntrustedIdentityException e) {
|
||||
Log.w("ReceiveKeyActivity", e);
|
||||
Toast.makeText(ReceiveKeyActivity.this, "Untrusted!", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
} else if (identityUpdateMessage != null) {
|
||||
DatabaseFactory.getIdentityDatabase(ReceiveKeyActivity.this)
|
||||
.saveIdentity(masterSecret, recipient.getRecipientId(), identityUpdateMessage);
|
||||
|
||||
DatabaseFactory.getSmsDatabase(ReceiveKeyActivity.this).markAsProcessedKeyExchange(messageId);
|
||||
if (message.isIdentityUpdate()) {
|
||||
smsDatabase.markAsProcessedKeyExchange(messageId);
|
||||
} else {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new SmsDecryptJob(context, messageId));
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user