mirror of
https://github.com/oxen-io/session-android.git
synced 2023-12-14 02:53:01 +01:00
Rollbacks, v2 sms-transport key exchanges, push identity conflicts.
1) Stop protocol rollbacks. 2) Handle v2 version key exchange messages. 3) Handle identity key conflicts on prekeybundle messages.
This commit is contained in:
parent
5e6d39beea
commit
073b1f69e3
16 changed files with 192 additions and 48 deletions
|
@ -89,7 +89,14 @@ public class SessionCipher {
|
|||
throws InvalidMessageException
|
||||
{
|
||||
try {
|
||||
KeyRecords records = getKeyRecords(context, masterSecret, recipient);
|
||||
KeyRecords records = getKeyRecords(context, masterSecret, recipient);
|
||||
|
||||
if (messageVersion < records.getSessionRecord().getNegotiatedSessionVersion()) {
|
||||
throw new InvalidMessageException("Message version: " + messageVersion +
|
||||
" but negotiated session version: " +
|
||||
records.getSessionRecord().getNegotiatedSessionVersion());
|
||||
}
|
||||
|
||||
SessionKey sessionKey = getSessionKey(masterSecret, Cipher.DECRYPT_MODE, messageVersion, localIdentityKey, records, recipientKeyId, senderKeyId);
|
||||
return new SessionCipherContext(records, sessionKey, senderKeyId,
|
||||
recipientKeyId, nextKey, counter,
|
||||
|
|
|
@ -44,7 +44,8 @@ public class SessionRecord extends Record {
|
|||
private int counter;
|
||||
private byte[] localFingerprint;
|
||||
private byte[] remoteFingerprint;
|
||||
private int sessionVersion;
|
||||
private int negotiatedSessionVersion;
|
||||
private int currentSessionVersion;
|
||||
|
||||
private IdentityKey identityKey;
|
||||
private SessionKey sessionKeyRecord;
|
||||
|
@ -59,8 +60,8 @@ public class SessionRecord extends Record {
|
|||
|
||||
public SessionRecord(Context context, MasterSecret masterSecret, long recipientId) {
|
||||
super(context, SESSIONS_DIRECTORY, recipientId+"");
|
||||
this.masterSecret = masterSecret;
|
||||
this.sessionVersion = 31337;
|
||||
this.masterSecret = masterSecret;
|
||||
this.currentSessionVersion = 31337;
|
||||
loadData();
|
||||
}
|
||||
|
||||
|
@ -91,11 +92,19 @@ public class SessionRecord extends Record {
|
|||
}
|
||||
|
||||
public int getSessionVersion() {
|
||||
return (sessionVersion == 31337 ? 0 : sessionVersion);
|
||||
return (currentSessionVersion == 31337 ? 0 : currentSessionVersion);
|
||||
}
|
||||
|
||||
public int getNegotiatedSessionVersion() {
|
||||
return negotiatedSessionVersion;
|
||||
}
|
||||
|
||||
public void setNegotiatedSessionVersion(int sessionVersion) {
|
||||
this.negotiatedSessionVersion = sessionVersion;
|
||||
}
|
||||
|
||||
public void setSessionVersion(int sessionVersion) {
|
||||
this.sessionVersion = sessionVersion;
|
||||
this.currentSessionVersion = sessionVersion;
|
||||
}
|
||||
|
||||
public int getCounter() {
|
||||
|
@ -169,10 +178,11 @@ public class SessionRecord extends Record {
|
|||
writeInteger(counter, out);
|
||||
writeBlob(localFingerprint, out);
|
||||
writeBlob(remoteFingerprint, out);
|
||||
writeInteger(sessionVersion, out);
|
||||
writeInteger(currentSessionVersion, out);
|
||||
writeIdentityKey(out);
|
||||
writeInteger(verifiedSessionKey ? 1 : 0, out);
|
||||
writeInteger(prekeyBundleRequired ? 1 : 0, out);
|
||||
writeInteger(negotiatedSessionVersion, out);
|
||||
|
||||
if (sessionKeyRecord != null)
|
||||
writeBlob(sessionKeyRecord.serialize(), out);
|
||||
|
@ -193,20 +203,20 @@ public class SessionRecord extends Record {
|
|||
|
||||
// Sigh, always put a version number on everything.
|
||||
if (!isValidVersionMarker(versionMarker)) {
|
||||
this.counter = versionMarker;
|
||||
this.localFingerprint = readBlob(in);
|
||||
this.remoteFingerprint = readBlob(in);
|
||||
this.sessionVersion = 31337;
|
||||
this.counter = versionMarker;
|
||||
this.localFingerprint = readBlob(in);
|
||||
this.remoteFingerprint = readBlob(in);
|
||||
this.currentSessionVersion = 31337;
|
||||
|
||||
if (in.available() != 0)
|
||||
this.sessionKeyRecord = new SessionKey(readBlob(in), masterSecret);
|
||||
|
||||
in.close();
|
||||
} else {
|
||||
this.counter = readInteger(in);
|
||||
this.localFingerprint = readBlob(in);
|
||||
this.remoteFingerprint = readBlob(in);
|
||||
this.sessionVersion = readInteger(in);
|
||||
this.counter = readInteger(in);
|
||||
this.localFingerprint = readBlob (in);
|
||||
this.remoteFingerprint = readBlob (in);
|
||||
this.currentSessionVersion = readInteger(in);
|
||||
|
||||
if (versionMarker >= 0X55555556) {
|
||||
readIdentityKey(in);
|
||||
|
@ -214,7 +224,10 @@ public class SessionRecord extends Record {
|
|||
}
|
||||
|
||||
if (versionMarker >= 0X55555557) {
|
||||
this.prekeyBundleRequired = (readInteger(in) == 1);
|
||||
this.prekeyBundleRequired = (readInteger(in) == 1);
|
||||
this.negotiatedSessionVersion = readInteger(in);
|
||||
} else {
|
||||
this.negotiatedSessionVersion = currentSessionVersion;
|
||||
}
|
||||
|
||||
if (in.available() != 0)
|
||||
|
|
|
@ -188,6 +188,8 @@
|
|||
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>
|
||||
|
||||
<!-- RegistrationActivity -->
|
||||
<string name="RegistrationActivity_connect_with_textsecure">Connect With TextSecure</string>
|
||||
|
@ -246,6 +248,18 @@
|
|||
<string name="RegistrationService_registration_error">Registration Error</string>
|
||||
<string name="RegistrationService_textsecure_registration_has_encountered_a_problem">TextSecure registration has encountered a problem.</string>
|
||||
|
||||
<!-- SmsMessageRecord -->
|
||||
<string name="SmsMessageRecord_received_corrupted_key_exchange_message">Received corrupted key
|
||||
exchange message!
|
||||
</string>
|
||||
<string name="SmsMessageRecord_received_key_exchange_message_for_invalid_protocol_version">
|
||||
Received key exchange message for invalid protocol version.
|
||||
</string>
|
||||
<string name="SmsMessageRecord_received_message_with_unknown_identity_key_click_to_process">
|
||||
Received message with unknown identity key. Click to process and display.
|
||||
</string>
|
||||
|
||||
|
||||
<!-- VerifyIdentityActivity -->
|
||||
<string name="VerifyIdentityActivity_you_do_not_have_an_identity_key">You do not have an identity key.</string>
|
||||
<string name="VerifyIdentityActivity_recipient_has_no_identity_key">Recipient has no identity key.</string>
|
||||
|
|
|
@ -336,6 +336,7 @@ public class ConversationItem extends LinearLayout {
|
|||
intent.putExtra("body", messageRecord.getBody().getBody());
|
||||
intent.putExtra("thread_id", messageRecord.getThreadId());
|
||||
intent.putExtra("message_id", messageRecord.getId());
|
||||
intent.putExtra("is_bundle", messageRecord.isBundleKeyExchange());
|
||||
intent.putExtra("master_secret", masterSecret);
|
||||
intent.putExtra("sent", messageRecord.isOutgoing());
|
||||
context.startActivity(intent);
|
||||
|
|
|
@ -30,6 +30,8 @@ import android.view.View;
|
|||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.DecryptingQueue;
|
||||
import org.thoughtcrime.securesms.sms.SmsTransportDetails;
|
||||
import org.whispersystems.textsecure.crypto.InvalidKeyException;
|
||||
import org.whispersystems.textsecure.crypto.InvalidVersionException;
|
||||
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
|
||||
|
@ -38,6 +40,10 @@ import org.whispersystems.textsecure.crypto.MasterSecret;
|
|||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.MemoryCleaner;
|
||||
import org.whispersystems.textsecure.crypto.protocol.PreKeyBundleMessage;
|
||||
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Activity for displaying sent/received session keys.
|
||||
|
@ -57,6 +63,7 @@ public class ReceiveKeyActivity extends Activity {
|
|||
private long messageId;
|
||||
|
||||
private MasterSecret masterSecret;
|
||||
private PreKeyBundleMessage keyExchangeMessageBundle;
|
||||
private KeyExchangeMessage keyExchangeMessage;
|
||||
private KeyExchangeProcessor keyExchangeProcessor;
|
||||
|
||||
|
@ -85,8 +92,8 @@ public class ReceiveKeyActivity extends Activity {
|
|||
}
|
||||
|
||||
private void initializeText() {
|
||||
if (keyExchangeProcessor.isTrusted(keyExchangeMessage)) initializeTrustedText();
|
||||
else initializeUntrustedText();
|
||||
if (isTrusted(keyExchangeMessage, keyExchangeMessageBundle)) initializeTrustedText();
|
||||
else initializeUntrustedText();
|
||||
}
|
||||
|
||||
private void initializeTrustedText() {
|
||||
|
@ -102,6 +109,9 @@ public class ReceiveKeyActivity extends Activity {
|
|||
Intent intent = new Intent(ReceiveKeyActivity.this, VerifyIdentityActivity.class);
|
||||
intent.putExtra("recipient", recipient);
|
||||
intent.putExtra("master_secret", masterSecret);
|
||||
intent.putExtra("remote_identity",
|
||||
keyExchangeMessage == null ?
|
||||
keyExchangeMessageBundle.getIdentityKey() : keyExchangeMessage.getIdentityKey());
|
||||
startActivity(intent);
|
||||
}
|
||||
}, getString(R.string.ReceiveKeyActivity_the_signature_on_this_key_exchange_is_different).length() +1,
|
||||
|
@ -111,9 +121,26 @@ public class ReceiveKeyActivity extends Activity {
|
|||
descriptionText.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
private boolean isTrusted(KeyExchangeMessage message, PreKeyBundleMessage messageBundle) {
|
||||
return (message != null && keyExchangeProcessor.isTrusted(message)) ||
|
||||
(messageBundle != null && keyExchangeProcessor.isTrusted(messageBundle));
|
||||
}
|
||||
|
||||
private void initializeKey() throws InvalidKeyException, InvalidVersionException {
|
||||
String messageBody = getIntent().getStringExtra("body");
|
||||
this.keyExchangeMessage = new KeyExchangeMessage(messageBody);
|
||||
try {
|
||||
String messageBody = getIntent().getStringExtra("body");
|
||||
|
||||
if (getIntent().getBooleanExtra("is_bundle", false)) {
|
||||
SmsTransportDetails transportDetails = new SmsTransportDetails();
|
||||
byte[] body = transportDetails.getDecodedMessage(messageBody.getBytes());
|
||||
|
||||
this.keyExchangeMessageBundle = new PreKeyBundleMessage(body);
|
||||
} else {
|
||||
this.keyExchangeMessage = new KeyExchangeMessage(messageBody);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
|
@ -123,7 +150,7 @@ public class ReceiveKeyActivity extends Activity {
|
|||
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.masterSecret = getIntent().getParcelableExtra("master_secret");
|
||||
this.keyExchangeProcessor = new KeyExchangeProcessor(this, masterSecret, recipient);
|
||||
}
|
||||
|
||||
|
@ -140,15 +167,39 @@ public class ReceiveKeyActivity extends Activity {
|
|||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
dialog = ProgressDialog.show(ReceiveKeyActivity.this, "Processing",
|
||||
"Processing key exchange...", true);
|
||||
dialog = ProgressDialog.show(ReceiveKeyActivity.this,
|
||||
getString(R.string.ReceiveKeyActivity_processing),
|
||||
getString(R.string.ReceiveKeyActivity_processing_key_exchange),
|
||||
true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
keyExchangeProcessor.processKeyExchangeMessage(keyExchangeMessage, threadId);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
|
||||
.markAsProcessedKeyExchange(messageId);
|
||||
if (keyExchangeMessage != null) {
|
||||
keyExchangeProcessor.processKeyExchangeMessage(keyExchangeMessage, threadId);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
|
||||
.markAsProcessedKeyExchange(messageId);
|
||||
} else if (keyExchangeMessageBundle != null) {
|
||||
try {
|
||||
keyExchangeProcessor.processKeyExchangeMessage(keyExchangeMessageBundle);
|
||||
byte[] bundledMessage = keyExchangeMessageBundle.getBundledMessage();
|
||||
SmsTransportDetails transportDetails = new SmsTransportDetails();
|
||||
String messageBody = new String(transportDetails.getEncodedMessage(bundledMessage));
|
||||
|
||||
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
|
||||
.updateBundleMessageBody(masterSecret, messageId, messageBody);
|
||||
|
||||
DecryptingQueue.scheduleDecryption(ReceiveKeyActivity.this, masterSecret, messageId,
|
||||
threadId, recipient.getNumber(), messageBody,
|
||||
true, false);
|
||||
} catch (InvalidKeyIdException e) {
|
||||
Log.w("ReceiveKeyActivity", e);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
|
||||
.markAsCorruptKeyExchange(messageId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,8 +66,12 @@ public class VerifyIdentityActivity extends KeyScanningActivity {
|
|||
}
|
||||
|
||||
private void initializeRemoteIdentityKey() {
|
||||
SessionRecord sessionRecord = new SessionRecord(this, masterSecret, recipient);
|
||||
IdentityKey identityKey = sessionRecord.getIdentityKey();
|
||||
IdentityKey identityKey = getIntent().getParcelableExtra("remote_identity");
|
||||
|
||||
if (identityKey == null) {
|
||||
SessionRecord sessionRecord = new SessionRecord(this, masterSecret, recipient);
|
||||
identityKey = sessionRecord.getIdentityKey();
|
||||
}
|
||||
|
||||
if (identityKey == null) {
|
||||
remoteIdentityFingerprint.setText(R.string.VerifyIdentityActivity_recipient_has_no_identity_key);
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.thoughtcrime.securesms.service.KeyCachingService;
|
|||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
|
||||
import org.whispersystems.textsecure.util.Conversions;
|
||||
import org.whispersystems.textsecure.util.Medium;
|
||||
|
||||
/**
|
||||
* This class processes key exchange interactions.
|
||||
|
@ -131,13 +132,15 @@ public class KeyExchangeProcessor {
|
|||
remoteKeyRecord.getCurrentRemoteKey().getFingerprintBytes());
|
||||
sessionRecord.setIdentityKey(remoteIdentity);
|
||||
sessionRecord.setSessionVersion(Math.min(message.getSupportedVersion(), MessageCipher.SUPPORTED_VERSION));
|
||||
|
||||
sessionRecord.setNegotiatedSessionVersion(sessionRecord.getSessionVersion());
|
||||
|
||||
localKeyRecord.save();
|
||||
remoteKeyRecord.save();
|
||||
sessionRecord.save();
|
||||
|
||||
PreKeyRecord.delete(context, preKeyId);
|
||||
if (preKeyId != Medium.MAX_VALUE) {
|
||||
PreKeyRecord.delete(context, preKeyId);
|
||||
}
|
||||
|
||||
DatabaseFactory.getIdentityDatabase(context)
|
||||
.saveIdentity(masterSecret, recipient, remoteIdentity);
|
||||
|
@ -156,6 +159,7 @@ public class KeyExchangeProcessor {
|
|||
sessionRecord.setSessionId(localKeyRecord.getCurrentKeyPair().getPublicKey().getFingerprintBytes(),
|
||||
remoteKeyRecord.getCurrentRemoteKey().getFingerprintBytes());
|
||||
sessionRecord.setIdentityKey(message.getIdentityKey());
|
||||
sessionRecord.setNegotiatedSessionVersion(MessageCipher.SUPPORTED_VERSION);
|
||||
sessionRecord.setSessionVersion(MessageCipher.SUPPORTED_VERSION);
|
||||
sessionRecord.setPrekeyBundleRequired(true);
|
||||
sessionRecord.save();
|
||||
|
@ -185,6 +189,7 @@ public class KeyExchangeProcessor {
|
|||
remoteKeyRecord.getCurrentRemoteKey().getFingerprintBytes());
|
||||
sessionRecord.setIdentityKey(message.getIdentityKey());
|
||||
sessionRecord.setSessionVersion(Math.min(MessageCipher.SUPPORTED_VERSION, message.getMaxVersion()));
|
||||
sessionRecord.setNegotiatedSessionVersion(sessionRecord.getSessionVersion());
|
||||
|
||||
Log.w("KeyExchangeUtil", "Setting session version: " + Math.min(MessageCipher.SUPPORTED_VERSION, message.getMaxVersion()));
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.whispersystems.textsecure.crypto.PublicKey;
|
|||
import org.whispersystems.textsecure.storage.LocalKeyRecord;
|
||||
import org.whispersystems.textsecure.util.Base64;
|
||||
import org.whispersystems.textsecure.util.Conversions;
|
||||
import org.whispersystems.textsecure.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -70,20 +71,26 @@ public class KeyExchangeMessage {
|
|||
this.supportedVersion = MessageCipher.SUPPORTED_VERSION;
|
||||
|
||||
publicKey.setId(publicKey.getId() | (highIdBits << 12));
|
||||
|
||||
|
||||
byte[] versionBytes = {Conversions.intsToByteHighAndLow(messageVersion, supportedVersion)};
|
||||
byte[] publicKeyBytes = publicKey.serialize();
|
||||
byte[] keyExchangeBytes = new byte[1 + publicKeyBytes.length];
|
||||
|
||||
keyExchangeBytes[0] = Conversions.intsToByteHighAndLow(messageVersion, supportedVersion);
|
||||
System.arraycopy(publicKeyBytes, 0, keyExchangeBytes, 1, publicKeyBytes.length);
|
||||
|
||||
if (includeIdentitySignature(messageVersion, context))
|
||||
keyExchangeBytes = IdentityKeyUtil.getSignedKeyExchange(context, masterSecret, keyExchangeBytes);
|
||||
byte[] serializedBytes;
|
||||
|
||||
if (messageVersion < 1)
|
||||
this.serialized = Base64.encodeBytes(keyExchangeBytes);
|
||||
else
|
||||
this.serialized = Base64.encodeBytesWithoutPadding(keyExchangeBytes);
|
||||
if (includeIdentityNoSignature(messageVersion, context)) {
|
||||
byte[] identityKey = IdentityKeyUtil.getIdentityKey(context).serialize();
|
||||
|
||||
serializedBytes = Util.combine(versionBytes, publicKeyBytes, identityKey);
|
||||
} else if (includeIdentitySignature(messageVersion, context)) {
|
||||
byte[] prolog = Util.combine(versionBytes, publicKeyBytes);
|
||||
|
||||
serializedBytes = IdentityKeyUtil.getSignedKeyExchange(context, masterSecret, prolog);
|
||||
} else {
|
||||
serializedBytes = Util.combine(versionBytes, publicKeyBytes);
|
||||
}
|
||||
|
||||
if (messageVersion < 1) this.serialized = Base64.encodeBytes(serializedBytes);
|
||||
else this.serialized = Base64.encodeBytesWithoutPadding(serializedBytes);
|
||||
}
|
||||
|
||||
public KeyExchangeMessage(String messageBody) throws InvalidVersionException, InvalidKeyException {
|
||||
|
@ -104,23 +111,33 @@ public class KeyExchangeMessage {
|
|||
|
||||
if (keyBytes.length <= PublicKey.KEY_SIZE + 1) {
|
||||
this.identityKey = null;
|
||||
} else {
|
||||
} else if (messageVersion == 1) {
|
||||
try {
|
||||
this.identityKey = IdentityKeyUtil.verifySignedKeyExchange(keyBytes);
|
||||
} catch (InvalidKeyException ike) {
|
||||
Log.w("KeyUtil", ike);
|
||||
this.identityKey = null;
|
||||
}
|
||||
}
|
||||
} else if (messageVersion == 2) {
|
||||
try {
|
||||
this.identityKey = new IdentityKey(keyBytes, 1 + PublicKey.KEY_SIZE);
|
||||
} catch (InvalidKeyException ike) {
|
||||
Log.w("KeyUtil", ike);
|
||||
this.identityKey = null;
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new InvalidKeyException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean includeIdentitySignature(int messageVersion, Context context) {
|
||||
return IdentityKeyUtil.hasIdentityKey(context) && (messageVersion >= 1);
|
||||
return IdentityKeyUtil.hasIdentityKey(context) && (messageVersion == 1);
|
||||
}
|
||||
|
||||
private static boolean includeIdentityNoSignature(int messageVersion, Context context) {
|
||||
return IdentityKeyUtil.hasIdentityKey(context) && (messageVersion >= 2);
|
||||
}
|
||||
|
||||
public PublicKey getPublicKey() {
|
||||
return publicKey;
|
||||
|
|
|
@ -628,7 +628,7 @@ public class DatabaseFactory {
|
|||
if (oldVersion < INTRODUCED_PUSH_DATABASE_VERSION) {
|
||||
db.execSQL("CREATE TABLE push (_id INTEGER PRIMARY KEY, type INTEGER, source TEXT, destinations TEXT, body TEXT, TIMESTAMP INTEGER);");
|
||||
db.execSQL("ALTER TABLE part ADD COLUMN pending_push INTEGER;");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS pending_push_index ON parts (pending_push);");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS pending_push_index ON part (pending_push);");
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
|
|
|
@ -96,6 +96,11 @@ public class EncryptingSmsDatabase extends SmsDatabase {
|
|||
return insertMessageInbox(message, type);
|
||||
}
|
||||
|
||||
public void updateBundleMessageBody(MasterSecret masterSecret, long messageId, String body) {
|
||||
updateMessageBodyAndType(messageId, body, Types.TOTAL_MASK,
|
||||
Types.BASE_INBOX_TYPE | Types.ENCRYPTION_REMOTE_BIT | Types.SECURE_MESSAGE_BIT);
|
||||
}
|
||||
|
||||
public void updateMessageBody(MasterSecret masterSecret, long messageId, String body) {
|
||||
String encryptedBody = getEncryptedBody(masterSecret, body);
|
||||
updateMessageBodyAndType(messageId, encryptedBody, Types.ENCRYPTION_MASK,
|
||||
|
|
|
@ -32,6 +32,7 @@ public interface MmsSmsColumns {
|
|||
protected static final long KEY_EXCHANGE_PROCESSED_BIT = 0x2000;
|
||||
protected static final long KEY_EXCHANGE_CORRUPTED_BIT = 0x1000;
|
||||
protected static final long KEY_EXCHANGE_INVALID_VERSION_BIT = 0x800;
|
||||
protected static final long KEY_EXCHANGE_BUNDLE_BIT = 0x400;
|
||||
|
||||
// Secure Message Information
|
||||
protected static final long SECURE_MESSAGE_BIT = 0x800000;
|
||||
|
@ -91,6 +92,10 @@ public interface MmsSmsColumns {
|
|||
return (type & KEY_EXCHANGE_INVALID_VERSION_BIT) != 0;
|
||||
}
|
||||
|
||||
public static boolean isBundleKeyExchange(long type) {
|
||||
return (type & KEY_EXCHANGE_BUNDLE_BIT) != 0;
|
||||
}
|
||||
|
||||
public static boolean isSymmetricEncryption(long type) {
|
||||
return (type & ENCRYPTION_SYMMETRIC_BIT) != 0;
|
||||
}
|
||||
|
|
|
@ -162,6 +162,10 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
|
|||
updateTypeBitmask(id, 0, Types.KEY_EXCHANGE_PROCESSED_BIT);
|
||||
}
|
||||
|
||||
public void markAsCorruptKeyExchange(long id) {
|
||||
updateTypeBitmask(id, 0, Types.KEY_EXCHANGE_CORRUPTED_BIT);
|
||||
}
|
||||
|
||||
public void markAsDecryptFailed(long id) {
|
||||
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT);
|
||||
}
|
||||
|
@ -239,6 +243,7 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
|
|||
else if (((IncomingKeyExchangeMessage)message).isProcessed()) type |= Types.KEY_EXCHANGE_PROCESSED_BIT;
|
||||
else if (((IncomingKeyExchangeMessage)message).isCorrupted()) type |= Types.KEY_EXCHANGE_CORRUPTED_BIT;
|
||||
else if (((IncomingKeyExchangeMessage)message).isInvalidVersion()) type |= Types.KEY_EXCHANGE_INVALID_VERSION_BIT;
|
||||
else if (((IncomingKeyExchangeMessage)message).isPreKeyBundle()) type |= Types.KEY_EXCHANGE_BUNDLE_BIT;
|
||||
} else if (message.isSecureMessage()) {
|
||||
type |= Types.SECURE_MESSAGE_BIT;
|
||||
type |= Types.ENCRYPTION_REMOTE_BIT;
|
||||
|
|
|
@ -103,6 +103,10 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
return SmsDatabase.Types.isProcessedKeyExchange(type);
|
||||
}
|
||||
|
||||
public boolean isBundleKeyExchange() {
|
||||
return SmsDatabase.Types.isBundleKeyExchange(type);
|
||||
}
|
||||
|
||||
public boolean isCorruptedKeyExchange() {
|
||||
return SmsDatabase.Types.isCorruptedKeyExchange(type);
|
||||
}
|
||||
|
|
|
@ -56,6 +56,12 @@ public class SmsMessageRecord extends MessageRecord {
|
|||
return emphasisAdded(context.getString(R.string.ConversationItem_received_and_processed_key_exchange_message));
|
||||
} else if (isStaleKeyExchange()) {
|
||||
return emphasisAdded(context.getString(R.string.ConversationItem_error_received_stale_key_exchange_message));
|
||||
} else if (isCorruptedKeyExchange()) {
|
||||
return emphasisAdded(context.getString(R.string.SmsMessageRecord_received_corrupted_key_exchange_message));
|
||||
} else if (isInvalidVersionKeyExchange()) {
|
||||
return emphasisAdded(context.getString(R.string.SmsMessageRecord_received_key_exchange_message_for_invalid_protocol_version));
|
||||
} else if (isBundleKeyExchange()) {
|
||||
return emphasisAdded(context.getString(R.string.SmsMessageRecord_received_message_with_unknown_identity_key_click_to_process));
|
||||
} else if (isKeyExchange() && isOutgoing()) {
|
||||
return emphasisAdded(context.getString(R.string.ConversationListAdapter_key_exchange_message));
|
||||
} else if (isKeyExchange() && !isOutgoing()) {
|
||||
|
|
|
@ -18,7 +18,10 @@ import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
|||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
|
||||
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.transport.SmsTransport;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.textsecure.crypto.InvalidKeyException;
|
||||
import org.whispersystems.textsecure.crypto.InvalidVersionException;
|
||||
|
@ -99,7 +102,12 @@ public class PushReceiver {
|
|||
IncomingPushMessage bundledMessage = message.withBody(preKeyExchange.getBundledMessage());
|
||||
handleReceivedSecureMessage(masterSecret, bundledMessage);
|
||||
} else {
|
||||
/// XXX
|
||||
SmsTransportDetails transportDetails = new SmsTransportDetails();
|
||||
String encoded = new String(transportDetails.getEncodedMessage(message.getBody()));
|
||||
IncomingTextMessage textMessage = new IncomingTextMessage(message, "");
|
||||
|
||||
textMessage = new IncomingPreKeyBundleMessage(textMessage, encoded);
|
||||
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, textMessage);
|
||||
}
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w("SmsReceiver", e);
|
||||
|
@ -118,6 +126,7 @@ public class PushReceiver {
|
|||
boolean secure)
|
||||
{
|
||||
try {
|
||||
Log.w("PushReceiver", "Processing: " + new String(message.getBody()));
|
||||
PushMessageContent messageContent = PushMessageContent.parseFrom(message.getBody());
|
||||
|
||||
if (messageContent.getAttachmentsCount() > 0 || message.getDestinations().size() > 0) {
|
||||
|
|
|
@ -120,8 +120,6 @@ public class SmsReceiver {
|
|||
context.sendBroadcast(intent, KeyCachingService.KEY_PERMISSION);
|
||||
|
||||
return messageAndThreadId;
|
||||
} else {
|
||||
/// XXX
|
||||
}
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w("SmsReceiver", e);
|
||||
|
|
Loading…
Reference in a new issue