Move session construction and KeyExchangeMessage into libaxolotl.

1) Add plain two-way key exchange support libaxolotl by moving
   all the KeyExchangeMessage code there.

2) Move the bulk of KeyExchangeProcessor code to libaxolotl
   for setting up sessions based on retrieved prekeys, received
   prekeybundles, or exchanged key exchange messages.
This commit is contained in:
Moxie Marlinspike
2014-04-22 21:31:57 -07:00
parent a1db221caf
commit 72af8b11c2
25 changed files with 565 additions and 476 deletions

View File

@@ -32,7 +32,6 @@ import android.widget.TextView;
import org.thoughtcrime.securesms.crypto.DecryptingQueue;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.SendReceiveService;
@@ -45,11 +44,12 @@ import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
import org.whispersystems.textsecure.crypto.IdentityKeyParcelable;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
import org.whispersystems.libaxolotl.InvalidKeyIdException;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.InvalidNumberException;
@@ -178,7 +178,7 @@ public class ReceiveKeyActivity extends Activity {
} else if (getIntent().getBooleanExtra("is_identity_update", false)) {
this.identityUpdateMessage = new IdentityKey(Base64.decodeWithoutPadding(messageBody), 0);
} else {
this.keyExchangeMessage = new KeyExchangeMessage(messageBody);
this.keyExchangeMessage = new KeyExchangeMessage(Base64.decodeWithoutPadding(messageBody));
}
} catch (IOException e) {
throw new AssertionError(e);
@@ -228,7 +228,7 @@ public class ReceiveKeyActivity extends Activity {
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
.markAsProcessedKeyExchange(messageId);
} catch (InvalidMessageException e) {
} catch (InvalidKeyException e) {
Log.w("ReceiveKeyActivity", e);
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
.markAsCorruptKeyExchange(messageId);

View File

@@ -22,7 +22,6 @@ import android.content.Intent;
import android.database.Cursor;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
@@ -41,10 +40,12 @@ import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.sms.SmsTransportDetails;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libaxolotl.DuplicateMessageException;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.SessionCipher;
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
import org.whispersystems.libaxolotl.protocol.WhisperMessage;
import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.textsecure.crypto.MasterSecret;
@@ -52,6 +53,7 @@ import org.whispersystems.textsecure.crypto.SessionCipherFactory;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.storage.TextSecureSessionStore;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.Hex;
import org.whispersystems.textsecure.util.Util;
@@ -428,7 +430,7 @@ public class DecryptingQueue {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, originator, false)
.getPrimaryRecipient();
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), deviceId);
KeyExchangeMessage message = new KeyExchangeMessage(plaintextBody);
KeyExchangeMessage message = new KeyExchangeMessage(Base64.decodeWithoutPadding(plaintextBody));
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipientDevice);
if (processor.isStale(message)) {
@@ -440,7 +442,7 @@ public class DecryptingQueue {
} catch (InvalidVersionException e) {
Log.w("DecryptingQueue", e);
DatabaseFactory.getEncryptingSmsDatabase(context).markAsInvalidVersionKeyExchange(messageId);
} catch (InvalidMessageException | RecipientFormattingException e) {
} catch (InvalidMessageException | IOException | InvalidKeyException | RecipientFormattingException e) {
Log.w("DecryptingQueue", e);
DatabaseFactory.getEncryptingSmsDatabase(context).markAsCorruptKeyExchange(messageId);
} catch (LegacyMessageException e) {

View File

@@ -22,7 +22,6 @@ import android.content.Context;
import android.content.DialogInterface;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
@@ -30,11 +29,13 @@ import org.thoughtcrime.securesms.util.Dialogs;
import org.whispersystems.libaxolotl.IdentityKeyPair;
import org.whispersystems.libaxolotl.ecc.Curve;
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
import org.whispersystems.libaxolotl.state.SessionRecord;
import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.storage.TextSecureSessionStore;
import org.whispersystems.textsecure.util.Base64;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@@ -68,11 +69,11 @@ public class KeyExchangeInitiator {
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
KeyExchangeMessage message = new KeyExchangeMessage(sequence, flags,
baseKey.getPublicKey(),
ephemeralKey.getPublicKey(),
identityKey.getPublicKey());
baseKey.getPublicKey(),
ephemeralKey.getPublicKey(),
identityKey.getPublicKey());
OutgoingKeyExchangeMessage textMessage = new OutgoingKeyExchangeMessage(recipient, message.serialize());
OutgoingKeyExchangeMessage textMessage = new OutgoingKeyExchangeMessage(recipient, Base64.encodeBytesWithoutPadding(message.serialize()));
SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret);
SessionRecord sessionRecord = sessionStore.get(recipient.getRecipientId(), RecipientDevice.DEFAULT_DEVICE_ID);

View File

@@ -2,9 +2,7 @@ package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
@@ -12,25 +10,22 @@ import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.PreKeyService;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.IdentityKeyPair;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.ecc.Curve;
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
import org.whispersystems.libaxolotl.InvalidKeyIdException;
import org.whispersystems.libaxolotl.SessionBuilder;
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
import org.whispersystems.libaxolotl.ratchet.RatchetingSession;
import org.whispersystems.libaxolotl.state.IdentityKeyStore;
import org.whispersystems.libaxolotl.state.PreKeyStore;
import org.whispersystems.libaxolotl.state.SessionRecord;
import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.push.PreKeyEntity;
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
import org.whispersystems.textsecure.storage.PreKeyRecord;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.storage.TextSecurePreKeyStore;
import org.whispersystems.textsecure.storage.TextSecureSessionStore;
import org.whispersystems.textsecure.util.Medium;
import org.whispersystems.textsecure.util.Base64;
/**
* This class processes key exchange interactions.
@@ -45,6 +40,7 @@ public class KeyExchangeProcessor {
private Context context;
private RecipientDevice recipientDevice;
private MasterSecret masterSecret;
private SessionBuilder sessionBuilder;
private SessionStore sessionStore;
public KeyExchangeProcessor(Context context, MasterSecret masterSecret, RecipientDevice recipientDevice)
@@ -52,7 +48,14 @@ public class KeyExchangeProcessor {
this.context = context;
this.recipientDevice = recipientDevice;
this.masterSecret = masterSecret;
this.sessionStore = new TextSecureSessionStore(context, masterSecret);
IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context, masterSecret);
PreKeyStore preKeyStore = new TextSecurePreKeyStore(context, masterSecret);
this.sessionStore = new TextSecureSessionStore(context, masterSecret);
this.sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, identityKeyStore,
recipientDevice.getRecipientId(),
recipientDevice.getDeviceId());
}
public boolean isTrusted(PreKeyWhisperMessage message) {
@@ -87,83 +90,14 @@ public class KeyExchangeProcessor {
public void processKeyExchangeMessage(PreKeyWhisperMessage message)
throws InvalidKeyIdException, InvalidKeyException
{
int preKeyId = message.getPreKeyId();
ECPublicKey theirBaseKey = message.getBaseKey();
ECPublicKey theirEphemeralKey = message.getWhisperMessage().getSenderEphemeral();
IdentityKey theirIdentityKey = message.getIdentityKey();
Log.w("KeyExchangeProcessor", "Received pre-key with local key ID: " + preKeyId);
if (!PreKeyRecord.hasRecord(context, preKeyId) &&
sessionStore.contains(recipientDevice.getRecipientId(), recipientDevice.getDeviceId()))
{
Log.w("KeyExchangeProcessor", "We've already processed the prekey part, letting bundled message fall through...");
return;
}
if (!PreKeyRecord.hasRecord(context, preKeyId))
throw new InvalidKeyIdException("No such prekey: " + preKeyId);
SessionRecord sessionRecord = sessionStore.get(recipientDevice.getRecipientId(),
recipientDevice.getDeviceId());
PreKeyRecord preKeyRecord = new PreKeyRecord(context, masterSecret, preKeyId);
ECKeyPair ourBaseKey = preKeyRecord.getKeyPair();
ECKeyPair ourEphemeralKey = ourBaseKey;
IdentityKeyPair ourIdentityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
boolean simultaneousInitiate = sessionRecord.getSessionState().hasPendingPreKey();
if (!simultaneousInitiate) sessionRecord.reset();
else sessionRecord.archiveCurrentState();
RatchetingSession.initializeSession(sessionRecord.getSessionState(),
ourBaseKey, theirBaseKey,
ourEphemeralKey, theirEphemeralKey,
ourIdentityKey, theirIdentityKey);
sessionRecord.getSessionState().setLocalRegistrationId(TextSecurePreferences.getLocalRegistrationId(context));
sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId());
if (simultaneousInitiate) sessionRecord.getSessionState().setNeedsRefresh(true);
sessionStore.put(recipientDevice.getRecipientId(), recipientDevice.getDeviceId(), sessionRecord);
if (preKeyId != Medium.MAX_VALUE) {
PreKeyRecord.delete(context, preKeyId);
}
sessionBuilder.process(message);
PreKeyService.initiateRefresh(context, masterSecret);
DatabaseFactory.getIdentityDatabase(context)
.saveIdentity(masterSecret, recipientDevice.getRecipientId(), theirIdentityKey);
}
public void processKeyExchangeMessage(PreKeyEntity message, long threadId)
throws InvalidKeyException
{
SessionRecord sessionRecord = sessionStore.get(recipientDevice.getRecipientId(),
recipientDevice.getDeviceId());
ECKeyPair ourBaseKey = Curve.generateKeyPair(true);
ECKeyPair ourEphemeralKey = Curve.generateKeyPair(true);
ECPublicKey theirBaseKey = message.getPublicKey();
ECPublicKey theirEphemeralKey = theirBaseKey;
IdentityKey theirIdentityKey = message.getIdentityKey();
IdentityKeyPair ourIdentityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
if (sessionRecord.getSessionState().getNeedsRefresh()) sessionRecord.archiveCurrentState();
else sessionRecord.reset();
RatchetingSession.initializeSession(sessionRecord.getSessionState(),
ourBaseKey, theirBaseKey, ourEphemeralKey,
theirEphemeralKey, ourIdentityKey, theirIdentityKey);
sessionRecord.getSessionState().setPendingPreKey(message.getKeyId(), ourBaseKey.getPublicKey());
sessionRecord.getSessionState().setLocalRegistrationId(TextSecurePreferences.getLocalRegistrationId(context));
sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId());
sessionStore.put(recipientDevice.getRecipientId(), recipientDevice.getDeviceId(), sessionRecord);
DatabaseFactory.getIdentityDatabase(context)
.saveIdentity(masterSecret, recipientDevice.getRecipientId(), message.getIdentityKey());
sessionBuilder.process(message);
if (threadId != -1) {
broadcastSecurityUpdateEvent(context, threadId);
@@ -171,84 +105,23 @@ public class KeyExchangeProcessor {
}
public void processKeyExchangeMessage(KeyExchangeMessage message, long threadId)
throws InvalidMessageException
throws InvalidKeyException
{
try {
SessionRecord sessionRecord = sessionStore.get(recipientDevice.getRecipientId(),
recipientDevice.getDeviceId());
Recipient recipient = RecipientFactory.getRecipientsForIds(context,
String.valueOf(recipientDevice.getRecipientId()),
false)
.getPrimaryRecipient();
KeyExchangeMessage responseMessage = sessionBuilder.process(message);
Recipient recipient = RecipientFactory.getRecipientsForIds(context,
String.valueOf(recipientDevice.getRecipientId()),
false)
.getPrimaryRecipient();
Log.w("KeyExchangeProcessor", "Received key exchange with sequence: " + message.getSequence());
if (message.isInitiate()) {
ECKeyPair ourBaseKey, ourEphemeralKey;
IdentityKeyPair ourIdentityKey;
int flags = KeyExchangeMessage.RESPONSE_FLAG;
Log.w("KeyExchangeProcessor", "KeyExchange is an initiate.");
if (!sessionRecord.getSessionState().hasPendingKeyExchange()) {
Log.w("KeyExchangeProcessor", "We don't have a pending initiate...");
ourBaseKey = Curve.generateKeyPair(true);
ourEphemeralKey = Curve.generateKeyPair(true);
ourIdentityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
sessionRecord.getSessionState().setPendingKeyExchange(message.getSequence(), ourBaseKey,
ourEphemeralKey, ourIdentityKey);
} else {
Log.w("KeyExchangeProcessor", "We alredy have a pending initiate, responding as simultaneous initiate...");
ourBaseKey = sessionRecord.getSessionState().getPendingKeyExchangeBaseKey();
ourEphemeralKey = sessionRecord.getSessionState().getPendingKeyExchangeEphemeralKey();
ourIdentityKey = sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey();
flags |= KeyExchangeMessage.SIMULTAENOUS_INITIATE_FLAG;
sessionRecord.getSessionState().setPendingKeyExchange(message.getSequence(), ourBaseKey,
ourEphemeralKey, ourIdentityKey);
}
KeyExchangeMessage ourMessage = new KeyExchangeMessage(message.getSequence(),
flags, ourBaseKey.getPublicKey(),
ourEphemeralKey.getPublicKey(),
ourIdentityKey.getPublicKey());
OutgoingKeyExchangeMessage textMessage = new OutgoingKeyExchangeMessage(recipient,
ourMessage.serialize());
MessageSender.send(context, masterSecret, textMessage, threadId, true);
}
if (message.getSequence() != sessionRecord.getSessionState().getPendingKeyExchangeSequence()) {
Log.w("KeyExchangeProcessor", "No matching sequence for response. " +
"Is simultaneous initiate response: " + message.isResponseForSimultaneousInitiate());
return;
}
ECKeyPair ourBaseKey = sessionRecord.getSessionState().getPendingKeyExchangeBaseKey();
ECKeyPair ourEphemeralKey = sessionRecord.getSessionState().getPendingKeyExchangeEphemeralKey();
IdentityKeyPair ourIdentityKey = sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey();
sessionRecord.reset();
RatchetingSession.initializeSession(sessionRecord.getSessionState(),
ourBaseKey, message.getBaseKey(),
ourEphemeralKey, message.getEphemeralKey(),
ourIdentityKey, message.getIdentityKey());
sessionRecord.getSessionState().setSessionVersion(message.getVersion());
sessionStore.put(recipientDevice.getRecipientId(), recipientDevice.getDeviceId(), sessionRecord);
DatabaseFactory.getIdentityDatabase(context)
.saveIdentity(masterSecret, recipientDevice.getRecipientId(), message.getIdentityKey());
DecryptingQueue.scheduleRogueMessages(context, masterSecret, recipient);
broadcastSecurityUpdateEvent(context, threadId);
} catch (InvalidKeyException e) {
throw new InvalidMessageException(e);
if (responseMessage != null) {
String serializedResponse = Base64.encodeBytesWithoutPadding(responseMessage.serialize());
OutgoingKeyExchangeMessage textMessage = new OutgoingKeyExchangeMessage(recipient, serializedResponse);
MessageSender.send(context, masterSecret, textMessage, threadId, true);
}
DecryptingQueue.scheduleRogueMessages(context, masterSecret, recipient);
broadcastSecurityUpdateEvent(context, threadId);
}
public static void broadcastSecurityUpdateEvent(Context context, long threadId) {

View File

@@ -0,0 +1,36 @@
package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.IdentityKeyPair;
import org.whispersystems.libaxolotl.state.IdentityKeyStore;
import org.whispersystems.textsecure.crypto.MasterSecret;
public class TextSecureIdentityKeyStore implements IdentityKeyStore {
private final Context context;
private final MasterSecret masterSecret;
public TextSecureIdentityKeyStore(Context context, MasterSecret masterSecret) {
this.context = context;
this.masterSecret = masterSecret;
}
@Override
public IdentityKeyPair getIdentityKeyPair() {
return IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
}
@Override
public int getLocalRegistrationId() {
return TextSecurePreferences.getLocalRegistrationId(context);
}
@Override
public void saveIdentity(long recipientId, IdentityKey identityKey) {
DatabaseFactory.getIdentityDatabase(context).saveIdentity(masterSecret, recipientId, identityKey);
}
}

View File

@@ -1,144 +0,0 @@
package org.thoughtcrime.securesms.crypto.protocol;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
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.ecc.Curve;
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
import org.whispersystems.libaxolotl.protocol.WhisperProtos;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.Conversions;
import org.whispersystems.textsecure.util.Util;
import java.io.IOException;
public class KeyExchangeMessage {
public static final int INITIATE_FLAG = 0x01;
public static final int RESPONSE_FLAG = 0X02;
public static final int SIMULTAENOUS_INITIATE_FLAG = 0x04;
private final int version;
private final int supportedVersion;
private final int sequence;
private final int flags;
private final ECPublicKey baseKey;
private final ECPublicKey ephemeralKey;
private final IdentityKey identityKey;
private final byte[] serialized;
public KeyExchangeMessage(int sequence, int flags,
ECPublicKey baseKey, ECPublicKey ephemeralKey,
IdentityKey identityKey)
{
this.supportedVersion = CiphertextMessage.CURRENT_VERSION;
this.version = CiphertextMessage.CURRENT_VERSION;
this.sequence = sequence;
this.flags = flags;
this.baseKey = baseKey;
this.ephemeralKey = ephemeralKey;
this.identityKey = identityKey;
byte[] version = {Conversions.intsToByteHighAndLow(this.version, this.supportedVersion)};
byte[] message = WhisperProtos.KeyExchangeMessage.newBuilder()
.setId((sequence << 5) | flags)
.setBaseKey(ByteString.copyFrom(baseKey.serialize()))
.setEphemeralKey(ByteString.copyFrom(ephemeralKey.serialize()))
.setIdentityKey(ByteString.copyFrom(identityKey.serialize()))
.build().toByteArray();
this.serialized = Util.combine(version, message);
}
public KeyExchangeMessage(String serializedAndEncoded)
throws InvalidMessageException, InvalidVersionException, LegacyMessageException
{
try {
byte[] serialized = Base64.decodeWithoutPadding(serializedAndEncoded);
byte[][] parts = Util.split(serialized, 1, serialized.length - 1);
this.version = Conversions.highBitsToInt(parts[0][0]);
this.supportedVersion = Conversions.lowBitsToInt(parts[0][0]);
if (this.version <= CiphertextMessage.UNSUPPORTED_VERSION) {
throw new LegacyMessageException("Unsupported legacy version: " + this.version);
}
if (this.version > CiphertextMessage.CURRENT_VERSION) {
throw new InvalidVersionException("Unknown version: " + this.version);
}
WhisperProtos.KeyExchangeMessage message = WhisperProtos.KeyExchangeMessage.parseFrom(parts[1]);
if (!message.hasId() || !message.hasBaseKey() ||
!message.hasEphemeralKey() || !message.hasIdentityKey())
{
throw new InvalidMessageException("Some required fields missing!");
}
this.sequence = message.getId() >> 5;
this.flags = message.getId() & 0x1f;
this.serialized = serialized;
this.baseKey = Curve.decodePoint(message.getBaseKey().toByteArray(), 0);
this.ephemeralKey = Curve.decodePoint(message.getEphemeralKey().toByteArray(), 0);
this.identityKey = new IdentityKey(message.getIdentityKey().toByteArray(), 0);
} catch (InvalidKeyException | IOException e) {
throw new InvalidMessageException(e);
}
}
public int getVersion() {
return version;
}
public ECPublicKey getBaseKey() {
return baseKey;
}
public ECPublicKey getEphemeralKey() {
return ephemeralKey;
}
public IdentityKey getIdentityKey() {
return identityKey;
}
public boolean hasIdentityKey() {
return true;
}
public int getMaxVersion() {
return supportedVersion;
}
public boolean isResponse() {
return ((flags & RESPONSE_FLAG) != 0);
}
public boolean isInitiate() {
return (flags & INITIATE_FLAG) != 0;
}
public boolean isResponseForSimultaneousInitiate() {
return (flags & SIMULTAENOUS_INITIATE_FLAG) != 0;
}
public int getFlags() {
return flags;
}
public int getSequence() {
return sequence;
}
public String serialize() {
return Base64.encodeBytesWithoutPadding(serialized);
}
}

View File

@@ -10,10 +10,10 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.state.PreKeyRecord;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.PreKeyUtil;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.storage.PreKeyRecord;
import java.io.IOException;
import java.util.List;

View File

@@ -32,7 +32,7 @@ import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
import org.whispersystems.libaxolotl.InvalidKeyIdException;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.storage.TextSecureSessionStore;
import org.whispersystems.textsecure.util.Base64;

View File

@@ -18,11 +18,11 @@ import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.state.PreKeyRecord;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.PreKeyUtil;
import org.whispersystems.textsecure.push.ExpectationFailedException;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.storage.PreKeyRecord;
import org.whispersystems.textsecure.util.Util;
import java.io.IOException;

View File

@@ -24,7 +24,6 @@ import android.util.Pair;
import org.thoughtcrime.securesms.crypto.DecryptingQueue;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.crypto.protocol.KeyExchangeMessage;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
@@ -44,11 +43,13 @@ import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
import org.whispersystems.libaxolotl.protocol.WhisperMessage;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
import org.whispersystems.libaxolotl.InvalidKeyIdException;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.util.Base64;
import java.io.IOException;
import java.util.List;
@@ -111,12 +112,12 @@ public class SmsReceiver {
Log.w("SmsReceiver", "Processing prekey message...");
try {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, message.getSender(), false).getPrimaryRecipient();
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), message.getSenderDeviceId());
Recipient recipient = RecipientFactory.getRecipientsFromString(context, message.getSender(), false).getPrimaryRecipient();
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), message.getSenderDeviceId());
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipientDevice);
SmsTransportDetails transportDetails = new SmsTransportDetails();
byte[] rawMessage = transportDetails.getDecodedMessage(message.getMessageBody().getBytes());
PreKeyWhisperMessage preKeyExchange = new PreKeyWhisperMessage(rawMessage);
SmsTransportDetails transportDetails = new SmsTransportDetails();
byte[] rawMessage = transportDetails.getDecodedMessage(message.getMessageBody().getBytes());
PreKeyWhisperMessage preKeyExchange = new PreKeyWhisperMessage(rawMessage);
if (processor.isTrusted(preKeyExchange)) {
processor.processKeyExchangeMessage(preKeyExchange);
@@ -163,7 +164,7 @@ public class SmsReceiver {
try {
Recipient recipient = RecipientFactory.getRecipientsFromString(context, message.getSender(), false).getPrimaryRecipient();
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), message.getSenderDeviceId());
KeyExchangeMessage exchangeMessage = new KeyExchangeMessage(message.getMessageBody());
KeyExchangeMessage exchangeMessage = new KeyExchangeMessage(Base64.decodeWithoutPadding(message.getMessageBody()));
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipientDevice);
if (processor.isStale(exchangeMessage)) {
@@ -179,7 +180,7 @@ public class SmsReceiver {
} catch (InvalidVersionException e) {
Log.w("SmsReceiver", e);
message.setInvalidVersion(true);
} catch (InvalidMessageException | RecipientFormattingException e) {
} catch (InvalidMessageException | InvalidKeyException | IOException | RecipientFormattingException e) {
Log.w("SmsReceiver", e);
message.setCorrupted(true);
} catch (LegacyMessageException e) {